No public description

GitOrigin-RevId: 34384c9d97f10d51b7f8fa157c48933d23e65edd
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..f891142
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,16 @@
+root = true
+
+[*]
+insert_final_newline = true
+
+[*.{h,hpp,c,tpl,def}]
+indent_style = tab
+tab_width = 4
+
+[*.py]
+indent_style = space
+indent_size = 4
+
+[*.yml]
+indent_style = space
+indent_size = 2
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..7638b1f
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,19 @@
+# Set the default behavior, in case people don't have core.autocrlf set.
+* text=auto
+
+# Declare files that will always have CRLF line endings on checkout.
+*.ac text eol=lf
+*.am text eol=lf
+*.m4 text eol=lf
+*.pc text eol=lf
+*.spec text eol=lf
+*.sh text eol=lf
+
+# Explicitly declare text files you want to always be normalized and converted
+# to native line endings on checkout.
+*.c text
+*.h text
+
+# exclude repository configurations from 'git archive'
+.git*      	export-ignore
+.travis.yml	export-ignore
diff --git a/.github/workflows/action.yml b/.github/workflows/action.yml
new file mode 100644
index 0000000..4c24215
--- /dev/null
+++ b/.github/workflows/action.yml
@@ -0,0 +1,238 @@
+name: C/C++ CI
+
+on: [push, pull_request]
+
+jobs:
+  build:
+    strategy:
+      fail-fast: false
+      matrix:
+        name: [
+          ubuntu-gcc-autotools,
+          ubuntu-clang-autotools,
+          ubuntu-gcc-ossfuzz,
+          macos-autotools,
+          ubuntu-gcc-cmake,
+          ubuntu-gcc-cmake-shared,
+          ubuntu-clang-cmake,
+          ubuntu-clang-cmake-shared,
+          macos-cmake,
+          macos-cmake-shared,
+          windows-vs2022-x64,
+          windows-vs2022-x64-shared,
+          windows-vs2022-Win32,
+          windows-vs2022-Win32-shared
+        ]
+        include:
+          - name: ubuntu-gcc-autotools
+            os: ubuntu-latest
+            cc: gcc
+            cxx: g++
+            autotools-options: --enable-werror
+            build-system: autotools
+
+          - name: ubuntu-clang-autotools
+            os: ubuntu-latest
+            cc: clang
+            cxx: clang++
+            autotools-options: --enable-werror
+            build-system: autotools
+
+          - name: ubuntu-gcc-ossfuzz
+            os: ubuntu-latest
+            cc: gcc
+            cxx: g++
+            autotools-options: --enable-werror
+            build-system: ossfuzz
+
+          - name: macos-autotools
+            os: macos-latest
+            cc: clang
+            cxx: clang++
+            autotools-options: --enable-werror
+            build-system: autotools
+
+          - name: ubuntu-gcc-cmake
+            os: ubuntu-latest
+            cc: gcc
+            cxx: g++
+            build-system: cmake
+            cmake-generator: 'Ninja'
+            cmake-options: >-
+              -DCMAKE_BUILD_TYPE=Release
+              -DCMAKE_C_FLAGS="-Wall -Wextra"
+              -DCMAKE_VERBOSE_MAKEFILE=ON
+
+          - name: ubuntu-gcc-cmake-shared
+            os: ubuntu-latest
+            cc: gcc
+            cxx: g++
+            build-system: cmake
+            cmake-generator: 'Ninja'
+            cmake-options: >-
+              -DCMAKE_BUILD_TYPE=Release
+              -DBUILD_SHARED_LIBS=ON
+              -DCMAKE_C_FLAGS="-Wall -Wextra"
+              -DCMAKE_VERBOSE_MAKEFILE=ON
+
+          - name: ubuntu-clang-cmake
+            os: ubuntu-latest
+            cc: clang
+            cxx: clang++
+            build-system: cmake
+            cmake-generator: 'Ninja'
+            cmake-options: >-
+              -DCMAKE_BUILD_TYPE=Release
+              -DCMAKE_C_FLAGS="-Wall -Wextra"
+              -DCMAKE_VERBOSE_MAKEFILE=ON
+
+          - name: ubuntu-clang-cmake-shared
+            os: ubuntu-latest
+            cc: clang
+            cxx: clang++
+            build-system: cmake
+            cmake-generator: 'Ninja'
+            cmake-options: >-
+              -DCMAKE_BUILD_TYPE=Release
+              -DBUILD_SHARED_LIBS=ON
+              -DCMAKE_C_FLAGS="-Wall -Wextra"
+              -DCMAKE_VERBOSE_MAKEFILE=ON
+
+          - name: macos-cmake
+            os: macos-latest
+            cc: clang
+            cxx: clang++
+            build-system: cmake
+            cmake-generator: 'Unix Makefiles'
+            cmake-options: >-
+              -DCMAKE_BUILD_TYPE=Release
+              -DCMAKE_C_FLAGS="-Wall -Wextra"
+              -DCMAKE_VERBOSE_MAKEFILE=ON
+
+          - name: macos-cmake-shared
+            os: macos-latest
+            cc: clang
+            cxx: clang++
+            build-system: cmake
+            cmake-generator: 'Unix Makefiles'
+            cmake-options: >-
+              -DCMAKE_BUILD_TYPE=Release
+              -DBUILD_SHARED_LIBS=ON
+              -DCMAKE_C_FLAGS="-Wall -Wextra"
+              -DCMAKE_VERBOSE_MAKEFILE=ON
+
+          - name: windows-vs2022-x64
+            os: windows-latest
+            triplet: 'x64-windows-static'
+            build-system: cmake
+            cmake-generator: 'Visual Studio 17 2022'
+            cmake-options: >-
+              -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded$<$<CONFIG:Debug>:Debug>
+              -DCMAKE_BUILD_TYPE=Release
+              -DVCPKG_TARGET_TRIPLET=x64-windows-static
+              -DCMAKE_TOOLCHAIN_FILE=c:/vcpkg/scripts/buildsystems/vcpkg.cmake
+
+          - name: windows-vs2022-x64-shared
+            os: windows-latest
+            triplet: 'x64-windows-static'
+            build-system: cmake
+            cmake-generator: 'Visual Studio 17 2022'
+            cmake-options: >-
+              -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded$<$<CONFIG:Debug>:Debug>
+              -DBUILD_SHARED_LIBS=ON
+              -DBUILD_REGTEST=OFF
+              -DBUILD_EXAMPLES=OFF
+              -DINSTALL_PKGCONFIG_MODULE=OFF
+              -DCMAKE_BUILD_TYPE=Release
+              -DVCPKG_TARGET_TRIPLET=x64-windows-static
+              -DCMAKE_TOOLCHAIN_FILE=c:/vcpkg/scripts/buildsystems/vcpkg.cmake
+
+          - name: windows-vs2022-Win32
+            os: windows-latest
+            triplet: 'x86-windows-static'
+            build-system: cmake
+            cmake-generator: 'Visual Studio 17 2022'
+            cmake-options: >-
+              -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded$<$<CONFIG:Debug>:Debug>
+              -DCMAKE_GENERATOR_PLATFORM=Win32
+              -DCMAKE_BUILD_TYPE=Release
+              -DVCPKG_TARGET_TRIPLET=x86-windows-static
+              -DCMAKE_TOOLCHAIN_FILE=c:/vcpkg/scripts/buildsystems/vcpkg.cmake
+
+          - name: windows-vs2022-Win32-shared
+            os: windows-latest
+            triplet: 'x86-windows-static'
+            build-system: cmake
+            cmake-generator: 'Visual Studio 17 2022'
+            cmake-options: >-
+              -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded$<$<CONFIG:Debug>:Debug>
+              -DCMAKE_GENERATOR_PLATFORM=Win32
+              -DBUILD_SHARED_LIBS=ON
+              -DBUILD_REGTEST=OFF
+              -DBUILD_EXAMPLES=OFF
+              -DINSTALL_PKGCONFIG_MODULE=OFF
+              -DCPACK_PACKAGE_NAME=libsndfile
+              -DCMAKE_BUILD_TYPE=Release
+              -DVCPKG_TARGET_TRIPLET=x86-windows-static
+              -DCMAKE_TOOLCHAIN_FILE=c:/vcpkg/scripts/buildsystems/vcpkg.cmake
+
+    runs-on: ${{ matrix.os }}
+    steps:
+      - uses: actions/checkout@v2
+
+      - name: Install MacOS dependencies
+        if: startsWith(matrix.os,'macos')
+        run: |
+          brew install automake autogen speex mpg123
+
+      - name: Install Linux dependencies
+        if: startsWith(matrix.os,'ubuntu')
+        run: sudo apt-get update -y && sudo apt-get install -y autogen ninja-build libogg-dev libvorbis-dev libflac-dev libopus-dev libasound2-dev libsqlite3-dev libspeex-dev libmp3lame-dev libmpg123-dev
+
+      - name: Setup NuGet Credentials
+        env:
+          VCPKG_BINARY_SOURCES: 'clear;nuget,GitHub,readwrite'
+        if: startsWith(matrix.os,'windows')
+        shell: 'bash'
+        run: >
+          `vcpkg fetch nuget | tail -n 1`
+          sources add
+          -source "https://nuget.pkg.github.com/libsndfile/index.json"
+          -storepasswordincleartext
+          -name "GitHub"
+          -username "evpobr"
+          -password "${{ secrets.GITHUB_TOKEN }}"
+
+      - name: Configure, build and test with Autotools
+        env:
+          CC: ${{ matrix.cc }}
+          CXX: ${{ matrix.cxx }}
+        if: startsWith(matrix.build-system,'autotools')
+        run: |
+          autoreconf -vif
+          if [[ "${CC}" == "clang" ]]; then
+            ./configure --enable-werror && make distcheck
+          else
+            Scripts/asan-configure.sh --enable-werror && make distcheck
+          fi
+
+      - name: Configure, build and test with CMake
+        env:
+          CC: ${{ matrix.cc }}
+          CXX: ${{ matrix.cxx }}
+          VCPKG_BINARY_SOURCES: 'clear;nuget,GitHub,readwrite'
+        if: startsWith(matrix.build-system,'cmake')
+        run: |
+          mkdir build
+          cd build
+          cmake .. -G "${{matrix.cmake-generator}}" ${{matrix.cmake-options}}
+          cmake --build . --config Release
+          ctest
+
+      - name: Configure, build and test with OSSFuzz
+        env:
+          CC: ${{ matrix.cc }}
+          CXX: ${{ matrix.cxx }}
+        if: startsWith(matrix.build-system,'ossfuzz')
+        run: |
+          ./ossfuzz/ci_oss.sh
diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml
new file mode 100644
index 0000000..58ef9c4
--- /dev/null
+++ b/.github/workflows/cifuzz.yml
@@ -0,0 +1,26 @@
+name: CIFuzz
+on: [pull_request]
+jobs:
+  Fuzzing:
+    runs-on: ubuntu-latest
+    steps:
+    - name: Build Fuzzers
+      id: build
+      uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
+      with:
+        oss-fuzz-project-name: 'libsndfile'
+        dry-run: false
+        language: c
+    - name: Run Fuzzers
+      uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
+      with:
+        oss-fuzz-project-name: 'libsndfile'
+        fuzz-seconds: 600
+        dry-run: false
+        language: c
+    - name: Upload Crash
+      uses: actions/upload-artifact@v1
+      if: failure() && steps.build.outcome == 'success'
+      with:
+        name: artifacts
+        path: ./out/artifacts
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..05f79a9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,146 @@
+*.user
+*.8svx
+*.a
+*.aif
+*.aifc
+*.aiff
+*.au
+*.bin
+*.caf
+*.dll
+*.exe
+*.flac
+*.la
+*.lib
+*.lo
+*.loT
+*.o
+*.oct
+*.oga
+*.ogg
+*.paf
+*.raw
+*.rifx
+*.sd2
+*.sds
+*.svx
+/*.so*
+*.w64
+*.wav
+*.wavex
+.DS_Store
+.deps
+.dirstamp
+.libs
+Hack
+Hack/sndfile-fuzzbomb
+INSTALL
+m4/libtool.m4
+m4/ltoptions.m4
+m4/ltsugar.m4
+m4/ltversion.m4
+m4/lt~obsolete.m4
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache
+build-test-tarball.mk
+config.log
+config.status
+configure
+configure~
+doc/AUTHORS
+doc/ChangeLog
+doc/NEWS
+doc/README
+doc/libsndfile.css
+echo-install-dirs
+examples/generate
+examples/list_formats
+examples/make_sine
+examples/sfprocess
+examples/sndfile-loopify
+examples/sndfile-to-text
+examples/sndfilehandle
+include/sndfile.h
+libsndfile-*.tar.xz
+libsndfile-testsuite-*
+libsndfile.spec
+libtool
+man/sndfile-deinterleave.1
+man/sndfile-metadata-set.1
+programs/sndfile-cmp
+programs/sndfile-concat
+programs/sndfile-convert
+programs/sndfile-data-trim
+programs/sndfile-deinterleave
+programs/sndfile-info
+programs/sndfile-interleave
+programs/sndfile-jackplay
+programs/sndfile-metadata-get
+programs/sndfile-metadata-set
+programs/sndfile-play
+programs/sndfile-salvage
+regtest/sndfile-regtest
+sndfile.pc
+src/Ext
+src/G72x/g72x_test
+Symbols.darwin
+Symbols.gnu-binutils
+Symbols.os2
+Symbols.static
+src/config.h
+src/config.h.in
+src/config.h.in~
+src/libsndfile.so*
+libsndfile*.def
+src/stamp-h1
+src/test_endswap.c
+src/test_main
+src/version-metadata.rc
+tests/*_test
+tests/benchmark
+tests/benchmark.c
+tests/fix_this
+tests/floating_point_test.c
+tests/header_test.c
+tests/libsndfile-1.dll
+tests/pcm_test.c
+tests/pedantic-header-test.sh
+tests/pipe_test.c
+tests/rdwr_test.c
+tests/scale_clip_test.c
+tests/sfversion
+tests/test_wrapper.sh
+tests/utils.c
+tests/utils.h
+tests/write_read_test.c
+*.log
+*.trs
+.vs/
+packages/
+*.vcxproj.user
+*.VC.db
+*.VC.opendb
+Debug/
+Release/
+bin/
+obj/
+octave-workspace
+
+CMakeCache.txt
+CMakeFiles
+CMakeScripts
+Testing
+Makefile
+cmake_install.cmake
+install_manifest.txt
+compile_commands.json
+CTestTestfile.cmake
+CMakeSettings.json
+
+/*[Bb]uild*/
+
+/.vscode/
+/.vs/
+/out/
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..9ef64da
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,235 @@
+Original author:
+
+The main author of libsndfile is Erik de Castro Lopo <erikd@mega-nerd.com>
+apart from code in the following directories:
+
+ - src/GSM610 : Written by Jutta Degener <jutta@cs.tu-berlin.de> and Carsten
+   Bormann <cabo@cs.tu-berlin.de>. They should not be contacted in relation to
+   libsndfile or the GSM 6.10 code that is part of libsndfile. Their original
+   code can be found at:
+
+       http://kbs.cs.tu-berlin.de/~jutta/toast.html
+
+ - src/G72x : Released by Sun Microsystems, Inc. to the public domain. Minor
+   modifications were required to integrate these files into libsndfile. The
+   changes are listed in src/G72x/ChangeLog.
+
+Current maintainers:
+
+After the release of version 1.0.30, @erikd transferred the project to
+[the libsndfile team](https://github.com/libsndfile) consisting of:
+
+* Erik de Castro Lopo aka @erikd
+* David Seifert aka @SoapGentoo
+* Arthur Taylor aka @arthurt
+* @evpobr
+
+Current releaser is David Seifert:
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2
+
+mQINBFppABgBEAC42ZiNvV7BTIgR6TQy0YnF54fx3mVRP1u8Mq00UZa7reAsNKh7
+1H60j0W4s6+4pVVIKGfpVGxLwUdJe+KVCYw1Cd3YW6uMf5zZrC/ZWqnJiH/n6S6o
+1l4INII2o6YbGBnzIWBPRo7PlOL+mvgKTLpBSJPnhD8XDGN5wRiV8rL2+6Dptg0F
+nJt7oxECGF3OD3gk6HMel0o82CVkIqMtNaX1L/bhcdF7K0Rp2MXPZMmpn1izW5sI
+asN1G9+w+Zwj7kMJzq1Aw3ac+rsX4SEYdvXjS2QhDHQUIr6LXri3D2WbcEqIZj2R
+JVoVwblsrG11dYXFDBbgrq4NhgTBsxHYDlkr/qF2W+kbPC/nhSqTVZeCYvTBZbOQ
++RqyN/I0izukglnWmV1jGijFA8snyP8efx732hw/24zRYmtXOtnEITUpw8WOeZCq
+6uiHaQ+eopnY2ojBg9BI7WZm0AFn58xxT9soMsyFOUFgXTqaWFZWlJ3fhZE8/0v8
+JEu/kPGE5aJReT3b34B+Bojkj74XR+h2u7iJJBHMTE8RwGoUOZHer/XsL9xlcdks
+I+7TCjiq++ShaSSt2XsJmw2BhREohrjW/2KkwmvT3b44RMpKPB4WTH+++aqJQNeM
+IqmswOMoZvzEZezInj7WVY/r0WEei1Y6wt1tBrJ/cFf1oQBM1UmphxcrfQARAQAB
+tB9EYXZpZCBTZWlmZXJ0IDxzb2FwQGdlbnRvby5vcmc+iQJUBBMBCgA+BQsJCAcD
+BRUKCQgLBRYCAwEAAh4BAheAAhsBFiEEMdlcq22A0mIkShdQpHYg6AHkfpUFAl/V
+CvoFCQkuceIACgkQpHYg6AHkfpXYxA//aiJW1NwunpmzEc62id8lRMnoLHWVjISZ
+b+xSlm+hk4LYq+ZbthJDzKcT86/3DJOSE1zQw9wLuCao9IW2UfFJQBtR+TAfbagG
+0Yyk/kMcLoFJxnG1ywdJWypCAauuIhia52Z7PmmjsBbFwr6LygDwSQmZAyACMAs7
+TLQe+yERc2RNDsIEsquLSxxRF0Spk9gagWtKgrPc2XBjuNtQDwW7JgsOUoEeHyxC
+29fRUjC3o/pG2I6iAZp17OROZI5yl4TSORrSBDGIi2sayxyxP0x+IPKtrCUcBGNx
+wGp+56bP/V0hA6sgCPh/iwvqLoeibso6l/Kd4ltVAEQnHTd6fr8g+wLEUXfbJVTR
+7aeFUoaFmWjSPlQrNr6HlxSLV/kRx9kVJp1Pn16vkfVBF7fG7iDLiqphwEeQg5ND
+nmGeKAbRRNxFHyBHf0XRsaYiFZQckguO+71XSRtVx8/YP5nyNbtl9y1h/4JlT6Gy
+t7hb5twYFQyQrKss83E/Bo1sRdHpj0ibtqb4ZbYANbh482E6yFhAkuo8YjVTJipI
+1Ve8EBKnX3R+pDt147uyysNvtPVXML+sWpGSMVSm4NA8uT3F5nqxVwj+SeXy3Wq/
+CHQ2VBKGBC655G+wFD5C6O7cTx2MwH+2H8tzhWm+gFlI3MFKEXa/PC+YUC/diYcb
+BrApavriTRa5Ag0EWmkAZgEQAPXMD3mZI+ChvBysXZWksC88/uSEwFeb3XkcRm7v
+04GN7hcz+bfrmnUTB3tuE/ZQgv+u7ZjetvH1aEKieznn/GjnWoOBoJusOYvfAQeF
+0mQVi118QiOZRCnEZpkz+RY9TiXVgrZJg+AGqHZ3Ol4GkInEV2NWgH37Xal+HkFl
+rwI2U7mL0kZRG+LAVCQHKzqU0R0HE1XyJ4qf0awtG5Qi/TZvgXBdZPDXgr8i9Vlf
+UUu10c2XnXM0Av/YAlZmBFjVYrSOUCFenqSVqL+s9sTCVdWlJrGjrr3Ja4uT3kl2
+rLva0AR4oSQoxt8adKohmFz0vzOkQtCoRzhrCwoo3JvNjKdSNoOP1nSsxlO5ji8r
+ih5d+ajPgi580XyHLnrvG7vobR48qqscv1hizKuCgTacOTe6Db2Gqc8xF6v8HhJa
+KwWJtmFllIfN/tIvZ6BbbgHQn0IGf4CYnWf0SksPZqpBmTRpD2jfBxcj2UEg+AR3
+LARjuyUVpFJScyu6ExQG+6O+ByLL31iWP5MgUrza1rIpriPa3NT3rZ3DG2pvQrS3
+ySsrPzH7VRX8L1ThSMSzjwF96aMsd14s7XzR4EzNuWwZDukfs0yavZk6l4o1M0mb
+tbJi7hE4cz13KRHYvIkKMdZGYUnzRzZUDlsj2imakk3BR6GXnxZ1ST6062g+QxiL
+AJFLABEBAAGJBHIEGAEKACYCGwIWIQQx2VyrbYDSYiRKF1CkdiDoAeR+lQUCX9UL
+DQUJCS5xpwJAwXQgBBkBCgAdFiEEuNUxXaAAcsCoYIifzjbhFyAuOEIFAlppAGYA
+CgkQzjbhFyAuOELmrQ/9H9wrWsWa21STZdxUmyU2sh9VXAWEHl1Ey0fVTznDM0Fl
+zx5YSR/TmmnE36rpaz31Ttkx8SP914oV+mMgseecdya9Bf6uZL9Cv7V3KEsJBRL/
+ncrOWQBHP/Xy1X+mLD6A19xq7H4RihSLj0LeK2YVjrJzJ7wMf4mKXuBayQeAHImU
+WRCRTbmK3umh2nB5V0iPd/XZEIiYtiTPe+7E/va6+0bBvOumF3a+Z0iui7eU4hFC
+7Jk71D0dcg09SlIaNoMOrw7cMC3j2pMdKtsj8+0I6WBv14PhhqPAsnjdf7I/4NfK
+L7Jav8T/gDS01uA2Jxm72d+wr+eSjOBXa6x8CEbTqfkjAGxsWENThCp6zDkaXSDd
+JsV0va47vjzG8+wTDAvPy5IxIM/KZZdl4uWM+mF5K+q+eSTOHe7aLF2OdcussoBA
+A18zm994dAkG1COX/qpxanxx2bv/2IvCGPg+x6JtAN8ji2kncWu3dWGQdE5XbVjc
+fDwgsUPpp04G27Mr/x+HpEbgZ5SdA0dAqJktlNvCcHALhlblCWrsh/1QNjT/2iG8
+wsjcpEy/s4tWAuV4PTa4xvZ1JPS7Z7Eo5aBy9ZGOWG9SrHEiHnhkUsiswbHBOEjd
+pBSkmNElDcv9fRUahVCTPfvWBATFDrQyMjJBSm+cV8c/iFQM7isVSu8W7E0eetsJ
+EKR2IOgB5H6Vv9sP/1dxTvH0N0UoEoxIG/hnirEkbRpljdvqy4/uikYBKyQgSbo8
+VITTjea7gIhDztil9WZYt35jbOmoaGM2Z6TP2LEDOWgljYUNq9pl9Sc2GS8cNtEO
+WxExzGOc1Flo730dX3A85Ks3+0WPXZjLDcRRcPVkFd5WLQQDV1YVYopWkuQBC+Br
+4q3uv+sk+bw6gDa9+zFBbDuegdsYuTXrFHoxHz2GRv9Yb7ULCMgpFeNKDgtQq91u
+RqewoTwQp9tlp91LH/hh7R0Q4DRgeFDkLnVRXwSKjVvCrT5cBgImGwtFTGS4egoy
+MDKd/KKjZllp1ahRCln1XfmFQyQVMVvuF/JTtt31n6KwXwK2yxIlXB01xvRH+Ees
+AWeRYWKWXydaAY/9Ve0/PLFlgsr/XUGvt0GoEKe7odD3nZgg6015+/8JTroKw19L
+NZkhdfFMl11Zi0j5k3UbyzjYVpFSd8K2o0VoOG1LFsPp8tlRxNoVzpId0CX1au/p
+y1H7Wy/39mzriRG3rw+mJAQbBjN09putCltXFXpOEWk08n/N3vufCVQUoSu/2Bqw
+2HYj8VtToQp+O5dG3XxvDHINtInP1yr2Wcw2plna0KoXLwv/lZgDm3LN+eCWpG6d
+N/xk25DTSqTHArUQIEkhcHYK6GnyxUcvoKtG88hXtqEPYXiK08FZYAUPTnDYuQIN
+BFppAIkBEADDjvQZUs1NoqJpxkD2QDBudU1DBCaeI1D6CancMtb5FebPUxgFlDMd
+CBGOun48dY5i87gDhT/qS3gP/Mv9rjKJmcG9JHfhpXdW73owxrcsQ96nxxVJNEVl
+UHJw00z8C9eGWqr0SzSoE33K/PkzSkgtsaotF6+3uCerWulweulmGa5dpVfV0mbS
+aVw8VmrhZ5NmCeodyy/lR85rPik5pb32NT6v7xBkgkfS0VYtPB2E5gW1pXX/jEOi
+Mfq9idOEP9lxrNXV9j49Lr0JQCwAcrYbQ2+VPe6eacJEjzJ/6HiUqhPrYdnvydmb
+hU+xmv2NjGp2UnDZDEhzQfwm6fMx+8Nx2uPzCnXQGoyRBwiC/KcdW0F1ZPKdSXqH
+NKoOF62pLvIMSmfI3ZVOrTohArfr1kFEYVDv9Nl7oY+qg2rZEc2srOF74a9Z46bR
+TDPsEQzE2UMCvu3+rofhSD7aRotlKeDCvbe2s0yE4Man457Xc3LXh8Gva8CzCOLE
+2eMhNTsHIZk68WgXp3/uvE4Xy42myrk1AV8XXDdlWgx0Kc/I6tE59O5NVPSfuGvH
+1a15KKx0F6euEnYDKKpQ5PDR6dSn61po0tfbt96m044G/xQFjrfhHei4jji9Ogd9
+vlXVAi2vn3+NCSHFP5l3igLByBHy9iLIdmz7yQuus/1nwRmxOHOf2QARAQABiQI8
+BBgBCgAmAhsMFiEEMdlcq22A0mIkShdQpHYg6AHkfpUFAl/VCxkFCQkucZAACgkQ
+pHYg6AHkfpVPSRAAmheYkYJmtDbkzPBBnj5mbCIQN1/G5PI9eixc/TXWFOXtcjU1
+mJlJpSidHJyLRrx7r0c+N+s8vnY/JuUBsNoMJMER+Mv/CFW4iFi59V534SyAb2S0
+7NINJnFNkXBY62CDz9KsMuv/MdSv2yLhPH2Tfrm/eDRQesj1PanE4U1cgjWyJRc/
+IOlaRHvTasWDLgwbQi8ykt+4xUWzL/YKHzB+KyyzBK7vPBXqySX8ka4BOw7SDwG5
+lX2gtmhk4AGBwVChLXKflqVx1WXj4DPOt0kmOKVnKFyvUijK58M0A2FMgFMXDTIS
+DRtoZPdx/rkODXxgS+W+27NcYAnxJiM0cQqizEnQh7PQ1KzgdChPejYXMKe9lwdn
+ssMUxrBpbuAuagEf+pebNjD2eaNR4p8kfaDdGn53q55ysDvoyxKvnVQGSk1FAR9Q
+s4N5a4f02U7dzlyEhEfIcuUlRCfnlpn4n725YIhHheDig5zKWoEZCkNIfiRcGzDl
+8Drj+tlZiUR+gDkIoWSBaCkKbIQlc8qCYy6Hm7oZBaol6xKlUnTMK2rjK8fR4i8r
+bVDWBAaWj3jcDHJ0Jg3fS/qBpeya/JXMp89TR8NK5Ys7PZpWbor+puXBYyXDAVx3
+rXQ7JBA5klHPxrgjso1S/LqwscKLENtrVjdjhryLBmPifrmofJRnrpiHIEa5Ag0E
+WmkAswEQAL0hKwsRybQzkNGpJP+ElLSwFHd7XQhr+qIwLllpumWtnIK/DHmv8SpW
+FqAYajmRTXipFcBHH25x2jIIliZidn0a9826l+sMzrFadMC6/W4pitP71TeqZzwn
+pAuHs14YL7Wiy0aJQnfbCpRzPq3kYyOXmhmY7lPWO0WdUpR6W8wUbleK5XOVDDRx
+aIC/M3hhDOxZOMzQ+pdn4BaOFQQ0ygsRkqOudbuc0R1giYRt1i6gMeT8gfzL9jlw
+HcJ+aVnxdUQQ4uC47oKo/+lg7qh7LsiW79pQC1Bcdm8lhRmqtxe6ub60ecjax3XU
+1ILIEfIFCv6M7LRUAwz0bqk35spgkJqrGGKkdeWEKAFHg2QWR2F0zy+HdlPLfKxO
+uhaccpwc9EJtf744GS0SXa2AXr32j56n7CFcEjFcIQPBC6OJn6eA3hOVUYGZ7SrT
+4fsmZiFAdGEkvLKFuNhju1Hj2EJQUY1pm4GSBco7BR8x+QqoYrt5clU3WxRMNfTR
+0Rtuzsh4xskXNVMMgvKOahAtxENv2M2Cx6zJPVL5dmaysP7d6QRVeOQA5PwkcZ5Q
+qK6JtDZj2jpaKQH4Za715kiIcdqMDSkwxa6avc0kARHvfFcBR4hwDm1GAlaKG7eH
+8TOGGQIk8x2F3s4l8mTJVLWTP/uJYnkYBdqANYo5t1NIQLvwLFV3ABEBAAGJAjwE
+GAEKACYCGyAWIQQx2VyrbYDSYiRKF1CkdiDoAeR+lQUCX9ULIwUJCS5xcAAKCRCk
+diDoAeR+leekD/sF7aHH0W35ckWrXZlfSp0qHPWrBUaLBI9OAUHenRhgs4SbK0D4
+wqEiu0C5iDQojpXAeALQ8g/1pUsZ1yuFqYbGYWrHkA0Pm+P3tAGB4LMZ41YfvROP
+uaiW/+IMJbWllgRtaDt8/NtCgs30WI9I+az5M29HcGfvEwEUykrBx3dE9T+1ui3O
+capdd+GMvdAAsX5PyVkjWgZ7GrZeH8mG7UysYfT4qthxEtQfZ/u8ceSduKA46ugh
+C2eafIDNvluqn7BU4oKxME61u6C8BN2yHLI6LV0Tr4z5H8joVbM4BSFMwLVGlsXf
+HhB8kLiErN6bXolxsjARlmYiD9S9H2AcYidr6RYXf2EVFSpBG59xn1WTDN+DsHQf
+7btNPEPl/OPxa3OQjG+xn8USddiP0N0B4xsyzMNCCKDgvXXcIhX55KG9eh3Tc98S
+fEyhxu8ybZBIGmTJysPKxijfvSgQF+RPNTsz9lvXqkoK7RTgeYMschpjJEznCLbt
+M6eTDb5z0G5uLXh6+dYxtDOlPogI5OHd+G51LwCjvrQ+AtIUCgafuemwA9mpFT2b
+svb/qcxSVUb44bVaNHn1JHebX2YbokGtBOm1x2PI5fT8n6YIIYz3jKYOZAYdUT7x
+6qURyNjOfG4aPJIATwuh4GSNuxUG40+yuT+XfQF24mu1esS1J3wzRloJ7w==
+=K3x+
+-----END PGP PUBLIC KEY BLOCK-----
+
+Previous releasers:
+
+Version 1.0.29-1.0.30 - @evpobr.
+
+The public GPG key:
+
+-----BEGIN PGP MESSAGE-----
+
+yMMwAnicrVJ7UFRVHF50SdiJ2AknHmYM19IhFrmPva91hnBKHMkcSyAyY7nn3nN3
+L7AP9qVoO5Lja1YUycgJ4iUFWYluTlE4Oi6PxdhpCkesIWiAQIbXqlSs1Ex1l7H/
+6r/OP2fOd77vm+/3za8nZqVCFVEfk/xkgPyrMiLgO+FUFIze8R5AgEUoR3QHkBK4
+fMFSAdod+hJJQHQIiqEYBzECYByBs4AnGBYDGIYDjqEYHMc4nKB5ntICkQSAxCke
+RTEaJ3AthWJaWccItIhyiAYRJbMB2qw2yeyQbVmA8aKAQxbHCZbXApxhIIqTIiug
+AiZiPA8IUhQZXhYaLfawQg4HODvcKFlkTH7ol+P9C/9/zu1cthMJlJG5NM+RFEUC
+nsIomiJIBhMIUmAwNky0Q5uZM0GZDV1WC7Ahbg0iYy6Jh+FaH/4ZJIfRCf6L7yi3
+hoG9EOgfSvVAMgtyd7LCBW12yWJGdJjM5B1SWItpWQwjtBjOahC4zyrZoF4KM0ia
+YlD5aBCrDbpkSxrwIkpwUO6YIHCUAjwOKEIUBFaAIsWKAJfrF7XyoJAGkIaUPD9g
+aSBwIkUIOIqEhykzWxAdKcfkDLKlXTKYOYfTBhF3l2+PUhGhUjwSuSK8UwpVtPqf
+TTv+gVoRyKt70PuZxj6oi7ZkZP481vcc2vJ6cV774dld3s3n3+3w+cXdW9YaVvc9
+ni8Nv6aOUqx5b/+Lexria1wn7c1DOxVnikYSV50eY0o7Lwatz3qqg6RHWVOVF3zm
+lai7u7ud7r7vh5Lrm+95j06OeWN+29SbkNVeW9X1Vs651NsF9K1jV7xzkRdz6N4f
+Vy+5B37qTr18Lclk9lx7o3cmP/+6Jbbl3qvj7w/PvTnVDyYiuxtSZlvtCZ2JT6yv
+21rw8tLXnDHhwv0/7kfnpgSNXxKLA/tyPRsunLxp8tw4e6zx6PGbwsxKsTopg9u1
+dkdzBxWf+VVb/tbUzZNtUzUpj0l1fiSUtu1UaHxwdOnj2JlI4fdfdyAh/5GqvbY5
+7YKy0hgabi89Ffen+qUNtZ+f90258OevKhf6S+p+qelSRh/64ZOihg87D857slVZ
+5wqttfExWb7LymH/oVjTpcWFt4u/uP3pC8YrwfY+ad2NpDuW5oqPZrKOFCoGzqgb
+t1jSF1Vph1c0qtZUds+PaAqpp/Mqhjo0yavKiid91emBiPaeb3s2uW8d9J6YXZiu
+T8p7tK+8yHp3ov4dcCk3lNY28s13ZePrW8ceVGc3zOT01/ibotLjdFZn9tmd67a3
+iCrDaJM7Y/9TY8XzHdPbTZ2wPG7bxFBg2LSx6WootqI1dF2fGutPzE0bzJzW/w3x
+usYz
+=XFTp
+-----END PGP MESSAGE-----
+
+Versions before 1.0.29 - Erik de Castro Lopo.
+
+The public GPG key:
+
+-----BEGIN PGP SIGNATURE-----
+
+iQIzBAABCAAdFiEEapGlzyLCTJmjXgE/z9z5H7JCrO0FAljgv0wACgkQz9z5H7JC
+rO2RhxAAnIuHquhkhaY8CVsIOGImMxEF8RKDyFoioV9RnckNzBb592EYxU9hS0MM
+cT6xPZU0bJa8CvmCxEABMlkk60JP4VmmOtWxD8EEyktbHGyao80mDR82yVKYv78L
+zTRzz/JoTncgwWz/QataMbkAfy3KpxkwDW87az0kzkqGuSm3Jg3medt+t/iBe0Wm
+jcxIexxEZCmIhty6VyaVEkGWax0zdJHe7aD9FK2C/R3DU5Byp4e9pcp/a8v74G1n
+dgD4S3YlqvJGuU9S6QD4PzsoWD1X8f6pHzxpRP/OkqHAe11sSRo6sSYYCjsYi9Ca
+IJSlVC1nMx9OUxxAA8ZqtnztOo7GZcJ5NAL5X3GNV6YfQ7Bqxvtr6QG26F06MB6i
+p08UC7NC0bxFsD3f07wY22QnbnM052rniTn5sqQZv+GVls6t1b3u1iHzEjAjlgvi
+eOjVVt/A8iaOzMbKC/3+SZ9/GYqbSAQOCIoWAXmhV9IaMQyQeLCIC1YD5NMGBXal
+HPfSHV0hSUDbKjAYXFigJRzFU125bXTgg+v13Kd0KklIEjoaZYZLAYs0BupXdjBf
+jSPv8pwUaXJkv9/bGH0Ot1fvwDqPDOlGFINQbGAWdxwsdhiYBeXMFeFNTEfiqEMz
+BhhMxKa+Mfd9fkrX4NNigGW5QgRBS+FHNLf6ZfiXSnBGjjxMcbI=
+=UUA0
+-----END PGP SIGNATURE-----
+
+Others:
+
+    2013-03-07 Michael Pruett
+    2013-02-11 Chris Roberts c.roberts@csrfm.com
+    2012-03-17 IOhannes m zmoelnig
+    2013-02-21 Jan Starry
+    2012-01-20 Bodo
+    2011-06-23 Tim van der Molen
+    2010-12-01 Tim Blechmann
+    2010-10-04 Matti Nykyri
+    2010-09-17 Brian Lewis
+    2010-02-22 Robin Gareus
+    2010-01-07 Christian Weisgerber and Jacob Meuserto
+    2009-10-18 Olivier Tristan
+    2009-10-14, 2009-08-30 Uli Franke
+    2009-06-24, 2008-11-19, 2004-09-22, 2004-06-17, 2003-05-03, 2003-05-02 Conrad Parker
+    2009-05-22 Lennart Poettering
+    2008-07-03, 2006-10-22, 2006-10-18 Fons Adriaensen
+    2008-04-19 David Yeo
+    2008-01-20 Yair K.
+    2007-12-03 Robs
+    2007-07-12 Ed Schouten
+    2007-06-07 Trent Apted
+    2007-04-14, 2003-12-12 André Pang
+    2007-04-14 Reuben Thomas
+    2006-11-09 Jonathan Woithe
+    2006-03-26 Diego 'Flameeyes' Pettenò
+    2006-03-17 Paul Davis
+    2006-03-09 Jesse Chappell
+    2006-01-09, 2005-12-28 John ffitch
+    2005-09-21 David A. van Leeuwen
+    2005-08-15 Mo DeJong
+    2005-04-30 David Viens
+    2004-12-29 Steve Baker
+    2004-09-05 Denis Cote
+    2004-06-28 Stanko Juzbasic
+    2004-02-14 Frank Neumann
+    2003-08-15 Axel Röbel
+    2003-08-06 Peter Miller
+    2003-07-21 Tero Pelander
+    2003-02-10 Antoine Mathys
+    2002-12-30 Marek Peteraj
diff --git a/BUILD b/BUILD
new file mode 100644
index 0000000..f8df0c2
--- /dev/null
+++ b/BUILD
@@ -0,0 +1,78 @@
+load("@bazel_lib//lib:write_source_files.bzl", "write_source_file")
+load("@package_metadata//licenses/rules:license.bzl", "license")
+load("@package_metadata//licenses/rules:license_kind.bzl", "license_kind")
+load("@package_metadata//rules:package_metadata.bzl", "package_metadata")
+load("@rules_foreign_cc//foreign_cc:defs.bzl", "cmake")
+load("@stardust_build_package_metadata_mirror_url//:mirror_url.bzl", "mirror_url")
+load(":license.bzl", "concat_package_licenses")
+
+package(
+    default_package_metadata = [":package_metadata"],
+)
+
+package_metadata(
+    name = "package_metadata",
+    attributes = [
+        ":license",
+        ":mirror_url",
+    ],
+    purl = "pkg:github/libsndfile/libsndfile@1.2.2",
+)
+
+license_kind(
+    name = "license_kind",
+    full_name = "libsndfile Composite License",
+    identifier = " AND ".join([
+        "Apache-2.0",
+        "BSD-3-Clause",
+        "BSD-Source-Code",
+        "LGPL-2.0",
+        "LGPL-2.1",
+        "LicenseRef-sun-source",
+        "TU-Berlin-1.0",
+    ]),
+)
+
+license(
+    name = "license",
+    kind = ":license_kind",
+    text = ":LICENSE.txt",
+)
+
+mirror_url(
+    name = "mirror_url",
+    url = "https://third-party-mirror.googlesource.com/libsndfile",
+)
+
+concat_package_licenses(
+    name = "concat_licenses",
+)
+
+write_source_file(
+    name = "write_license",
+    in_file = ":concat_licenses",
+    out_file = "LICENSE.txt",
+)
+
+filegroup(
+    name = "all_srcs",
+    srcs = glob(["**"]),
+)
+
+cmake(
+    name = "libsndfile",
+    cache_entries = {
+        "BUILD_SHARED_LIBS": "OFF",
+        "ENABLE_EXTERNAL_LIBS": "OFF",
+        "ENABLE_MPEG": "OFF",
+        "BUILD_REGTEST": "OFF",
+        "BUILD_EXAMPLES": "OFF",
+        "BUILD_TESTING": "OFF",
+        "ANDROID": "ON",
+        "CMAKE_DISABLE_FIND_PACKAGE_ALSA": "TRUE",
+    },
+    lib_source = ":all_srcs",
+    out_static_libs = ["libsndfile.a"],
+    resource_size = "medium",
+    visibility = ["//visibility:public"],
+)
diff --git a/Building-for-Android.md b/Building-for-Android.md
new file mode 100644
index 0000000..eb5a0d3
--- /dev/null
+++ b/Building-for-Android.md
@@ -0,0 +1,46 @@
+# Building for Android
+
+Assuming the Android Ndk is installed at location `/path/to/toolchain`, building
+libsndfile for Android (arm-linux-androideabi) should be as simple as:
+```
+autoreconf -vif
+export ANDROID_TOOLCHAIN_HOME=/path/to/android/toolchain
+./Scripts/android-configure.sh
+make
+```
+The `Scripts/android-configure.sh` contains four of variables; `ANDROID_NDK_VER`,
+`ANDROID_GCC_VER`, `ANDROID_API_VER` and `ANDROID_TARGET` that can be overridden
+by setting them before the script is run.
+
+Since I (erikd), do almost zero Android development, I am happy accept patches
+for this documentation and script to improve its utility for real Android
+developers.
+
+---
+
+## Using CMake
+
+(Tested on Linux)
+
+For convenience, export the following variables:
+
+```
+export ANDROID_ABI=arm64-v8a
+export ANDROID_PLATFORM_API_LEVEL=29
+export NDK_ROOT=/path/to/android/ndk
+```
+
+Set `ANDROID_ABI`,  `ANDROID_PLATFORM_API_LEVEL`  according to your target system. Now cd into the libsndfile root directory, and run
+
+```
+cmake -S . -B build  -DCMAKE_TOOLCHAIN_FILE=$NDK_ROOT/build/cmake/android.toolchain.cmake -DANDROID_ABI=$ANDROID_ABI -DANDROID_PLATFORM=$ANDROID_PLATFORM_API_LEVEL
+```
+
+cd into `build` and run make
+
+```
+cd build
+make [-j <number of parallel jobs>]
+```
+
+This will build libsndfile for android.
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..1220853
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,190 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [1.2.2] - 2023-08-13
+
+### Fixed
+
+* Fixed invalid regex in src/create_symbols_file.py
+* Fixed passing null pointer to printf %s in tests
+
+## [1.2.1] - 2023-08-12
+
+### Added
+
+* RISC OS support to `sndfile-play`, thanks @ccawley2011
+* Move semantics to `SndFileHandle` C++ class, thanks @haydaralaidrus
+
+### Fixed
+
+* Various typos, thanks @@uniontech-lilinjie
+* Handling of absolute `CMAKE_INSTALL_LIBDIR`/`CMAKE_INSTALL_INCLUDEDIR`, thanks
+  @Staudey (issue #908)
+* Add `localtime_s` support to `sndfile-metadata-set`, thanks @neheb (issue #907)
+* Linking with CMake against `Ogg::ogg`, thanks @FtZPetruska
+* CMake `mpg123` module handling bugs, thanks @FtZPetruska
+* CMake dependencies handling, thanks @FtZPetruska
+* Various `Ogg` & `Opus` format fixes, thanks @weiliang (issue #888)
+* Redefining `ssize_t` when building with Autotools, thanks @ccawley2011
+  (issue #934)
+* Bug related to incorrect `realloc` use, thanks @Halmoni100
+* Style errors, thanks to @arthurt
+* AIFF format related bugs, thanks to @arthurt
+* Reading of MP3 files without Xing or INFO headers, thanks @arthurt
+* Coding style of `src/mpeg_decode.c`, thanks @arthurt
+* Various documentation types, thanks @luzpaz
+* Intrinsics inclusion for MSVC and ARM64/ARM64EC, thanks @frysee
+* `sf_open_fd`() regression, thanks @brentr (PR #950)
+* WAV format related bug, thanks @magnus-nomono (issue #930)
+
+### Removed
+
+* Obsolete file `libsndfile.spec.in``, thanks @janstary
+
+## [1.2.0] - 2022-12-25
+
+### Fixed
+
+* Searching for LAME dependency with CMake build system (issue #821).
+* CMake build from Autotools tarball (issue #816).
+* Build on UWP platform (issue #824).
+* Fix signed integer overflow (issue #785).
+* Skipping large wav chunks on stdin (PR #819).
+
+### Removed
+
+* Maximum samplerate limit, thanks @drmpeg, @justacec (issue #850).
+  
+  In version 1.1.0, an artificial limit of 655350 Hz was created, but as it
+  turned out, this is not enough for some scenarios.
+
+## [1.1.0] - 2022-03-27
+
+### Added
+
+* MPEG Encode/Decode Support.
+
+  Uses libmpg123 for decode, liblame for encode. Encoding and decoding support
+  is independent of each other and is split into separate files. MPEG support
+  is generalized as subformats, `SF_FORMAT_MPEG_LAYER`(I,II,III) so that it
+  might be used by other containers (`MPEG1WAVEFORMAT` for example), but also
+  contains a major format `SF_FORMAT_MPEG` for 'mp3 files.'
+
+  Encoding Status:
+  * Layer III encoding
+  * ID3v1 writing
+  * ID3v2 writing
+  * Lame/Xing Tag writing
+  * Bitrate selection command
+  * VBR or CBR
+  
+  Decoding Status:
+  * Layers I/II/III decoding
+  * ID3v1 reading
+  * ID3v2 reading
+  * Seeking
+* New fuzzer for OSS-Fuzz, thanks @DavidKorczynski.
+* This `CHANGELOG.md`. All notable changes to this project will be documented in
+  this file. The old `NEWS` file has been renamed to `NEWS.OLD` and is no longer
+  updated.
+* Add support for decoding MPEG III Audio in WAV files.
+* `SECURITY.md` file to give people instructions for reporting security
+  vulnerabilities, thanks @zidingz.
+* Support for [Vcpkg manifest mode](https://vcpkg.readthedocs.io/en/latest/users/manifests/).
+
+  If you have problems with manifest mode, disable it with `VCPKG_MANIFEST_MODE`
+  switch.
+* [Export CMake targets from the build tree (PR #802)](https://cmake.org/cmake/help/latest/guide/importing-exporting/index.html#exporting-targets-from-the-build-tree)
+* CIFuzz fuzzer, thanks to @AdamKorcz (PR #796)
+
+### Changed
+
+* `SFC_SET_DITHER_ON_READ` and `SFC_SET_DITHER_ON_WRITE` enums comments in
+  public header, thanks @SmiVan (issue #677).
+* `ENABLE_SNDFILE_WINDOWS_PROTOTYPES` define is deprecated and not needed
+  anymore.
+
+  Previously, in order for the [`sf_wchar_open`()](http://libsndfile.github.io/libsndfile/api.html#open)
+  function to become available on   the Windows platform, it was required to
+  perform certain actions:
+
+  ```c
+  #include <windows.h>
+  #define ENABLE_SNDFILE_WINDOWS_PROTOTYPES 1
+  #including <sndfile.h>
+  ```
+
+  These steps are no longer required and the `sf_wchar_open`() function is
+  always available on the Windows platform.
+* Use UTF-8 as internal path encoding on Windows platform.
+
+  This is an internal change to unify and simplify the handling of file paths.
+
+  On the Windows platform, the file path is always converted to UTF-8 and
+  converted to UTF-16 only for calls to WinAPI functions.
+
+  The behavior of the functions for opening files on other platforms does not
+  change.
+* Switch to .xz over .bz2 for release tarballs.
+* Disable static builds using Autotools by default. If you want static
+  libraries, pass --enable-static to ./configure
+
+### Fixed
+
+* Typo in `docs/index.md`.
+* Typo in `programs/sndfile-convert.c`, thanks @fjl.
+* Memory leak in `caf_read_header`(), credit to OSS-Fuzz ([issue 30375](https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=30375)).
+* Stack overflow in `guess_file_type`(), thanks @bobsayshilol, credit to
+  OSS-Fuzz ([issue 29339](https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=29339)).
+* Abort in fuzzer, thanks @bobsayshilol, credit to OSS-Fuzz
+  ([issue 26257](https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=26257)).
+* Infinite loop in `svx_read_header`(), thanks @bobsayshilol, credit to OSS-Fuzz
+  ([issue 25442](https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=25442)).
+* GCC and Clang pedantic warnings, thanks @bobsayshilol.
+* Normalisation issue when scaling floating point data to `int` in
+  `replace_read_f2i`(), thanks @bobsayshilol, (issue #702).
+* Missing samples when doing a partial read of Ogg file from index till the end
+  of file, thanks @arthurt (issue #643).
+* sndfile-salvage: Handle files > 4 GB on Windows OS
+* Undefined shift in `dyn_get_32bit`(), credit to OSS-Fuzz
+  ([issue 27366](https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=27366)).
+* Integer overflow in `nms_adpcm_update`(), credit to OSS-Fuzz
+  ([issue 25522](https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=25522)).
+* Integer overflow in `psf_log_printf`(), credit to OSS-Fuzz
+  ([issue 28441](https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=28441)),
+  ([issue 25624](https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=25624)).
+* ABI version incompatibility between Autotools and CMake build on Apple
+  platforms.
+
+  Now ABI must be compatible with Autotools builds. Note that this change
+  requires CMake >= 3.17 for building dylib on Apple platforms.
+
+* Fix build with Autotools + MinGW toolchain on Windows platform.
+
+  See https://github.com/msys2/MINGW-packages/issues/5803 for details.
+
+### Security
+
+* Heap buffer overflow in `wavlike_ima_decode_block`(), thanks @bobsayshilol,
+  credit to OSS-Fuzz ([issue 25530](https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=25530)).
+* Heap buffer overflow in `msadpcm_decode_block`(), thanks @bobsayshilol,
+  credit to OSS-Fuzz ([issue 26803](https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=26803)).
+* Heap buffer overflow in `psf_binheader_readf`(), thanks @bobsayshilol,
+  credit to OSS-Fuzz ([issue 26026](https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=26026)).
+* Index out of bounds in `psf_nms_adpcm_decode_block`(), credit to OSS-Fuzz
+  ([issue 25561](https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=25561)).
+* Heap buffer overflow in `flac_buffer_copy`(), thanks @yuawn,  @bobsayshilol.
+* Heap buffer overflow in `copyPredictorTo24`(), thanks @bobsayshilol,
+  credit to OSS-Fuzz ([issue 27503](https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=27503)).
+* Uninitialized variable in `psf_binheader_readf`(), thanks @shao-hua-li,
+  credit to OSS-Fuzz ([issue 25364](https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=25364)).
+
+[Unreleased]: https://github.com/libsndfile/libsndfile/compare/1.2.2...HEAD
+[1.2.2]: https://github.com/libsndfile/libsndfile/compare/1.2.1...1.2.2
+[1.2.1]: https://github.com/libsndfile/libsndfile/compare/1.2.0...1.2.1
+[1.2.0]: https://github.com/libsndfile/libsndfile/compare/1.1.0...1.2.0
+[1.1.0]: https://github.com/libsndfile/libsndfile/compare/1.0.31...1.1.0
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..b501f08
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,1604 @@
+cmake_minimum_required (VERSION 3.1..3.18)
+
+# MSVC runtime library flags are selected by an abstraction, CMake >= 3.15
+# This policy still need to be set even with cmake_minimum_required() command above.
+if (POLICY CMP0091)
+	if ((DEFINED ENABLE_STATIC_RUNTIME) AND (DEFINED CMAKE_MSVC_RUNTIME_LIBRARY))
+		message (FATAL_ERROR "Both ENABLE_STATIC_RUNTIME and CMAKE_MSVC_RUNTIME_LIBRARY are set.")
+		return ()
+	endif ()
+
+	if (DEFINED CMAKE_MSVC_RUNTIME_LIBRARY)
+		cmake_policy (SET CMP0091 NEW)
+	else ()
+		cmake_policy (SET CMP0091 OLD)
+	endif ()
+endif ()
+
+option (ENABLE_EXTERNAL_LIBS "Enable FLAC, Vorbis, and Opus codecs" ON)
+if (ENABLE_EXTERNAL_LIBS)
+	list (APPEND VCPKG_MANIFEST_FEATURES "external-libs")
+endif ()
+
+option (ENABLE_MPEG "Enable MPEG codecs" ON)
+if (ENABLE_MPEG)
+	list (APPEND VCPKG_MANIFEST_FEATURES "mpeg")
+endif ()
+
+option (ENABLE_EXPERIMENTAL "Enable experimental code" OFF)
+if (ENABLE_EXPERIMENTAL)
+	list (APPEND VCPKG_MANIFEST_FEATURES "speex")
+endif ()
+
+option (BUILD_REGTEST "Build regtest" OFF)
+if (BUILD_REGTEST)
+	list (APPEND VCPKG_MANIFEST_FEATURES "regtest")
+endif ()
+
+project(libsndfile VERSION 1.2.2)
+
+#
+# Variables
+#
+
+set (CMAKE_C_STANDARD 99)
+set (CMAKE_C_STANDARD_REQUIRED TRUE)
+set_property(GLOBAL PROPERTY USE_FOLDERS ON)
+
+set (PACKAGE_NAME ${PROJECT_NAME})
+set (CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
+set (CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
+set (CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
+set (CPACK_PACKAGE_VERSION_STAGE "")
+set (CPACK_PACKAGE_VERSION_FULL "${PROJECT_VERSION}${CPACK_PACKAGE_VERSION_STAGE}")
+
+#
+# System-wide includes
+#
+
+include (GNUInstallDirs)
+include (FeatureSummary)
+include (CMakeDependentOption)
+include (CTest)
+include (CheckCCompilerFlag)
+
+#
+# Options
+#
+
+option (BUILD_SHARED_LIBS "Build shared libraries" OFF)
+if (BUILD_SHARED_LIBS AND BUILD_TESTING)
+	set (BUILD_TESTING OFF)
+	message ("Build testing required static libraries. To prevent build errors BUILD_TESTING disabled.")
+endif ()
+option (BUILD_PROGRAMS "Build programs" ON)
+option (BUILD_EXAMPLES "Build examples" ON)
+option (ENABLE_CPACK "Enable CPack support" ON)
+option (ENABLE_BOW_DOCS "Enable black-on-white html docs" OFF)
+if (MSVC AND (DEFINED ENABLE_STATIC_RUNTIME))
+	option (ENABLE_STATIC_RUNTIME "Enable static runtime" ${ENABLE_STATIC_RUNTIME})
+elseif (MINGW)
+	option (ENABLE_STATIC_RUNTIME "Enable static runtime" OFF)
+endif ()
+option (ENABLE_PACKAGE_CONFIG "Generate and install package config file" ON)
+option (INSTALL_PKGCONFIG_MODULE "Generate and install pkg-config module" ON)
+
+list (APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
+if (CMAKE_VERSION VERSION_LESS 3.14)
+	list (APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/sqlite")
+endif ()
+
+#
+# Setup definitions
+#
+
+include(SndFileChecks)
+
+if (ENABLE_EXTERNAL_LIBS AND NOT (Vorbis_FOUND OR FLAC_FOUND OR OPUS_FOUND))
+	set (ENABLE_EXTERNAL_LIBS OFF)
+endif()
+if(ENABLE_MPEG AND (NOT HAVE_MPEG_LIBS))
+	set (ENABLE_MPEG OFF)
+endif()
+if (BUILD_REGTEST AND (NOT SQLITE3_FOUND))
+	set (BUILD_REGTEST OFF)
+endif()
+
+cmake_dependent_option (ENABLE_CPU_CLIP "Enable tricky cpu specific clipper" ON "CPU_CLIPS_POSITIVE;CPU_CLIPS_NEGATIVE" OFF)
+if (NOT ENABLE_CPU_CLIP)
+	set (CPU_CLIPS_POSITIVE FALSE)
+	set (CPU_CLIPS_NEGATIVE FALSE)
+endif ()
+cmake_dependent_option (ENABLE_COMPATIBLE_LIBSNDFILE_NAME "Set DLL name to libsndfile-1.dll (canonical name), sndfile.dll otherwise" OFF "WIN32;BUILD_SHARED_LIBS" OFF)
+cmake_dependent_option (INSTALL_MANPAGES "Install man pages for programs" ON "BUILD_PROGRAMS" OFF)
+
+if (NOT MSVC)
+	if (CPU_IS_X86)
+		check_c_compiler_flag (-msse2 HAVE_MSSE2_COMPILER_FLAG)
+		if (HAVE_MSSE2_COMPILER_FLAG)
+			cmake_dependent_option (ENABLE_SSE2 "Add SSE2 compiler flag" ON "HAVE_MSSE2_COMPILER_FLAG" OFF)
+		endif ()
+	endif ()
+endif ()
+if (ENABLE_SSE2)
+	add_compile_options (-msse2)
+endif ()
+
+set (HAVE_EXTERNAL_XIPH_LIBS ${ENABLE_EXTERNAL_LIBS})
+set (HAVE_SQLITE3 ${BUILD_REGTEST})
+set (HAVE_ALSA_ASOUNDLIB_H ${ALSA_FOUND})
+set (HAVE_SNDIO_H ${SNDIO_FOUND})
+
+set (ENABLE_EXPERIMENTAL_CODE ${ENABLE_EXPERIMENTAL})
+set (HAVE_MPEG ${ENABLE_MPEG})
+set (HAVE_SPEEX ${ENABLE_EXPERIMENTAL})
+
+add_feature_info (BUILD_SHARED_LIBS BUILD_SHARED_LIBS "build shared libraries")
+add_feature_info (ENABLE_EXTERNAL_LIBS ENABLE_EXTERNAL_LIBS "enable FLAC, Vorbis, and Opus codecs")
+add_feature_info (ENABLE_MPEG ENABLE_MPEG "enable MPEG audio (including mp3) codecs")
+add_feature_info (ENABLE_EXPERIMENTAL ENABLE_EXPERIMENTAL "enable experimental code")
+add_feature_info (BUILD_TESTING BUILD_TESTING "build tests")
+add_feature_info (BUILD_REGTEST BUILD_REGTEST "build regtest")
+add_feature_info (ENABLE_CPACK ENABLE_CPACK "enable CPack support")
+add_feature_info (ENABLE_CPU_CLIP ENABLE_CPU_CLIP "Enable tricky cpu specific clipper")
+add_feature_info (ENABLE_BOW_DOCS ENABLE_BOW_DOCS "enable black-on-white html docs")
+add_feature_info (ENABLE_PACKAGE_CONFIG ENABLE_PACKAGE_CONFIG "generate and install package config file")
+add_feature_info (INSTALL_PKGCONFIG_MODULE INSTALL_PKGCONFIG_MODULE "generate and install pkg-config module")
+add_feature_info (INSTALL_MANPAGES INSTALL_MANPAGES "install man pages for programs")
+if (WIN32 AND BUILD_SHARED_LIBS)
+	add_feature_info (ENABLE_COMPATIBLE_LIBSNDFILE_NAME ENABLE_COMPATIBLE_LIBSNDFILE_NAME "Set DLL name to libsndfile-1.dll (canonical name), sndfile.dll otherwise")
+endif ()
+
+if (HAVE_MSSE2_COMPILER_FLAG)
+	add_feature_info (ENABLE_SSE2 ENABLE_SSE2 "add SSE2 compiler flag")
+endif ()
+
+
+
+
+set_package_properties (Ogg PROPERTIES
+	TYPE RECOMMENDED
+	URL "www.xiph.org/ogg/"
+	DESCRIPTION "library for manipulating ogg bitstreams"
+	PURPOSE "Required to enable Vorbis, Speex, and Opus support"
+	)
+set_package_properties (Vorbis PROPERTIES
+	TYPE RECOMMENDED
+	URL "www.vorbis.com/"
+	DESCRIPTION "open source lossy audio codec"
+	PURPOSE "Enables Vorbis support"
+	)
+set_package_properties (FLAC PROPERTIES
+	TYPE RECOMMENDED
+	URL "www.xiph.org/flac/"
+	DESCRIPTION "Free Lossless Audio Codec Library"
+	PURPOSE "Enables FLAC support"
+	)
+set_package_properties (mp3lame PROPERTIES
+	TYPE RECOMMENDED
+	URL "https://lame.sourceforge.io/"
+	DESCRIPTION "High quality MPEG Audio Layer III (MP3) encoder"
+	PURPOSE "Enables MPEG layer III (MP3) writing support"
+	)
+set_package_properties (mpg123 PROPERTIES
+	TYPE RECOMMENDED
+	URL "https://www.mpg123.de/"
+	DESCRIPTION "MPEG Audio Layer I/II/III decoder"
+	PURPOSE "Enables MPEG Audio reading support"
+	)
+set_package_properties(Opus PROPERTIES
+	TYPE RECOMMENDED
+	URL	"www.opus-codec.org/"
+	DESCRIPTION "Standardized open source low-latency fullband codec"
+	PURPOSE	"Enables experimental Opus support"
+	)
+set_package_properties(Speex PROPERTIES TYPE OPTIONAL
+	URL "www.speex.org/"
+	DESCRIPTION "an audio codec tuned for speech"
+	PURPOSE "Enables experemental Speex support"
+	)
+set_package_properties (SQLite3 PROPERTIES
+	TYPE OPTIONAL
+	URL "www.sqlite.org/"
+	DESCRIPTION "light weight SQL database engine."
+	PURPOSE "Enables regtest"
+	)
+if (BUILD_SHARED_LIBS)
+	set_package_properties (PythonInterp PROPERTIES
+		TYPE REQUIRED
+		URL "www.python.org/"
+		DESCRIPTION "Python is a widely used high-level programming language."
+		PURPOSE "Required to build shared libraries"
+		)
+endif()
+
+feature_summary (WHAT ALL)
+
+#
+# Setup configuration
+#
+
+configure_file (src/config.h.cmake src/config.h)
+
+if (INSTALL_PKGCONFIG_MODULE)
+
+	set (prefix ${CMAKE_INSTALL_PREFIX})
+	set (exec_prefix "\$\{prefix\}")
+	set (libdir "${CMAKE_INSTALL_FULL_LIBDIR}")
+	set (includedir "${CMAKE_INSTALL_FULL_INCLUDEDIR}")
+	set (VERSION ${PROJECT_VERSION})
+	if (ENABLE_EXTERNAL_LIBS)
+		set (EXTERNAL_XIPH_REQUIRE "flac ogg vorbis vorbisenc opus")
+		if (ENABLE_EXPERIMENTAL)
+			set (EXTERNAL_XIPH_REQUIRE "${EXTERNAL_XIPH_REQUIRE} speex")
+		endif ()
+	endif ()
+	if (ENABLE_MPEG)
+		set (EXTERNAL_MPEG_REQUIRE "libmpg123")
+		set (EXTERNAL_MPEG_LIBS "-lmp3lame")
+	endif ()
+
+	configure_file (sndfile.pc.in sndfile.pc @ONLY)
+
+endif ()
+
+#
+# libsndfile
+#
+
+# Public libsndfile headers
+set (sndfile_HDRS
+	include/sndfile.h
+	include/sndfile.hh
+	)
+
+#
+# libsndfile static library
+#
+
+add_library (sndfile
+	src/sfconfig.h
+	src/sfendian.h
+	src/sf_unistd.h
+	src/common.h
+	src/common.c
+	src/file_io.c
+	src/command.c
+	src/pcm.c
+	src/ulaw.c
+	src/alaw.c
+	src/float32.c
+	src/double64.c
+	src/ima_adpcm.c
+	src/ms_adpcm.c
+	src/gsm610.c
+	src/dwvw.c
+	src/vox_adpcm.c
+	src/interleave.c
+	src/strings.c
+	src/dither.c
+	src/cart.c
+	src/broadcast.c
+	src/audio_detect.c
+ 	src/ima_oki_adpcm.c
+	src/ima_oki_adpcm.h
+	src/alac.c
+	src/chunk.c
+	src/ogg.h
+	src/ogg.c
+	src/chanmap.h
+	src/chanmap.c
+	src/id3.h
+	src/id3.c
+	$<$<BOOL:${WIN32}>:src/windows.c>
+	src/sndfile.c
+	src/aiff.c
+	src/au.c
+	src/avr.c
+	src/caf.c
+	src/dwd.c
+	src/flac.c
+	src/g72x.c
+	src/htk.c
+	src/ircam.c
+	src/macos.c
+	src/mat4.c
+	src/mat5.c
+	src/nist.c
+	src/paf.c
+	src/pvf.c
+	src/raw.c
+	src/rx2.c
+	src/sd2.c
+	src/sds.c
+	src/svx.c
+	src/txw.c
+	src/voc.c
+	src/wve.c
+	src/w64.c
+	src/wavlike.h
+	src/wavlike.c
+	src/wav.c
+	src/xi.c
+	src/mpc2k.c
+	src/rf64.c
+	src/ogg_vorbis.c
+	src/ogg_speex.c
+	src/ogg_pcm.c
+	src/ogg_opus.c
+	src/ogg_vcomment.h
+	src/ogg_vcomment.c
+	src/nms_adpcm.c
+	src/mpeg.c
+	src/mpeg_decode.c
+	src/mpeg_l3_encode.c
+	src/GSM610/config.h
+	src/GSM610/gsm.h
+	src/GSM610/gsm610_priv.h
+	src/GSM610/add.c
+	src/GSM610/code.c
+	src/GSM610/decode.c
+	src/GSM610/gsm_create.c
+	src/GSM610/gsm_decode.c
+	src/GSM610/gsm_destroy.c
+	src/GSM610/gsm_encode.c
+	src/GSM610/gsm_option.c
+	src/GSM610/long_term.c
+	src/GSM610/lpc.c
+	src/GSM610/preprocess.c
+	src/GSM610/rpe.c
+	src/GSM610/short_term.c
+	src/GSM610/table.c
+	src/G72x/g72x.h
+	src/G72x/g72x_priv.h
+	src/G72x/g721.c
+	src/G72x/g723_16.c
+	src/G72x/g723_24.c
+	src/G72x/g723_40.c
+	src/G72x/g72x.c
+	src/ALAC/ALACAudioTypes.h
+	src/ALAC/ALACBitUtilities.h
+	src/ALAC/EndianPortable.h
+	src/ALAC/aglib.h
+	src/ALAC/dplib.h
+	src/ALAC/matrixlib.h
+	src/ALAC/alac_codec.h
+	src/ALAC/shift.h
+	src/ALAC/ALACBitUtilities.c
+	src/ALAC/ag_dec.c
+	src/ALAC/ag_enc.c
+	src/ALAC/dp_dec.c
+	src/ALAC/dp_enc.c
+	src/ALAC/matrix_dec.c
+	src/ALAC/matrix_enc.c
+	src/ALAC/alac_decoder.c
+	src/ALAC/alac_encoder.c
+	${sndfile_HDRS}
+	${CMAKE_CURRENT_BINARY_DIR}/src/config.h
+	)
+
+add_library (SndFile::sndfile ALIAS sndfile)
+
+target_include_directories (sndfile
+	PUBLIC
+		$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+		$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>
+		$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+	PRIVATE
+		src
+		$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/src>
+	)
+target_link_libraries (sndfile
+	PRIVATE
+		$<$<BOOL:${LIBM_REQUIRED}>:m>
+		$<$<BOOL:${HAVE_EXTERNAL_XIPH_LIBS}>:Ogg::ogg>
+		$<$<BOOL:${HAVE_EXTERNAL_XIPH_LIBS}>:Vorbis::vorbisenc>
+		$<$<BOOL:${HAVE_EXTERNAL_XIPH_LIBS}>:FLAC::FLAC>
+		$<$<AND:$<BOOL:${ENABLE_EXPERIMENTAL}>,$<BOOL:${HAVE_EXTERNAL_XIPH_LIBS}>,$<BOOL:${HAVE_SPEEX}>>:Speex::Speex>
+		$<$<BOOL:${HAVE_EXTERNAL_XIPH_LIBS}>:Opus::opus>
+		$<$<BOOL:${HAVE_MPEG}>:MPG123::libmpg123>
+		$<$<BOOL:${HAVE_MPEG}>:mp3lame::mp3lame>
+	)
+set_target_properties (sndfile PROPERTIES
+	PUBLIC_HEADER "${sndfile_HDRS}"
+	)
+
+if (ENABLE_COMPATIBLE_LIBSNDFILE_NAME)
+	if (MINGW OR CYGWIN)
+		set_target_properties (sndfile PROPERTIES
+			RUNTIME_OUTPUT_NAME "sndfile-1"
+			)
+	else ()
+		set_target_properties (sndfile PROPERTIES
+			RUNTIME_OUTPUT_NAME "libsndfile-1"
+			)
+	endif ()
+endif ()
+
+if (BUILD_SHARED_LIBS)
+
+	#
+	# ABI version of library.
+	#
+
+	#
+	# Read libtool version from `configure.ac` and set libsndfile ABI version:
+	#
+	#   SNDFILE_ABI_VERSION_MAJOR
+	#   SNDFILE_ABI_VERSION_MINOR
+	#   SNDFILE_ABI_VERSION_PATCH
+	#   SNDFILE_ABI_VERSION
+	#
+	# and Mach-O current and compatibility versions:
+	#
+	#   SNDFILE_MACHO_CURRENT_VERSION
+	#   SNDFILE_MACHO_COMPATIBILITY_VERSION
+	#
+
+	include (SetupABIVersions)
+
+	setup_abi_versions()
+
+	if (WIN32)
+		set (VERSION_MAJOR ${CPACK_PACKAGE_VERSION_MAJOR})
+		set (GEN_TOOL cmake)
+		
+		set (WIN_RC_VERSION "${CPACK_PACKAGE_VERSION_MAJOR},${CPACK_PACKAGE_VERSION_MINOR},${CPACK_PACKAGE_VERSION_PATCH}")
+		set (CLEAN_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")
+		set (PACKAGE_VERSION ${CPACK_PACKAGE_VERSION})
+
+		configure_file (src/version-metadata.rc.in src/version-metadata.rc @ONLY)
+		target_sources (sndfile PRIVATE ${PROJECT_BINARY_DIR}/src/version-metadata.rc)
+	endif ()
+
+
+	set_target_properties (sndfile PROPERTIES
+		SOVERSION ${SNDFILE_ABI_VERSION_MAJOR}
+		VERSION ${SNDFILE_ABI_VERSION}
+		)
+
+	if (APPLE)
+		if (NOT (CMAKE_VERSION VERSION_LESS 3.17))
+			set_target_properties (sndfile PROPERTIES
+				MACHO_CURRENT_VERSION ${SNDFILE_MACHO_CURRENT_VERSION}
+				MACHO_COMPATIBILITY_VERSION ${SNDFILE_MACHO_COMPATIBILITY_VERSION}
+				)
+		else ()
+			message (FATAL_ERROR "Apple platform requires cmake >= 3.17 to build dylib.")
+		endif ()
+	endif ()
+
+	# Symbol files generation
+
+	if (WIN32)
+		set (SYMBOL_FILENAME "sndfile.def")
+		set (SYMBOL_OS "win32")
+	elseif ((CMAKE_SYSTEM_NAME MATCHES "Darwin") OR (CMAKE_SYSTEM_NAME MATCHES "Rhapsody"))
+		set (SYMBOL_FILENAME "Symbols.darwin")
+		set (SYMBOL_OS "darwin")
+	elseif (CMAKE_SYSTEM_NAME MATCHES "OS2")
+		set (SYMBOL_FILENAME "Symbols.os2")
+		set (SYMBOL_OS "os2")
+	elseif (UNIX)
+		set (SYMBOL_FILENAME "Symbols.gnu-binutils")
+		set (SYMBOL_OS "linux")
+	endif ()
+
+	if (DEFINED SYMBOL_OS)
+		add_custom_command (
+			OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/src/${SYMBOL_FILENAME}
+			COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/src/create_symbols_file.py ${SYMBOL_OS} ${SNDFILE_ABI_VERSION} > ${CMAKE_CURRENT_BINARY_DIR}/src/${SYMBOL_FILENAME}
+			COMMENT "Generating ${SYMBOL_FILENAME}..."
+			)
+
+		add_custom_target (GENFILES DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/src/${SYMBOL_FILENAME})
+		if (SYMBOL_OS MATCHES "win32")
+			target_sources (sndfile
+				PRIVATE
+					${CMAKE_CURRENT_BINARY_DIR}/src/${SYMBOL_FILENAME}
+				)
+		elseif (SYMBOL_OS MATCHES "darwin")
+			add_dependencies (sndfile GENFILES)
+			if (CMAKE_VERSION VERSION_LESS 3.13)
+				set_property (TARGET sndfile APPEND_STRING PROPERTY
+					LINK_FLAGS "-Wl,-exported_symbols_list -Wl,${CMAKE_CURRENT_BINARY_DIR}/src/${SYMBOL_FILENAME}"
+					)
+			else ()
+				target_link_options (sndfile PRIVATE "LINKER:-exported_symbols_list,${CMAKE_CURRENT_BINARY_DIR}/src/${SYMBOL_FILENAME}")
+			endif()
+		elseif (SYMBOL_OS MATCHES "os")
+			add_dependencies (sndfile GENFILES)
+			if (CMAKE_VERSION VERSION_LESS 3.13)
+				set_property (TARGET sndfile APPEND_STRING PROPERTY
+					LINK_FLAGS "-Wl,-export-symbols ${CMAKE_CURRENT_BINARY_DIR}/src/${SYMBOL_FILENAME}"
+					)
+			else ()
+				target_link_options (sndfile PRIVATE "LINKER:-export-symbols ${CMAKE_CURRENT_BINARY_DIR}/src/${SYMBOL_FILENAME}")
+			endif()
+		elseif (UNIX)
+			add_dependencies (sndfile GENFILES)
+			if (CMAKE_VERSION VERSION_LESS 3.13)
+				set_property (TARGET sndfile APPEND_STRING PROPERTY
+					LINK_FLAGS "-Wl,--version-script,${CMAKE_CURRENT_BINARY_DIR}/src/${SYMBOL_FILENAME}"
+					)
+			else ()
+				target_link_options (sndfile PRIVATE "LINKER:--version-script,${CMAKE_CURRENT_BINARY_DIR}/src/${SYMBOL_FILENAME}")
+			endif()
+		endif()
+	endif()
+
+endif ()
+
+#
+# Programs
+#
+
+if (BUILD_PROGRAMS)
+
+# sndfile-info
+
+	add_executable (sndfile-info
+		programs/sndfile-info.c
+		programs/common.c
+		programs/common.h
+		)
+	target_link_libraries (sndfile-info
+		PRIVATE
+			sndfile
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+
+# sndfile-play
+
+	add_executable (sndfile-play
+		$<$<NOT:$<BOOL:${BEOS}>>:programs/sndfile-play.c>
+		$<$<NOT:$<BOOL:${BEOS}>>:programs/common.c>
+		$<$<NOT:$<BOOL:${BEOS}>>:programs/sndfile-play.c>
+		$<$<BOOL:${BEOS}>:programs/sndfile-play-beos.cpp>
+		)
+	target_include_directories (sndfile-play
+		PRIVATE
+			src
+			${CMAKE_CURRENT_BINARY_DIR}/src
+		)
+	target_link_libraries (sndfile-play PRIVATE $<$<BOOL:${LIBM_REQUIRED}>:m>)
+	target_link_libraries (sndfile-play PRIVATE sndfile)
+	if (WIN32)
+		target_link_libraries(sndfile-play PRIVATE winmm)
+	# Maybe ALSA & Sndio are present in BeOS. They are not required
+	# so skip them anyway.
+	elseif ((NOT BEOS) AND ALSA_FOUND)
+		target_include_directories (sndfile-play PRIVATE ${ALSA_INCLUDE_DIRS})
+		target_link_libraries (sndfile-play PRIVATE ${ALSA_LIBRARIES})
+	elseif (CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
+		target_link_libraries (sndfile-play PRIVATE Sndio::Sndio)
+	endif ()
+
+# sndfile-convert
+
+	add_executable (sndfile-convert
+		programs/sndfile-convert.c
+		programs/common.c
+		programs/common.h
+		)
+	target_link_libraries (sndfile-convert PRIVATE sndfile $<$<BOOL:${LIBM_REQUIRED}>:m>)
+
+# sndfile-cmp
+
+	add_executable (sndfile-cmp
+		programs/sndfile-cmp.c
+		programs/common.c
+		programs/common.h
+		)
+	target_include_directories (sndfile-cmp
+		PUBLIC
+			src
+			${CMAKE_CURRENT_BINARY_DIR}/src
+			${CMAKE_CURRENT_BINARY_DIR}/tests
+		)
+	target_link_libraries (sndfile-cmp PRIVATE sndfile $<$<BOOL:${LIBM_REQUIRED}>:m>)
+
+# sndfile-metadata-set
+
+	add_executable (sndfile-metadata-set
+		programs/sndfile-metadata-set.c
+		programs/common.c
+		programs/common.h
+		)
+	target_include_directories (sndfile-metadata-set
+		PUBLIC
+			src
+			${CMAKE_CURRENT_BINARY_DIR}/src
+			${CMAKE_CURRENT_BINARY_DIR}/tests
+		)
+	target_link_libraries (sndfile-metadata-set PRIVATE sndfile $<$<BOOL:${LIBM_REQUIRED}>:m>)
+
+# sndfile-metadata-get
+
+	add_executable (sndfile-metadata-get
+		programs/sndfile-metadata-get.c
+		programs/common.c
+		programs/common.h
+		)
+	target_include_directories (sndfile-metadata-get
+		PUBLIC
+			src
+			${CMAKE_CURRENT_BINARY_DIR}/src
+			${CMAKE_CURRENT_BINARY_DIR}/tests
+		)
+	target_link_libraries (sndfile-metadata-get PRIVATE sndfile $<$<BOOL:${LIBM_REQUIRED}>:m>)
+
+# sndfile-interleave
+
+	add_executable (sndfile-interleave
+		programs/sndfile-interleave.c
+		programs/common.c
+		programs/common.h
+		)
+	target_link_libraries (sndfile-interleave PRIVATE sndfile $<$<BOOL:${LIBM_REQUIRED}>:m>)
+
+# sndfile-deinterleave
+
+	add_executable (sndfile-deinterleave
+		programs/sndfile-deinterleave.c
+		programs/common.c
+		programs/common.h
+		)
+	target_link_libraries (sndfile-deinterleave PRIVATE sndfile $<$<BOOL:${LIBM_REQUIRED}>:m>)
+
+# sndfile-concat
+
+	add_executable (sndfile-concat
+		programs/sndfile-concat.c
+		programs/common.c
+		programs/common.h
+		)
+	target_link_libraries (sndfile-concat PRIVATE sndfile $<$<BOOL:${LIBM_REQUIRED}>:m>)
+
+# sndfile-salvage
+
+	add_executable (sndfile-salvage
+		programs/sndfile-salvage.c
+		programs/common.c
+		programs/common.h
+		)
+	target_include_directories (sndfile-salvage
+		PUBLIC
+			src
+			${CMAKE_CURRENT_BINARY_DIR}/src
+			${CMAKE_CURRENT_BINARY_DIR}/tests
+		)
+	target_link_libraries (sndfile-salvage PRIVATE sndfile $<$<BOOL:${LIBM_REQUIRED}>:m>)
+
+	set (SNDFILE_PROGRAM_TARGETS
+		sndfile-info
+		sndfile-play
+		sndfile-convert
+		sndfile-cmp
+		sndfile-metadata-set
+		sndfile-metadata-get
+		sndfile-interleave
+		sndfile-deinterleave
+		sndfile-concat
+		sndfile-salvage
+		)
+
+	set_target_properties(${SNDFILE_PROGRAM_TARGETS} PROPERTIES FOLDER Programs)
+
+endif ()
+
+#
+# Examples
+#
+
+if (BUILD_EXAMPLES)
+
+# sndfile-to-text
+
+	add_executable (sndfile-to-text examples/sndfile-to-text.c)
+	target_link_libraries (sndfile-to-text PRIVATE sndfile)
+
+# sndfile-loopify
+
+	add_executable (sndfile-loopify examples/sndfile-loopify.c)
+	target_link_libraries (sndfile-loopify PRIVATE sndfile)
+
+# make_sine
+
+	add_executable (make_sine examples/make_sine.c)
+	target_link_libraries (make_sine
+		PRIVATE
+			sndfile
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+
+# sfprocess
+
+	add_executable (sfprocess examples/sfprocess.c)
+	target_link_libraries (sfprocess
+		PRIVATE
+			sndfile
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+
+# list_formats
+
+	add_executable (list_formats examples/list_formats.c)
+	target_link_libraries (list_formats
+		PRIVATE
+			sndfile
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+
+# generate
+
+	add_executable (generate examples/generate.c)
+	target_link_libraries (generate PRIVATE sndfile)
+
+# sndfilehandle
+
+	add_executable (sndfilehandle examples/sndfilehandle.cc)
+	target_link_libraries (sndfilehandle PUBLIC sndfile)
+
+	set (SNDFILE_EXAMPLE_TARGETS
+		sndfile-to-text
+		sndfile-loopify
+		make_sine
+		sfprocess
+		list_formats
+		generate
+		sndfilehandle
+		)
+
+	set_target_properties(${SNDFILE_EXAMPLE_TARGETS} PROPERTIES FOLDER Examples)
+
+
+endif ()
+
+#
+# sndfile-regtest
+#
+
+if (BUILD_REGTEST)
+
+	add_executable (sndfile-regtest
+		regtest/sndfile-regtest.c
+		regtest/database.c
+		regtest/checksum.c
+		)
+	target_include_directories (sndfile-regtest
+		PUBLIC
+			src
+			${CMAKE_CURRENT_BINARY_DIR}/src
+			${CMAKE_CURRENT_BINARY_DIR}/tests
+		)
+	target_link_libraries(sndfile-regtest
+		PRIVATE
+			sndfile
+			SQLite::SQLite3
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+
+endif ()
+
+#
+# Installation
+#
+
+if (ENABLE_PACKAGE_CONFIG)
+
+	if (WIN32 AND (NOT MINGW) AND (NOT CYGWIN))
+		set (CMAKE_INSTALL_PACKAGEDIR cmake) 
+	 else ()
+	 	set (CMAKE_INSTALL_PACKAGEDIR ${CMAKE_INSTALL_LIBDIR}/cmake/SndFile)
+	 endif()
+
+	 install (TARGETS ${SNDFILE_PROGRAM_TARGETS}
+		RUNTIME DESTINATION			${CMAKE_INSTALL_BINDIR}
+ 	)
+
+	install (TARGETS sndfile
+		EXPORT SndFileTargets
+		RUNTIME DESTINATION			${CMAKE_INSTALL_BINDIR}
+		ARCHIVE DESTINATION			${CMAKE_INSTALL_LIBDIR}
+		LIBRARY DESTINATION			${CMAKE_INSTALL_LIBDIR}
+		PUBLIC_HEADER DESTINATION	${CMAKE_INSTALL_INCLUDEDIR}
+	)
+
+	export (EXPORT SndFileTargets NAMESPACE SndFile:: FILE ${PROJECT_BINARY_DIR}/SndFileTargets.cmake)
+
+	include (CMakePackageConfigHelpers)
+
+	if (ENABLE_EXTERNAL_LIBS)
+		set (SndFile_WITH_EXTERNAL_LIBS 1)
+		list (APPEND FIND_MODULES_INSTALL_LIST 
+			${CMAKE_CURRENT_LIST_DIR}/cmake/FindFLAC.cmake
+			${CMAKE_CURRENT_LIST_DIR}/cmake/FindOgg.cmake
+			${CMAKE_CURRENT_LIST_DIR}/cmake/FindOpus.cmake
+			${CMAKE_CURRENT_LIST_DIR}/cmake/FindVorbis.cmake)
+	else ()
+		set (SndFile_WITH_EXTERNAL_LIBS 0)
+	endif ()
+
+	if(ENABLE_MPEG)
+		set (SndFile_WITH_MPEG 1)
+		list (APPEND FIND_MODULES_INSTALL_LIST 
+			${CMAKE_CURRENT_LIST_DIR}/cmake/Findmpg123.cmake
+			${CMAKE_CURRENT_LIST_DIR}/cmake/Findmp3lame.cmake)
+	else ()
+		set (SndFile_WITH_MPEG 0)
+	endif ()
+
+	set (INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR})
+	configure_package_config_file(cmake/SndFileConfig.cmake.in SndFileConfig.cmake
+		INSTALL_DESTINATION ${PROJECT_BINARY_DIR}
+		INSTALL_PREFIX ${PROJECT_BINARY_DIR}
+		PATH_VARS INCLUDE_INSTALL_DIR
+		)
+	configure_package_config_file(cmake/SndFileConfig.cmake.in SndFileConfig2.cmake
+		INSTALL_DESTINATION ${CMAKE_INSTALL_PACKAGEDIR}
+		PATH_VARS INCLUDE_INSTALL_DIR
+		)
+	write_basic_package_version_file (SndFileConfigVersion.cmake COMPATIBILITY SameMajorVersion)
+
+	install(EXPORT SndFileTargets
+		NAMESPACE SndFile::
+		DESTINATION ${CMAKE_INSTALL_PACKAGEDIR}
+		)
+	install(
+		FILES ${CMAKE_CURRENT_BINARY_DIR}/SndFileConfig2.cmake
+		RENAME SndFileConfig.cmake
+		DESTINATION	${CMAKE_INSTALL_PACKAGEDIR}
+		)
+	install(
+		FILES
+			${CMAKE_CURRENT_BINARY_DIR}/SndFileConfigVersion.cmake
+		DESTINATION	${CMAKE_INSTALL_PACKAGEDIR}
+		)
+
+		if (NOT BUILD_SHARED_LIBS AND FIND_MODULES_INSTALL_LIST)
+			file(COPY ${FIND_MODULES_INSTALL_LIST} DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
+			install(FILES ${FIND_MODULES_INSTALL_LIST} DESTINATION ${CMAKE_INSTALL_PACKAGEDIR})
+		endif ()
+else ()
+
+	install (TARGETS sndfile ${sdnfile_PROGRAMS}
+		RUNTIME DESTINATION			${CMAKE_INSTALL_BINDIR}
+		ARCHIVE DESTINATION			${CMAKE_INSTALL_LIBDIR}
+		LIBRARY DESTINATION			${CMAKE_INSTALL_LIBDIR}
+		PUBLIC_HEADER DESTINATION	${CMAKE_INSTALL_INCLUDEDIR})
+
+endif ()
+
+if (INSTALL_MANPAGES)
+
+	set (man_MANS
+		man/sndfile-info.1
+		man/sndfile-play.1
+		man/sndfile-convert.1
+		man/sndfile-cmp.1
+		man/sndfile-metadata-get.1
+		man/sndfile-concat.1
+		man/sndfile-interleave.1
+		man/sndfile-salvage.1
+		)
+	install (FILES ${man_MANS} DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
+	install (FILES man/sndfile-metadata-get.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 RENAME sndfile-metadata-set.1)
+	install (FILES man/sndfile-interleave.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 RENAME sndfile-deinterleave.1)
+endif ()
+
+if (ENABLE_BOW_DOCS)
+	set (HTML_BGCOLOUR "white")
+	set (HTML_FGCOLOUR "black")
+else ()
+	set (HTML_BGCOLOUR "black")
+	set (HTML_FGCOLOUR "white")
+endif ()
+set (dist_doc_DATA
+	docs/index.md
+	docs/libsndfile.jpg
+	docs/libsndfile.css
+	docs/print.css
+	docs/api.md
+	docs/command.md
+	docs/bugs.md
+	docs/formats.md
+	docs/sndfile_info.md
+	docs/new_file_type_howto.md
+	docs/win32.md
+	docs/FAQ.md
+	docs/lists.md
+	docs/embedded_files.md
+	docs/octave.md
+	docs/tutorial.md
+	)
+install (FILES ${dist_doc_DATA} DESTINATION ${CMAKE_INSTALL_DOCDIR})
+
+if (INSTALL_PKGCONFIG_MODULE)
+	install (FILES ${CMAKE_CURRENT_BINARY_DIR}/sndfile.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
+endif ()
+
+#
+# Testing
+#
+
+if (BUILD_TESTING)
+
+	enable_testing ()
+
+	include (CMakeAutoGen)
+
+	# generate tests sources from autogen templates
+	lsf_autogen (tests benchmark c)
+	lsf_autogen (tests floating_point_test c)
+	lsf_autogen (tests header_test c)
+	lsf_autogen (tests pcm_test c)
+	lsf_autogen (tests pipe_test c)
+	lsf_autogen (tests rdwr_test c)
+	lsf_autogen (tests scale_clip_test c)
+	lsf_autogen (tests utils c h)
+	lsf_autogen (tests write_read_test c)
+	lsf_autogen (src test_endswap c)
+
+	# utils static library
+	add_library(test_utils STATIC tests/utils.c)
+	target_include_directories (test_utils
+		PUBLIC
+			src
+			${CMAKE_CURRENT_BINARY_DIR}/src
+			${CMAKE_CURRENT_BINARY_DIR}/tests
+		)
+	target_link_libraries(test_utils PRIVATE sndfile)
+
+	### test_main
+
+	add_executable (test_main
+		src/test_main.c
+		src/test_main.h
+		src/test_conversions.c
+		src/test_float.c
+		src/test_endswap.c
+		src/test_audio_detect.c
+		src/test_log_printf.c
+		src/test_file_io.c
+		src/test_ima_oki_adpcm.c
+		src/test_strncpy_crlf.c
+		src/test_broadcast_var.c
+		src/test_cart_var.c
+		src/test_binheader_writef.c
+		src/test_nms_adpcm.c
+		)
+	target_include_directories (test_main
+		PUBLIC
+			src
+			${CMAKE_CURRENT_BINARY_DIR}/src
+			${CMAKE_CURRENT_BINARY_DIR}/tests
+		)
+	target_link_libraries (test_main PRIVATE sndfile)
+	if (MSVC)
+		target_compile_definitions (test_main PRIVATE _USE_MATH_DEFINES)
+	endif ()
+	add_test (test_main test_main)
+
+	### sfversion_test
+
+	add_executable (sfversion tests/sfversion.c)
+	target_include_directories (sfversion
+		PRIVATE
+			src
+			${CMAKE_CURRENT_BINARY_DIR}/src
+		)
+	target_link_libraries (sfversion sndfile)
+	add_test (sfversion sfversion)
+	set_tests_properties (sfversion PROPERTIES
+		PASS_REGULAR_EXPRESSION "${PACKAGE_NAME}-${CPACK_PACKAGE_VERSION_FULL}"
+		)
+
+	### error_test
+
+	add_executable (error_test tests/error_test.c)
+	target_link_libraries (error_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+	add_test (error_test error_test)
+
+	### ulaw_test
+	add_executable (ulaw_test tests/ulaw_test.c)
+	target_link_libraries (ulaw_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+	add_test (ulaw_test ulaw_test)
+
+	### alaw_test
+	add_executable (alaw_test tests/alaw_test.c)
+	target_link_libraries (alaw_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+	add_test (alaw_test alaw_test)
+
+	### dwvw_test
+
+	add_executable (dwvw_test tests/dwvw_test.c)
+	target_link_libraries (dwvw_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+	add_test (dwvw_test dwvw_test)
+
+	### command_test
+
+	add_executable (command_test tests/command_test.c)
+	target_link_libraries (command_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+	add_test (command_test command_test all)
+
+	### floating_point_test
+
+	add_executable (floating_point_test
+		tests/dft_cmp.c
+		tests/floating_point_test.c
+		)
+	target_link_libraries (floating_point_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+	target_include_directories (floating_point_test PRIVATE tests)
+	add_test (floating_point_test floating_point_test)
+
+	### checksum_test
+
+	add_executable (checksum_test tests/checksum_test.c)
+	target_link_libraries (checksum_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+	add_test (checksum_test checksum_test)
+
+	### scale_clip_test
+
+	add_executable (scale_clip_test tests/scale_clip_test.c)
+	target_link_libraries (scale_clip_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+	add_test (scale_clip_test scale_clip_test)
+
+	### headerless_test
+
+	add_executable (headerless_test tests/headerless_test.c)
+	target_link_libraries (headerless_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+	add_test (headerless_test headerless_test)
+
+	### rdwr_test
+
+	add_executable (rdwr_test tests/rdwr_test.c)
+	target_link_libraries (rdwr_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+	add_test (rdwr_test rdwr_test)
+
+	### locale_test
+
+	add_executable (locale_test tests/locale_test.c)
+	target_link_libraries (locale_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+	add_test (locale_test locale_test)
+
+	### win32_ordinal_test
+
+# Disabled because we cannot test with shared sndfile library
+#	if (WIN32 AND BUILD_SHARED_LIBS)
+#		add_executable (win32_ordinal_test tests/win32_ordinal_test.c)
+#		target_link_libraries (win32_ordinal_test PRIVATE sndfile test_utils)
+#		add_test (win32_ordinal_test win32_ordinal_test)
+#	endif ()
+
+	### cpp_test
+
+	add_executable (cpp_test tests/cpp_test.cc)
+	target_link_libraries (cpp_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+	add_test (cpp_test cpp_test)
+
+	### external_libs_test
+
+	add_executable (external_libs_test tests/external_libs_test.c)
+	target_link_libraries (external_libs_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+	add_test (external_libs_test external_libs_test)
+
+	### format_check_test
+
+	add_executable (format_check_test tests/format_check_test.c)
+	target_link_libraries (format_check_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+	add_test (format_check_test format_check_test)
+
+	### channel_test
+
+	add_executable (channel_test tests/channel_test.c)
+	target_link_libraries (channel_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+	add_test (channel_test channel_test)
+
+	### pcm_test
+
+	add_executable (pcm_test tests/pcm_test.c)
+	target_link_libraries (pcm_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+	add_test (pcm_test pcm_test)
+
+	### common test executables
+
+	add_executable (write_read_test
+		tests/generate.c
+		tests/write_read_test.c
+	)
+	target_link_libraries (write_read_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+	target_include_directories (write_read_test PRIVATE tests)
+
+	add_executable (lossy_comp_test tests/lossy_comp_test.c)
+	target_link_libraries (lossy_comp_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+
+	add_executable (peak_chunk_test tests/peak_chunk_test.c)
+	target_link_libraries (peak_chunk_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+
+	add_executable (header_test tests/header_test.c)
+	target_link_libraries (header_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+
+	add_executable (misc_test tests/misc_test.c)
+	target_link_libraries (misc_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+
+	add_executable (string_test tests/string_test.c)
+	target_link_libraries (string_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+
+	add_executable (multi_file_test tests/multi_file_test.c)
+	target_link_libraries (multi_file_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+
+	add_executable (aiff_rw_test tests/aiff_rw_test.c)
+	target_link_libraries (aiff_rw_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+
+	add_executable (chunk_test tests/chunk_test.c)
+	target_link_libraries (chunk_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+
+	add_executable (long_read_write_test tests/long_read_write_test.c)
+	target_link_libraries (long_read_write_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+
+	add_executable (raw_test tests/raw_test.c)
+	target_link_libraries (raw_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+
+	add_executable (compression_size_test tests/compression_size_test.c)
+	target_link_libraries (compression_size_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+
+	add_executable (ogg_test tests/ogg_test.c)
+	target_link_libraries (ogg_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+
+	add_executable (ogg_opus_test tests/ogg_opus_test.c)
+	target_link_libraries (ogg_opus_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+
+	add_executable (mpeg_test tests/mpeg_test.c)
+	target_link_libraries (mpeg_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+
+	add_executable (stdin_test tests/stdin_test.c)
+	target_link_libraries (stdin_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+	set_target_properties (stdin_test PROPERTIES RUNTIME_OUTPUT_DIRECTORY "tests")
+
+	add_executable (stdout_test tests/stdout_test.c)
+	target_link_libraries (stdout_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+	set_target_properties (stdout_test PROPERTIES RUNTIME_OUTPUT_DIRECTORY "tests")
+
+	add_executable (stdio_test tests/stdio_test.c)
+	target_link_libraries (stdio_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+
+	add_executable (pipe_test tests/pipe_test.c)
+	target_link_libraries (pipe_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+
+	add_executable (virtual_io_test tests/virtual_io_test.c)
+	target_link_libraries (virtual_io_test
+		PRIVATE
+			sndfile
+			test_utils
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+
+	### g72x_test
+
+	add_executable (g72x_test src/G72x/g72x_test.c)
+	target_include_directories (g72x_test
+		PRIVATE
+			src
+			${CMAKE_CURRENT_BINARY_DIR}/src
+		)
+	target_link_libraries (g72x_test
+		PRIVATE
+			sndfile
+			$<$<BOOL:${LIBM_REQUIRED}>:m>
+		)
+	add_test (g72x_test g72x_test all)
+
+	### aiff-tests
+
+	add_test (write_read_test_aiff write_read_test aiff)
+	add_test (lossy_comp_test_aiff_ulaw lossy_comp_test aiff_ulaw)
+	add_test (lossy_comp_test_aiff_alaw lossy_comp_test aiff_alaw)
+	add_test (lossy_comp_test_aiff_gsm610 lossy_comp_test aiff_gsm610)
+	add_test (peak_chunk_test_aiff peak_chunk_test aiff)
+	add_test (header_test_aiff header_test aiff)
+	add_test (misc_test_aiff misc_test aiff)
+	add_test (string_test_aiff string_test aiff)
+	add_test (multi_file_test_aiff multi_file_test aiff)
+	add_test (aiff_rw_test aiff_rw_test)
+
+	### au-tests
+
+	add_test (write_read_test_au write_read_test au)
+	add_test (lossy_comp_test_au_ulaw lossy_comp_test au_ulaw)
+	add_test (lossy_comp_test_au_alaw lossy_comp_test au_alaw)
+	add_test (lossy_comp_test_au_g721 lossy_comp_test au_g721)
+	add_test (lossy_comp_test_au_g723 lossy_comp_test au_g723)
+	add_test (header_test_au header_test au)
+	add_test (misc_test_au misc_test au)
+	add_test (multi_file_test_au multi_file_test au)
+
+	### caf-tests
+
+	add_test (write_read_test_caf write_read_test caf)
+	add_test (lossy_comp_test_caf_ulaw lossy_comp_test caf_ulaw)
+	add_test (lossy_comp_test_caf_alaw lossy_comp_test caf_alaw)
+	add_test (header_test_caf header_test caf)
+	add_test (peak_chunk_test_caf peak_chunk_test caf)
+	add_test (misc_test_caf misc_test caf)
+	add_test (chunk_test_caf chunk_test caf)
+	add_test (string_test_caf string_test caf)
+	add_test (long_read_write_test_alac long_read_write_test alac)
+
+	# wav-tests
+	add_test (write_read_test_wav write_read_test wav)
+	add_test (lossy_comp_test_wav_pcm lossy_comp_test wav_pcm)
+	add_test (lossy_comp_test_wav_ima lossy_comp_test wav_ima)
+	add_test (lossy_comp_test_wav_msadpcm lossy_comp_test wav_msadpcm)
+	add_test (lossy_comp_test_wav_ulaw lossy_comp_test wav_ulaw)
+	add_test (lossy_comp_test_wav_alaw lossy_comp_test wav_alaw)
+	add_test (lossy_comp_test_wav_gsm610 lossy_comp_test wav_gsm610)
+	add_test (lossy_comp_test_wav_g721 lossy_comp_test wav_g721)
+	add_test (lossy_comp_test_wav_nmsadpcm lossy_comp_test wav_nmsadpcm)
+	add_test (peak_chunk_test_wav peak_chunk_test wav)
+	add_test (header_test_wav header_test wav)
+	add_test (misc_test_wav misc_test wav)
+	add_test (string_test_wav string_test wav)
+	add_test (multi_file_test_wav multi_file_test wav)
+	add_test (chunk_test_wav chunk_test wav)
+
+	### w64-tests
+
+	add_test (write_read_test_w64 write_read_test w64)
+	add_test (lossy_comp_test_w64_ima lossy_comp_test w64_ima)
+	add_test (lossy_comp_test_w64_msadpcm lossy_comp_test w64_msadpcm)
+	add_test (lossy_comp_test_w64_ulaw lossy_comp_test w64_ulaw)
+	add_test (lossy_comp_test_w64_alaw lossy_comp_test w64_alaw)
+	add_test (lossy_comp_test_w64_gsm610 lossy_comp_test w64_gsm610)
+	add_test (header_test_w64 header_test w64)
+	add_test (misc_test_w64 misc_test w64)
+
+	### rf64-tests
+
+	add_test (write_read_test_rf64 write_read_test rf64)
+	add_test (header_test_rf64 header_test rf64)
+	add_test (misc_test_rf64 misc_test rf64)
+	add_test (string_test_rf64 string_test rf64)
+	add_test (peak_chunk_test_rf64 peak_chunk_test rf64)
+	add_test (chunk_test_rf64 chunk_test rf64)
+
+	### raw-tests
+	add_test (write_read_test_raw write_read_test raw)
+	add_test (lossy_comp_test_raw_ulaw lossy_comp_test raw_ulaw)
+	add_test (lossy_comp_test_raw_alaw lossy_comp_test raw_alaw)
+	add_test (lossy_comp_test_raw_gsm610 lossy_comp_test raw_gsm610)
+	add_test (lossy_comp_test_vox_adpcm lossy_comp_test vox_adpcm)
+	add_test (raw_test raw_test)
+
+	### paf-tests
+	add_test (write_read_test_paf write_read_test paf)
+	add_test (header_test_paf header_test paf)
+	add_test (misc_test_paf misc_test paf)
+
+	### svx-tests
+	add_test (write_read_test_svx write_read_test svx)
+	add_test (header_test_svx header_test svx)
+	add_test (misc_test_svx misc_test svx)
+
+	### nist-tests
+	add_test (write_read_test_nist write_read_test nist)
+	add_test (lossy_comp_test_nist_ulaw lossy_comp_test nist_ulaw)
+	add_test (lossy_comp_test_nist_alaw lossy_comp_test nist_alaw)
+	add_test (header_test_nist header_test nist)
+	add_test (misc_test_nist misc_test nist)
+
+	### ircam-tests
+	add_test (write_read_test_ircam write_read_test ircam)
+	add_test (lossy_comp_test_ircam_ulaw lossy_comp_test ircam_ulaw)
+	add_test (lossy_comp_test_ircam_alaw lossy_comp_test ircam_alaw)
+	add_test (header_test_ircam header_test ircam)
+	add_test (misc_test_ircam misc_test ircam)
+
+	### voc-tests
+	add_test (write_read_test_voc write_read_test voc)
+	add_test (lossy_comp_test_voc_ulaw lossy_comp_test voc_ulaw)
+	add_test (lossy_comp_test_voc_alaw lossy_comp_test voc_alaw)
+	add_test (header_test_voc header_test voc)
+	add_test (misc_test_voc misc_test voc)
+
+	### mat4-tests
+	add_test (write_read_test_mat4 write_read_test mat4)
+	add_test (header_test_mat4 header_test mat4)
+	add_test (misc_test_mat4 misc_test mat4)
+
+	### mat5-tests
+	add_test (write_read_test_mat5 write_read_test mat5)
+	add_test (header_test_mat5 header_test mat5)
+	add_test (misc_test_mat5 misc_test mat5)
+
+	### pvf-tests
+	add_test (write_read_test_pvf write_read_test pvf)
+	add_test (header_test_pvf header_test pvf)
+	add_test (misc_test_pvf misc_test pvf)
+
+	### xi-tests
+	add_test (lossy_comp_test_xi_dpcm lossy_comp_test xi_dpcm)
+
+	### htk-tests
+	add_test (write_read_test_htk write_read_test htk)
+	add_test (header_test_htk header_test htk)
+	add_test (misc_test_htk misc_test htk)
+
+	### avr-tests
+	add_test (write_read_test_avr write_read_test avr)
+	add_test (header_test_avr header_test avr)
+	add_test (misc_test_avr misc_test avr)
+
+	### sds-tests
+	add_test (write_read_test_sds write_read_test sds)
+	add_test (header_test_sds header_test sds)
+	add_test (misc_test_sds misc_test sds)
+
+	# sd2-tests
+	add_test (write_read_test_sd2 write_read_test sd2)
+
+	### wve-tests
+	add_test (lossy_comp_test_wve lossy_comp_test wve)
+
+	### mpc2k-tests
+	add_test (write_read_test_mpc2k write_read_test mpc2k)
+	add_test (header_test_mpc2k header_test mpc2k)
+	add_test (misc_test_mpc2k misc_test mpc2k)
+
+	### flac-tests
+	add_test (write_read_test_flac write_read_test flac)
+	add_test (compression_size_test_flac compression_size_test flac)
+	add_test (string_test_flac string_test flac)
+
+	### vorbis-tests
+	add_test (ogg_test ogg_test)
+	add_test (compression_size_test_vorbis compression_size_test vorbis)
+	add_test (lossy_comp_test_ogg_vorbis lossy_comp_test ogg_vorbis)
+	add_test (string_test_ogg string_test ogg)
+	add_test (misc_test_ogg misc_test ogg)
+
+	### opus-tests ###
+	add_test (ogg_opus_test ogg_opus_test)
+	add_test (compression_size_test_opus compression_size_test opus)
+	add_test (lossy_comp_test_ogg_opus lossy_comp_test ogg_opus)
+	add_test (string_test_opus string_test opus)
+
+	### mpeg-tests ###
+	add_test (mpeg_test mpeg_test)
+	add_test (compression_size_test_mpeg compression_size_test mpeg)
+
+	### io-tests
+	add_test (stdio_test stdio_test)
+	add_test (pipe_test pipe_test)
+	add_test (virtual_io_test virtual_io_test)
+
+	set (SNDFILE_TEST_TARGETS
+		test_utils
+		test_main
+		sfversion
+		error_test
+		ulaw_test
+		alaw_test
+		dwvw_test
+		command_test
+		floating_point_test
+		checksum_test
+		scale_clip_test
+		headerless_test
+		rdwr_test
+		locale_test
+		cpp_test
+		external_libs_test
+		format_check_test
+		channel_test
+		pcm_test
+		write_read_test
+		lossy_comp_test
+		peak_chunk_test
+		header_test
+		misc_test
+		string_test
+		multi_file_test
+		aiff_rw_test
+		chunk_test
+		long_read_write_test
+		raw_test
+		compression_size_test
+		ogg_test
+		stdin_test
+		stdout_test
+		stdio_test
+		pipe_test
+		virtual_io_test
+		g72x_test
+		)
+
+#	if (WIN32 AND BUILD_SHARED_LIBS)
+#		list (APPEND SNDFILE_TEST_TARGETS win32_ordinal_test)
+#	endif ()
+
+	set_target_properties(${SNDFILE_TEST_TARGETS} PROPERTIES FOLDER Tests)
+
+endif ()
+
+if (ENABLE_CPACK)
+	if ((NOT CPACK_PACKAGE_VERSION) AND CPACK_PACKAGE_VERSION_STAGE)
+		set(CPACK_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION_FULL}")
+	endif ()
+	include (CPack)
+endif ()
diff --git a/CODEOWNERS b/CODEOWNERS
new file mode 100644
index 0000000..d67616e
--- /dev/null
+++ b/CODEOWNERS
@@ -0,0 +1,14 @@
+# The owners of this repository. In order of suggestions as reviewers.
+*                           @evpobr @SoapGentoo @arthurt @erikd
+
+# Autotools
+configure.ac Makefile.am    @SoapGentoo @erikd
+
+# CMake
+CMakeLists.txt /cmake/      @evpobr
+
+# Ogg Opus format
+/src/ogg_opus.c             @arthurt
+
+# Documentation
+/doc/ /man/                 @evpobr
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..0bf14b7
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,49 @@
+# Contributing
+
+## Submitting Issues
+
+* If your issue is that libsndfile is not able to or is incorrectly reading one
+  of your files, please include the output of the `sndfile-info` program run
+  against the file.
+* If you are writing a program that uses libsndfile and you think there is a bug
+  in libsndfile, reduce your program to the minimal example, make sure you compile
+  it with warnings on (for GCC I would recommend at least `-Wall -Wextra`) and that
+  your program is warning free, and that is is error free when run under Valgrind
+  or compiled with AddressSanitizer.
+
+## Submitting Patches
+
+* Patches should pass all existing tests
+* Patches should pass all pre-commit hook tests.
+* Patches should always be submitted via a either Github "pull request" or a
+  via emailed patches created using "git format-patch".
+* Patches for new features should include tests and documentation.
+* Commit messages should follow the ["How to Write a Git Commit Message"](https://chris.beams.io/posts/git-commit/) guide:
+  1. Separate subject from body with a blank line
+  2. Limit the subject line to 50 characters
+  3. Capitalize the subject line
+  4. Do not end the subject line with a period
+  5. Use the imperative mood in the subject line
+  6. Wrap the body at 72 characters
+  7. Use the body to explain what and why vs. how
+
+  Additional rule: the commit message may contain a prefix. The prefix must
+  contain the name of the feature or source file related to the commit and must
+  end with a colon followed by the message body.
+
+  Examples of good commit messages:
+  1. Fix typo
+  2. Update CHANGELOG.md
+  3. Add ACT file format support
+  4. ogg_vorbis: Fix granule position when seeking Vorbis streams
+
+  Examples of bad commit messages:
+  1. Fixed bug (rule 5)
+  2. update docs (rule 3)
+  3. Add very cool feature. (rule 4)
+
+* Patches to fix bugs should either pass all tests, or modify the tests in some
+  sane way.
+* When a new feature is added for a particular file format and that feature
+  makes sense for other formats, then it should also be implemented for one
+  or two of the other formats.
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..c396169
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,503 @@
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+                  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                            NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library 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
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..3843688
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,3 @@
+For changes, please see the git commit history on github:
+
+  https://github.com/libsndfile/libsndfile/commits/master
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..c396169
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,503 @@
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+                  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                            NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library 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
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..4b42877
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,4721 @@
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+                  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                            NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+
+--------------------------------------------------------------------------------
+
+                     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library 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
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
+--------------------------------------------------------------------------------
+
+#!/bin/bash -e
+
+# Copyright (C) 2013-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Neither the author nor the names of any contributors may be used
+#       to endorse or promote products derived from this software without
+#       specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+--------------------------------------------------------------------------------
+
+http://www.gnu.org/copyleft/lesser.html).
+
+
+--------------------------------------------------------------------------------
+
+http://www.gnu.org/copyleft/lesser.html).
+
+## Download
+
+Check latest version on
+[GitHub Releases page](https://github.com/libsndfile/libsndfile/releases/).
+
+Binaries and source packages are signed by current releaser David Seifert aka
+@SoapGentoo. You can verify signatures with his public GPG key:
+
+```
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2
+
+mQINBFppABgBEAC42ZiNvV7BTIgR6TQy0YnF54fx3mVRP1u8Mq00UZa7reAsNKh7
+1H60j0W4s6+4pVVIKGfpVGxLwUdJe+KVCYw1Cd3YW6uMf5zZrC/ZWqnJiH/n6S6o
+1l4INII2o6YbGBnzIWBPRo7PlOL+mvgKTLpBSJPnhD8XDGN5wRiV8rL2+6Dptg0F
+nJt7oxECGF3OD3gk6HMel0o82CVkIqMtNaX1L/bhcdF7K0Rp2MXPZMmpn1izW5sI
+asN1G9+w+Zwj7kMJzq1Aw3ac+rsX4SEYdvXjS2QhDHQUIr6LXri3D2WbcEqIZj2R
+JVoVwblsrG11dYXFDBbgrq4NhgTBsxHYDlkr/qF2W+kbPC/nhSqTVZeCYvTBZbOQ
++RqyN/I0izukglnWmV1jGijFA8snyP8efx732hw/24zRYmtXOtnEITUpw8WOeZCq
+6uiHaQ+eopnY2ojBg9BI7WZm0AFn58xxT9soMsyFOUFgXTqaWFZWlJ3fhZE8/0v8
+JEu/kPGE5aJReT3b34B+Bojkj74XR+h2u7iJJBHMTE8RwGoUOZHer/XsL9xlcdks
+I+7TCjiq++ShaSSt2XsJmw2BhREohrjW/2KkwmvT3b44RMpKPB4WTH+++aqJQNeM
+IqmswOMoZvzEZezInj7WVY/r0WEei1Y6wt1tBrJ/cFf1oQBM1UmphxcrfQARAQAB
+tB9EYXZpZCBTZWlmZXJ0IDxzb2FwQGdlbnRvby5vcmc+iQJUBBMBCgA+BQsJCAcD
+BRUKCQgLBRYCAwEAAh4BAheAAhsBFiEEMdlcq22A0mIkShdQpHYg6AHkfpUFAl/V
+CvoFCQkuceIACgkQpHYg6AHkfpXYxA//aiJW1NwunpmzEc62id8lRMnoLHWVjISZ
+b+xSlm+hk4LYq+ZbthJDzKcT86/3DJOSE1zQw9wLuCao9IW2UfFJQBtR+TAfbagG
+0Yyk/kMcLoFJxnG1ywdJWypCAauuIhia52Z7PmmjsBbFwr6LygDwSQmZAyACMAs7
+TLQe+yERc2RNDsIEsquLSxxRF0Spk9gagWtKgrPc2XBjuNtQDwW7JgsOUoEeHyxC
+29fRUjC3o/pG2I6iAZp17OROZI5yl4TSORrSBDGIi2sayxyxP0x+IPKtrCUcBGNx
+wGp+56bP/V0hA6sgCPh/iwvqLoeibso6l/Kd4ltVAEQnHTd6fr8g+wLEUXfbJVTR
+7aeFUoaFmWjSPlQrNr6HlxSLV/kRx9kVJp1Pn16vkfVBF7fG7iDLiqphwEeQg5ND
+nmGeKAbRRNxFHyBHf0XRsaYiFZQckguO+71XSRtVx8/YP5nyNbtl9y1h/4JlT6Gy
+t7hb5twYFQyQrKss83E/Bo1sRdHpj0ibtqb4ZbYANbh482E6yFhAkuo8YjVTJipI
+1Ve8EBKnX3R+pDt147uyysNvtPVXML+sWpGSMVSm4NA8uT3F5nqxVwj+SeXy3Wq/
+CHQ2VBKGBC655G+wFD5C6O7cTx2MwH+2H8tzhWm+gFlI3MFKEXa/PC+YUC/diYcb
+BrApavriTRa5Ag0EWmkAZgEQAPXMD3mZI+ChvBysXZWksC88/uSEwFeb3XkcRm7v
+04GN7hcz+bfrmnUTB3tuE/ZQgv+u7ZjetvH1aEKieznn/GjnWoOBoJusOYvfAQeF
+0mQVi118QiOZRCnEZpkz+RY9TiXVgrZJg+AGqHZ3Ol4GkInEV2NWgH37Xal+HkFl
+rwI2U7mL0kZRG+LAVCQHKzqU0R0HE1XyJ4qf0awtG5Qi/TZvgXBdZPDXgr8i9Vlf
+UUu10c2XnXM0Av/YAlZmBFjVYrSOUCFenqSVqL+s9sTCVdWlJrGjrr3Ja4uT3kl2
+rLva0AR4oSQoxt8adKohmFz0vzOkQtCoRzhrCwoo3JvNjKdSNoOP1nSsxlO5ji8r
+ih5d+ajPgi580XyHLnrvG7vobR48qqscv1hizKuCgTacOTe6Db2Gqc8xF6v8HhJa
+KwWJtmFllIfN/tIvZ6BbbgHQn0IGf4CYnWf0SksPZqpBmTRpD2jfBxcj2UEg+AR3
+LARjuyUVpFJScyu6ExQG+6O+ByLL31iWP5MgUrza1rIpriPa3NT3rZ3DG2pvQrS3
+ySsrPzH7VRX8L1ThSMSzjwF96aMsd14s7XzR4EzNuWwZDukfs0yavZk6l4o1M0mb
+tbJi7hE4cz13KRHYvIkKMdZGYUnzRzZUDlsj2imakk3BR6GXnxZ1ST6062g+QxiL
+AJFLABEBAAGJBHIEGAEKACYCGwIWIQQx2VyrbYDSYiRKF1CkdiDoAeR+lQUCX9UL
+DQUJCS5xpwJAwXQgBBkBCgAdFiEEuNUxXaAAcsCoYIifzjbhFyAuOEIFAlppAGYA
+CgkQzjbhFyAuOELmrQ/9H9wrWsWa21STZdxUmyU2sh9VXAWEHl1Ey0fVTznDM0Fl
+zx5YSR/TmmnE36rpaz31Ttkx8SP914oV+mMgseecdya9Bf6uZL9Cv7V3KEsJBRL/
+ncrOWQBHP/Xy1X+mLD6A19xq7H4RihSLj0LeK2YVjrJzJ7wMf4mKXuBayQeAHImU
+WRCRTbmK3umh2nB5V0iPd/XZEIiYtiTPe+7E/va6+0bBvOumF3a+Z0iui7eU4hFC
+7Jk71D0dcg09SlIaNoMOrw7cMC3j2pMdKtsj8+0I6WBv14PhhqPAsnjdf7I/4NfK
+L7Jav8T/gDS01uA2Jxm72d+wr+eSjOBXa6x8CEbTqfkjAGxsWENThCp6zDkaXSDd
+JsV0va47vjzG8+wTDAvPy5IxIM/KZZdl4uWM+mF5K+q+eSTOHe7aLF2OdcussoBA
+A18zm994dAkG1COX/qpxanxx2bv/2IvCGPg+x6JtAN8ji2kncWu3dWGQdE5XbVjc
+fDwgsUPpp04G27Mr/x+HpEbgZ5SdA0dAqJktlNvCcHALhlblCWrsh/1QNjT/2iG8
+wsjcpEy/s4tWAuV4PTa4xvZ1JPS7Z7Eo5aBy9ZGOWG9SrHEiHnhkUsiswbHBOEjd
+pBSkmNElDcv9fRUahVCTPfvWBATFDrQyMjJBSm+cV8c/iFQM7isVSu8W7E0eetsJ
+EKR2IOgB5H6Vv9sP/1dxTvH0N0UoEoxIG/hnirEkbRpljdvqy4/uikYBKyQgSbo8
+VITTjea7gIhDztil9WZYt35jbOmoaGM2Z6TP2LEDOWgljYUNq9pl9Sc2GS8cNtEO
+WxExzGOc1Flo730dX3A85Ks3+0WPXZjLDcRRcPVkFd5WLQQDV1YVYopWkuQBC+Br
+4q3uv+sk+bw6gDa9+zFBbDuegdsYuTXrFHoxHz2GRv9Yb7ULCMgpFeNKDgtQq91u
+RqewoTwQp9tlp91LH/hh7R0Q4DRgeFDkLnVRXwSKjVvCrT5cBgImGwtFTGS4egoy
+MDKd/KKjZllp1ahRCln1XfmFQyQVMVvuF/JTtt31n6KwXwK2yxIlXB01xvRH+Ees
+AWeRYWKWXydaAY/9Ve0/PLFlgsr/XUGvt0GoEKe7odD3nZgg6015+/8JTroKw19L
+NZkhdfFMl11Zi0j5k3UbyzjYVpFSd8K2o0VoOG1LFsPp8tlRxNoVzpId0CX1au/p
+y1H7Wy/39mzriRG3rw+mJAQbBjN09putCltXFXpOEWk08n/N3vufCVQUoSu/2Bqw
+2HYj8VtToQp+O5dG3XxvDHINtInP1yr2Wcw2plna0KoXLwv/lZgDm3LN+eCWpG6d
+N/xk25DTSqTHArUQIEkhcHYK6GnyxUcvoKtG88hXtqEPYXiK08FZYAUPTnDYuQIN
+BFppAIkBEADDjvQZUs1NoqJpxkD2QDBudU1DBCaeI1D6CancMtb5FebPUxgFlDMd
+CBGOun48dY5i87gDhT/qS3gP/Mv9rjKJmcG9JHfhpXdW73owxrcsQ96nxxVJNEVl
+UHJw00z8C9eGWqr0SzSoE33K/PkzSkgtsaotF6+3uCerWulweulmGa5dpVfV0mbS
+aVw8VmrhZ5NmCeodyy/lR85rPik5pb32NT6v7xBkgkfS0VYtPB2E5gW1pXX/jEOi
+Mfq9idOEP9lxrNXV9j49Lr0JQCwAcrYbQ2+VPe6eacJEjzJ/6HiUqhPrYdnvydmb
+hU+xmv2NjGp2UnDZDEhzQfwm6fMx+8Nx2uPzCnXQGoyRBwiC/KcdW0F1ZPKdSXqH
+NKoOF62pLvIMSmfI3ZVOrTohArfr1kFEYVDv9Nl7oY+qg2rZEc2srOF74a9Z46bR
+TDPsEQzE2UMCvu3+rofhSD7aRotlKeDCvbe2s0yE4Man457Xc3LXh8Gva8CzCOLE
+2eMhNTsHIZk68WgXp3/uvE4Xy42myrk1AV8XXDdlWgx0Kc/I6tE59O5NVPSfuGvH
+1a15KKx0F6euEnYDKKpQ5PDR6dSn61po0tfbt96m044G/xQFjrfhHei4jji9Ogd9
+vlXVAi2vn3+NCSHFP5l3igLByBHy9iLIdmz7yQuus/1nwRmxOHOf2QARAQABiQI8
+BBgBCgAmAhsMFiEEMdlcq22A0mIkShdQpHYg6AHkfpUFAl/VCxkFCQkucZAACgkQ
+pHYg6AHkfpVPSRAAmheYkYJmtDbkzPBBnj5mbCIQN1/G5PI9eixc/TXWFOXtcjU1
+mJlJpSidHJyLRrx7r0c+N+s8vnY/JuUBsNoMJMER+Mv/CFW4iFi59V534SyAb2S0
+7NINJnFNkXBY62CDz9KsMuv/MdSv2yLhPH2Tfrm/eDRQesj1PanE4U1cgjWyJRc/
+IOlaRHvTasWDLgwbQi8ykt+4xUWzL/YKHzB+KyyzBK7vPBXqySX8ka4BOw7SDwG5
+lX2gtmhk4AGBwVChLXKflqVx1WXj4DPOt0kmOKVnKFyvUijK58M0A2FMgFMXDTIS
+DRtoZPdx/rkODXxgS+W+27NcYAnxJiM0cQqizEnQh7PQ1KzgdChPejYXMKe9lwdn
+ssMUxrBpbuAuagEf+pebNjD2eaNR4p8kfaDdGn53q55ysDvoyxKvnVQGSk1FAR9Q
+s4N5a4f02U7dzlyEhEfIcuUlRCfnlpn4n725YIhHheDig5zKWoEZCkNIfiRcGzDl
+8Drj+tlZiUR+gDkIoWSBaCkKbIQlc8qCYy6Hm7oZBaol6xKlUnTMK2rjK8fR4i8r
+bVDWBAaWj3jcDHJ0Jg3fS/qBpeya/JXMp89TR8NK5Ys7PZpWbor+puXBYyXDAVx3
+rXQ7JBA5klHPxrgjso1S/LqwscKLENtrVjdjhryLBmPifrmofJRnrpiHIEa5Ag0E
+WmkAswEQAL0hKwsRybQzkNGpJP+ElLSwFHd7XQhr+qIwLllpumWtnIK/DHmv8SpW
+FqAYajmRTXipFcBHH25x2jIIliZidn0a9826l+sMzrFadMC6/W4pitP71TeqZzwn
+pAuHs14YL7Wiy0aJQnfbCpRzPq3kYyOXmhmY7lPWO0WdUpR6W8wUbleK5XOVDDRx
+aIC/M3hhDOxZOMzQ+pdn4BaOFQQ0ygsRkqOudbuc0R1giYRt1i6gMeT8gfzL9jlw
+HcJ+aVnxdUQQ4uC47oKo/+lg7qh7LsiW79pQC1Bcdm8lhRmqtxe6ub60ecjax3XU
+1ILIEfIFCv6M7LRUAwz0bqk35spgkJqrGGKkdeWEKAFHg2QWR2F0zy+HdlPLfKxO
+uhaccpwc9EJtf744GS0SXa2AXr32j56n7CFcEjFcIQPBC6OJn6eA3hOVUYGZ7SrT
+4fsmZiFAdGEkvLKFuNhju1Hj2EJQUY1pm4GSBco7BR8x+QqoYrt5clU3WxRMNfTR
+0Rtuzsh4xskXNVMMgvKOahAtxENv2M2Cx6zJPVL5dmaysP7d6QRVeOQA5PwkcZ5Q
+qK6JtDZj2jpaKQH4Za715kiIcdqMDSkwxa6avc0kARHvfFcBR4hwDm1GAlaKG7eH
+8TOGGQIk8x2F3s4l8mTJVLWTP/uJYnkYBdqANYo5t1NIQLvwLFV3ABEBAAGJAjwE
+GAEKACYCGyAWIQQx2VyrbYDSYiRKF1CkdiDoAeR+lQUCX9ULIwUJCS5xcAAKCRCk
+diDoAeR+leekD/sF7aHH0W35ckWrXZlfSp0qHPWrBUaLBI9OAUHenRhgs4SbK0D4
+wqEiu0C5iDQojpXAeALQ8g/1pUsZ1yuFqYbGYWrHkA0Pm+P3tAGB4LMZ41YfvROP
+uaiW/+IMJbWllgRtaDt8/NtCgs30WI9I+az5M29HcGfvEwEUykrBx3dE9T+1ui3O
+capdd+GMvdAAsX5PyVkjWgZ7GrZeH8mG7UysYfT4qthxEtQfZ/u8ceSduKA46ugh
+C2eafIDNvluqn7BU4oKxME61u6C8BN2yHLI6LV0Tr4z5H8joVbM4BSFMwLVGlsXf
+HhB8kLiErN6bXolxsjARlmYiD9S9H2AcYidr6RYXf2EVFSpBG59xn1WTDN+DsHQf
+7btNPEPl/OPxa3OQjG+xn8USddiP0N0B4xsyzMNCCKDgvXXcIhX55KG9eh3Tc98S
+fEyhxu8ybZBIGmTJysPKxijfvSgQF+RPNTsz9lvXqkoK7RTgeYMschpjJEznCLbt
+M6eTDb5z0G5uLXh6+dYxtDOlPogI5OHd+G51LwCjvrQ+AtIUCgafuemwA9mpFT2b
+svb/qcxSVUb44bVaNHn1JHebX2YbokGtBOm1x2PI5fT8n6YIIYz3jKYOZAYdUT7x
+6qURyNjOfG4aPJIATwuh4GSNuxUG40+yuT+XfQF24mu1esS1J3wzRloJ7w==
+=K3x+
+-----END PGP PUBLIC KEY BLOCK-----
+```
+
+## See Also
+
+* [sndfile-tools](https://github.com/libsndfile/sndfile-tools): a small
+collection of programs which use libsndfile.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2002-2011 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in
+**       the documentation and/or other materials provided with the
+**       distribution.
+**     * Neither the author nor the names of any contributors may be used
+**       to endorse or promote products derived from this software without
+**       specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2001-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in
+**       the documentation and/or other materials provided with the
+**       distribution.
+**     * Neither the author nor the names of any contributors may be used
+**       to endorse or promote products derived from this software without
+**       specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 1999-2012 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in
+**       the documentation and/or other materials provided with the
+**       distribution.
+**     * Neither the author nor the names of any contributors may be used
+**       to endorse or promote products derived from this software without
+**       specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2001-2013 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**	 * Redistributions of source code must retain the above copyright
+**	   notice, this list of conditions and the following disclaimer.
+**	 * Redistributions in binary form must reproduce the above copyright
+**	   notice, this list of conditions and the following disclaimer in
+**	   the documentation and/or other materials provided with the
+**	   distribution.
+**	 * Neither the author nor the names of any contributors may be used
+**	   to endorse or promote products derived from this software without
+**	   specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 1999-2015 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in
+**       the documentation and/or other materials provided with the
+**       distribution.
+**     * Neither the author nor the names of any contributors may be used
+**       to endorse or promote products derived from this software without
+**       specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2008-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in
+**       the documentation and/or other materials provided with the
+**       distribution.
+**     * Neither the author nor the names of any contributors may be used
+**       to endorse or promote products derived from this software without
+**       specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 1999-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2005-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in
+**       the documentation and/or other materials provided with the
+**       distribution.
+**     * Neither the author nor the names of any contributors may be used
+**       to endorse or promote products derived from this software without
+**       specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 1999-2019 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2008 George Blood Audio
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in
+**       the documentation and/or other materials provided with the
+**       distribution.
+**     * Neither the author nor the names of any contributors may be used
+**       to endorse or promote products derived from this software without
+**       specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 1999-2013 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in
+**       the documentation and/or other materials provided with the
+**       distribution.
+**     * Neither the author nor the names of any contributors may be used
+**       to endorse or promote products derived from this software without
+**       specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2008-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2008 Conrad Parker <conrad@metadecks.org>
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in
+**       the documentation and/or other materials provided with the
+**       distribution.
+**     * Neither the author nor the names of any contributors may be used
+**       to endorse or promote products derived from this software without
+**       specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 1999-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in
+**       the documentation and/or other materials provided with the
+**       distribution.
+**     * Neither the author nor the names of any contributors may be used
+**       to endorse or promote products derived from this software without
+**       specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 1999-2019 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in
+**       the documentation and/or other materials provided with the
+**       distribution.
+**     * Neither the author nor the names of any contributors may be used
+**       to endorse or promote products derived from this software without
+**       specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2009-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in
+**       the documentation and/or other materials provided with the
+**       distribution.
+**     * Neither the author nor the names of any contributors may be used
+**       to endorse or promote products derived from this software without
+**       specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2009-2015 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in
+**       the documentation and/or other materials provided with the
+**       distribution.
+**     * Neither the author nor the names of any contributors may be used
+**       to endorse or promote products derived from this software without
+**       specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2008-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2008-2010 George Blood Audio
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in
+**       the documentation and/or other materials provided with the
+**       distribution.
+**     * Neither the author nor the names of any contributors may be used
+**       to endorse or promote products derived from this software without
+**       specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2008-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2008-2010 George Blood Audio
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in
+**       the documentation and/or other materials provided with the
+**       distribution.
+**     * Neither the author nor the names of any contributors may be used
+**       to endorse or promote products derived from this software without
+**       specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 1999-2018 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in
+**       the documentation and/or other materials provided with the
+**       distribution.
+**     * Neither the author nor the names of any contributors may be used
+**       to endorse or promote products derived from this software without
+**       specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2010-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in
+**       the documentation and/or other materials provided with the
+**       distribution.
+**     * Neither the author nor the names of any contributors may be used
+**       to endorse or promote products derived from this software without
+**       specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+--------------------------------------------------------------------------------
+
+#!/usr/bin/env python
+
+# Copyright (C) 2008-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#     * Neither the author nor the names of any contributors may be used
+#       to endorse or promote products derived from this software without
+#       specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+--------------------------------------------------------------------------------
+
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	 http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+
+
+--------------------------------------------------------------------------------
+
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	 http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*=============================================================================
+	File:		ALACBitUtilities.c
+
+	$NoKeywords: $
+
+
+--------------------------------------------------------------------------------
+
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	 http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*=============================================================================
+	File:		ALACBitUtilities.h
+
+	$NoKeywords: $
+
+
+--------------------------------------------------------------------------------
+
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+
+
+--------------------------------------------------------------------------------
+
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+** Copyright (C) 2013-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+//
+//  EndianPortable.h
+//
+
+
+--------------------------------------------------------------------------------
+
+Apache License
+
+Version 2.0, January 2004
+
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and
+distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the
+copyright owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other
+entities that control, are controlled by, or are under common control
+with that entity. For the purposes of this definition, "control" means
+(i) the power, direct or indirect, to cause the direction or management
+of such entity, whether by contract or otherwise, or (ii) ownership
+of fifty percent (50%) or more of the outstanding shares, or (iii)
+beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising
+permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications,
+including but not limited to software source code, documentation source,
+and configuration files.
+
+"Object" form shall mean any form resulting from mechanical
+transformation or translation of a Source form, including but not
+limited to compiled object code, generated documentation, and
+conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object
+form, made available under the License, as indicated by a copyright
+notice that is included in or attached to the work (an example is
+provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object
+form, that is based on (or derived from) the Work and for which the
+editorial revisions, annotations, elaborations, or other modifications
+represent, as a whole, an original work of authorship. For the purposes
+of this License, Derivative Works shall not include works that remain
+separable from, or merely link (or bind by name) to the interfaces of,
+the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original
+version of the Work and any modifications or additions to that Work or
+Derivative Works thereof, that is intentionally submitted to Licensor
+for inclusion in the Work by the copyright owner or by an individual
+or Legal Entity authorized to submit on behalf of the copyright owner.
+For the purposes of this definition, "submitted" means any form of
+electronic, verbal, or written communication sent to the Licensor
+or its representatives, including but not limited to communication
+on electronic mailing lists, source code control systems, and issue
+tracking systems that are managed by, or on behalf of, the Licensor
+for the purpose of discussing and improving the Work, but excluding
+communication that is conspicuously marked or otherwise designated in
+writing by the copyright owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity
+on behalf of whom a Contribution has been received by Licensor and
+subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions
+of this License, each Contributor hereby grants to You a perpetual,
+worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+copyright license to reproduce, prepare Derivative Works of, publicly
+display, publicly perform, sublicense, and distribute the Work and such
+Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+this License, each Contributor hereby grants to You a perpetual,
+worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except
+as stated in this section) patent license to make, have made, use,
+offer to sell, sell, import, and otherwise transfer the Work, where
+such license applies only to those patent claims licensable by such
+Contributor that are necessarily infringed by their Contribution(s)
+alone or by combination of their Contribution(s) with the Work to which
+such Contribution(s) was submitted. If You institute patent litigation
+against any entity (including a cross-claim or counterclaim in a
+lawsuit) alleging that the Work or a Contribution incorporated within
+the Work constitutes direct or contributory patent infringement, then
+any patent licenses granted to You under this License for that Work
+shall terminate as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+Work or Derivative Works thereof in any medium, with or without
+modifications, and in Source or Object form, provided that You meet the
+following conditions:
+
+You must give any other recipients of the Work or Derivative Works a
+copy of this License; and
+
+You must cause any modified files to carry prominent notices stating
+that You changed the files; and
+
+You must retain, in the Source form of any Derivative Works that You
+distribute, all copyright, patent, trademark, and attribution notices
+from the Source form of the Work, excluding those notices that do not
+pertain to any part of the Derivative Works; and
+
+If the Work includes a "NOTICE" text file as part of its distribution,
+then any Derivative Works that You distribute must include a readable
+copy of the attribution notices contained within such NOTICE file,
+excluding those notices that do not pertain to any part of the
+Derivative Works, in at least one of the following places: within a
+NOTICE text file distributed as part of the Derivative Works; within
+the Source form or documentation, if provided along with the Derivative
+Works; or, within a display generated by the Derivative Works, if and
+wherever such third-party notices normally appear. The contents of the
+NOTICE file are for informational purposes only and do not modify the
+License. You may add Your own attribution notices within Derivative
+Works that You distribute, alongside or as an addendum to the NOTICE
+text from the Work, provided that such additional attribution notices
+cannot be construed as modifying the License. You may add Your own
+copyright statement to Your modifications and may provide additional
+or different license terms and conditions for use, reproduction, or
+distribution of Your modifications, or for any such Derivative Works as
+a whole, provided Your use, reproduction, and distribution of the Work
+otherwise complies with the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+any Contribution intentionally submitted for inclusion in the Work by
+You to the Licensor shall be under the terms and conditions of this
+License, without any additional terms or conditions. Notwithstanding
+the above, nothing herein shall supersede or modify the terms of any
+separate license agreement you may have executed with Licensor regarding
+such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+names, trademarks, service marks, or product names of the Licensor,
+except as required for reasonable and customary use in describing the
+origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or agreed
+to in writing, Licensor provides the Work (and each Contributor
+provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+CONDITIONS OF ANY KIND, either express or implied, including, without
+limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT,
+MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
+solely responsible for determining the appropriateness of using or
+redistributing the Work and assume any risks associated with Your
+exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+whether in tort (including negligence), contract, or otherwise, unless
+required by applicable law (such as deliberate and grossly negligent
+acts) or agreed to in writing, shall any Contributor be liable to You
+for damages, including any direct, indirect, special, incidental, or
+consequential damages of any character arising as a result of this
+License or out of the use or inability to use the Work (including but
+not limited to damages for loss of goodwill, work stoppage, computer
+failure or malfunction, or any and all other commercial damages or
+losses), even if such Contributor has been advised of the possibility of
+such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing the
+Work or Derivative Works thereof, You may choose to offer, and charge a
+fee for, acceptance of support, warranty, indemnity, or other liability
+obligations and/or rights consistent with this License. However, in
+accepting such obligations, You may act only on Your own behalf and
+on Your sole responsibility, not on behalf of any other Contributor,
+and only if You agree to indemnify, defend, and hold each Contributor
+harmless for any liability incurred by, or claims asserted against, such
+Contributor by reason of your accepting any such warranty or additional
+liability.
+
+
+--------------------------------------------------------------------------------
+
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	 http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+	File:		ag_dec.c
+
+	Contains:   Adaptive Golomb decode routines.
+
+
+--------------------------------------------------------------------------------
+
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	 http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+	File:		ag_enc.c
+
+	Contains:   Adaptive Golomb encode routines.
+
+
+--------------------------------------------------------------------------------
+
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	 http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+	File:		aglib.h
+
+
+--------------------------------------------------------------------------------
+
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+
+
+--------------------------------------------------------------------------------
+
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2015 Erik de Castro Lopo <erikd@mega-nerd.com>
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	 http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+	File:		ALACDecoder.cpp
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "alac_codec.h"
+
+#include "dplib.h"
+#include "aglib.h"
+#include "matrixlib.h"
+#include "shift.h"
+
+#include "ALACBitUtilities.h"
+#include "EndianPortable.h"
+
+typedef enum
+{	false = 0,
+	true = 1
+} bool ;
+
+// constants/data
+const uint32_t kMaxBitDepth = 32 ;			// max allowed bit depth is 32
+
+
+// prototypes
+static int32_t	alac_fill_element (struct BitBuffer * bits) ;
+static int32_t	alac_data_stream_element (struct BitBuffer * bits) ;
+
+static void Zero32 (int32_t * buffer, uint32_t numItems, uint32_t stride) ;
+
+
+/*
+
+
+--------------------------------------------------------------------------------
+
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2015 Erik de Castro Lopo <erikd@mega-nerd.com>
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	 http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+
+
+--------------------------------------------------------------------------------
+
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	 http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+	File:		dp_dec.c
+
+	Contains:	Dynamic Predictor decode routines
+
+
+--------------------------------------------------------------------------------
+
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	 http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+	File:		dp_enc.c
+
+	Contains:	Dynamic Predictor encode routines
+
+
+--------------------------------------------------------------------------------
+
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+	File:		dplib.h
+
+	Contains:	Dynamic Predictor routines
+
+
+--------------------------------------------------------------------------------
+
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+	File:		matrix_dec.c
+
+	Contains:	ALAC mixing/matrixing decode routines.
+
+
+--------------------------------------------------------------------------------
+
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+	File:		matrix_enc.c
+
+	Contains:	ALAC mixing/matrixing encode routines.
+
+
+--------------------------------------------------------------------------------
+
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+	File:		matrixlib.h
+
+	Contains:	ALAC mixing/matrixing routines to/from 32-bit predictor buffers.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+ * This source code is a product of Sun Microsystems, Inc. and is provided
+ * for unrestricted use.  Users may copy or modify this source code without
+ * charge.
+ *
+ * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
+ * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun source code is provided with no support and without any obligation on
+ * the part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+
+
+--------------------------------------------------------------------------------
+
+/*
+ * This source code is a product of Sun Microsystems, Inc. and is provided
+ * for unrestricted use.  Users may copy or modify this source code without
+ * charge.
+ *
+ * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
+ * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun source code is provided with no support and without any obligation on
+ * the part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+/* 16kbps version created, used 24kbps code and changing as little as possible.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 1999-2011 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+ * This source code is a product of Sun Microsystems, Inc. and is provided
+ * for unrestricted use.  Users may copy or modify this source code without
+ * charge.
+ *
+ * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
+ * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun source code is provided with no support and without any obligation on
+ * the part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+
+#ifndef G72X_
+
+--------------------------------------------------------------------------------
+
+Copyright 1992, 1993, 1994 by Jutta Degener and Carsten Bormann,
+Technische Universitaet Berlin
+
+Any use of this software is permitted provided that this notice is not
+removed and that neither the authors nor the Technische Universitaet Berlin
+are deemed to have made any representations as to the suitability of this
+software for any purpose nor are held responsible for any defects of
+this software.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+
+As a matter of courtesy, the authors request to be informed about uses
+this software has found, about bugs in this software, and about any
+improvements that may be of general interest.
+
+Berlin, 28.11.1994
+Jutta Degener
+Carsten Bormann
+
+
+--------------------------------------------------------------------------------
+
+----------------------------------------------------------------------------
+
+Copyright 1992, 1993, 1994 by Jutta Degener and Carsten Bormann,
+Technische Universitaet Berlin
+
+Any use of this software is permitted provided that this notice is not
+removed and that neither the authors nor the Technische Universitaet Berlin
+are deemed to have made any representations as to the suitability of this
+software for any purpose nor are held responsible for any defects of
+this software.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+
+As a matter of courtesy, the authors request to be informed about uses
+this software has found, about bugs in this software, and about any
+improvements that may be of general interest.
+
+Berlin, 28.11.1994
+Jutta Degener (jutta@cs.tu-berlin.de)
+Carsten Bormann (cabo@cs.tu-berlin.de)
+
+----------------------------------------------------------------------------
+
+Jutta Degener and Carsten Bormann's work can be found on their homepage
+at:
+
+    http://kbs.cs.tu-berlin.de/~jutta/toast.html
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 1999-2018 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2005 David Viens <davidv@plogue.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <ctype.h>
+#include <inttypes.h>
+
+#include "sndfile.h"
+#include "sfendian.h"
+#include "common.h"
+#include "chanmap.h"
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2011-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 1999-2013 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 1999-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<fcntl.h>
+#include	<string.h>
+#include	<ctype.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+/*------------------------------------------------------------------------------
+** Macros to handle big/little endian issues.
+*/
+
+#define DOTSND_MARKER	(MAKE_MARKER ('.', 's', 'n', 'd'))
+#define DNSDOT_MARKER	(MAKE_MARKER ('d', 'n', 's', '.'))
+
+#define AU_DATA_OFFSET	24
+
+/*------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 1999-2012 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2004-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+#!/usr/bin/python
+
+# Copyright (C) 2006-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#     * Neither the author nor the names of any contributors may be used
+#       to endorse or promote products derived from this software without
+#       specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2006-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2006 Paul Davis <paul@linuxaudiosystems.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2005-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<stdlib.h>
+#include	<string.h>
+#include	<ctype.h>
+#include	<math.h>
+#include	<inttypes.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+#include	"chanmap.h"
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2012 Chris Roberts <c.roberts@csrfm.com>
+** Copyright (C) 2006-2013 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2006 Paul Davis <paul@linuxaudiosystems.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2009-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2009-2011 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2008-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2012 IOhannes m zmoelnig, IEM <zmoelnig@iem.at>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2001-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 1999-2019 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 1999-2018 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+#!/usr/bin/python
+
+# Copyright (C) 2003-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#     * Neither the author nor the names of any contributors may be used
+#       to endorse or promote products derived from this software without
+#       specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import re, sys
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2003-2011 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdlib.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+/*============================================================================
+**	Rule number 1 is to only apply dither when going from a larger bitwidth
+**	to a smaller bitwidth. This can happen on both read and write.
+**
+**	Need to apply dither on all conversions marked X below.
+**
+**	Dither on write:
+**
+**										Input
+**					|	short		int			float		double
+**			--------+-----------------------------------------------
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 1999-2015 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<stdlib.h>
+#include	<string.h>
+#include	<limits.h>
+#include	<math.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+#if CPU_IS_LITTLE_ENDIAN
+	#define DOUBLE64_READ	double64_le_read
+	#define DOUBLE64_WRITE	double64_le_write
+#elif CPU_IS_BIG_ENDIAN
+	#define DOUBLE64_READ	double64_be_read
+	#define DOUBLE64_WRITE	double64_be_write
+#endif
+
+/* A 32 number which will not overflow when multiplied by sizeof (double). */
+#define SENSIBLE_LEN	(0x8000000)
+
+/*--------------------------------------------------------------------------------------------
+**	Processor floating point capabilities. double64_get_capability () returns one of the
+**	latter three values.
+*/
+
+enum
+{	DOUBLE_UNKNOWN		= 0x00,
+	DOUBLE_CAN_RW_LE	= 0x23,
+	DOUBLE_CAN_RW_BE	= 0x34,
+	DOUBLE_BROKEN_LE	= 0x45,
+	DOUBLE_BROKEN_BE	= 0x56
+} ;
+
+/*--------------------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2002-2012 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<fcntl.h>
+#include	<string.h>
+#include	<ctype.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+#if (ENABLE_EXPERIMENTAL_CODE == 0)
+
+int
+dwd_open	(SF_PRIVATE *psf)
+{	if (psf)
+		return SFE_UNIMPLEMENTED ;
+	return 0 ;
+} /* dwd_open */
+
+#else
+
+/*------------------------------------------------------------------------------
+** Macros to handle big/little endian issues.
+*/
+
+#define SFE_DWD_NO_DWD			1666
+#define SFE_DWD_BAND_BIT_WIDTH 	1667
+#define SFE_DWD_COMPRESSION		1668
+
+#define	DWD_IDENTIFIER		"DiamondWare Digitized\n\0\x1a"
+#define	DWD_IDENTIFIER_LEN	24
+
+#define	DWD_HEADER_LEN		57
+
+/*------------------------------------------------------------------------------
+** Typedefs.
+*/
+
+/*------------------------------------------------------------------------------
+** Private static functions.
+*/
+
+static int	dwd_read_header (SF_PRIVATE *psf) ;
+
+static int	dwd_close		(SF_PRIVATE *psf) ;
+
+/*------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2002-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*===========================================================================
+** Delta Word Variable Width
+**
+** This decoder and encoder were implemented using information found in this
+** document : http://home.swbell.net/rubywand/R011SNDFMTS.TXT
+**
+** According to the document, the algorithm "was invented 1991 by Magnus
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2002-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2003 Ross Bencina <rbencina@iprimus.com.au>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2004-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2004 Tobias Gehrig <tgehrig@ira.uka.de>
+**
+** This program is free software ; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation ; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program ; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<stdlib.h>
+#include	<fcntl.h>
+#include	<string.h>
+#include	<ctype.h>
+#include	<math.h>
+
+#include	"sndfile.h"
+#include	"common.h"
+
+#if HAVE_EXTERNAL_XIPH_LIBS
+
+#include	<FLAC/stream_decoder.h>
+#include	<FLAC/stream_encoder.h>
+#include	<FLAC/metadata.h>
+
+/*------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 1999-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<stdlib.h>
+#include	<string.h>
+#include	<limits.h>
+#include	<math.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+#if CPU_IS_LITTLE_ENDIAN
+	#define FLOAT32_READ	float32_le_read
+	#define FLOAT32_WRITE	float32_le_write
+#elif CPU_IS_BIG_ENDIAN
+	#define FLOAT32_READ	float32_be_read
+	#define FLOAT32_WRITE	float32_be_write
+#endif
+
+/*--------------------------------------------------------------------------------------------
+**	Processor floating point capabilities. float32_get_capability () returns one of the
+**	latter four values.
+*/
+
+enum
+{	FLOAT_UNKNOWN		= 0x00,
+	FLOAT_CAN_RW_LE		= 0x12,
+	FLOAT_CAN_RW_BE		= 0x23,
+	FLOAT_BROKEN_LE		= 0x34,
+	FLOAT_BROKEN_BE		= 0x45
+} ;
+
+/*--------------------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 1999-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2002-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<fcntl.h>
+#include	<string.h>
+#include	<ctype.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+/*------------------------------------------------------------------------------
+** Macros to handle big/little endian issues.
+*/
+
+#define	SFE_HTK_BAD_FILE_LEN 	1666
+#define	SFE_HTK_NOT_WAVEFORM	1667
+
+/*------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2010-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2008-2019 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2019 Arthur Taylor <art@ified.ca>
+**
+** This program is free software ; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation ; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program ; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 1999-2020 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2007-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (c) 2007 <robs@users.sourceforge.net>
+**
+** This library is free software; you can redistribute it and/or modify it
+** under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or (at
+** your option) any later version.
+**
+** This library 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 Lesser
+** General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this library.  If not, write to the Free Software Foundation,
+** Fifth Floor, 51 Franklin Street, Boston, MA 02111-1301, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2007-2011 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (c) 2007 <robs@users.sourceforge.net>
+**
+** This library is free software; you can redistribute it and/or modify it
+** under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or (at
+** your option) any later version.
+**
+** This library 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 Lesser
+** General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this library.  If not, write to the Free Software Foundation,
+** Fifth Floor, 51 Franklin Street, Boston, MA 02111-1301, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2002-2013 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2001-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<fcntl.h>
+#include	<string.h>
+#include	<ctype.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2003-2011 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2002-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<fcntl.h>
+#include	<string.h>
+#include	<ctype.h>
+#include	<math.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+/*------------------------------------------------------------------------------
+** Information on how to decode and encode this file was obtained in a PDF
+** file which I found on http://www.wotsit.org/.
+** Also did a lot of testing with GNU Octave but do not have access to
+** Matlab (tm) and so could not test it there.
+*/
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2002-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<fcntl.h>
+#include	<string.h>
+#include	<ctype.h>
+#include	<math.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+/*------------------------------------------------------------------------------
+** Information on how to decode and encode this file was obtained in a PDF
+** file which I found on http://www.wotsit.org/.
+** Also did a lot of testing with GNU Octave but do not have access to
+** Matlab (tm) and so could not test it there.
+*/
+
+/*------------------------------------------------------------------------------
+** Macros to handle big/little endian issues.
+*/
+
+#define MATL_MARKER	(MAKE_MARKER ('M', 'A', 'T', 'L'))
+
+#define IM_MARKER	(('I' << 8) + 'M')
+#define MI_MARKER	(('M' << 8) + 'I')
+
+/*------------------------------------------------------------------------------
+** Enums and typedefs.
+*/
+
+enum
+{	MAT5_TYPE_SCHAR			= 0x1,
+	MAT5_TYPE_UCHAR			= 0x2,
+	MAT5_TYPE_INT16			= 0x3,
+	MAT5_TYPE_UINT16		= 0x4,
+	MAT5_TYPE_INT32			= 0x5,
+	MAT5_TYPE_UINT32		= 0x6,
+	MAT5_TYPE_FLOAT			= 0x7,
+	MAT5_TYPE_DOUBLE		= 0x9,
+	MAT5_TYPE_ARRAY			= 0xE,
+
+	MAT5_TYPE_COMP_USHORT	= 0x00020004,
+	MAT5_TYPE_COMP_UINT		= 0x00040006
+} ;
+
+typedef struct
+{	sf_count_t	size ;
+	int			rows, cols ;
+	char		name [32] ;
+} MAT5_MATRIX ;
+
+/*------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2008-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2019 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2021 Arthur Taylor <art@ified.ca>
+**
+** This program is free software ; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation ; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program ; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2019 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2019 Arthur Taylor <art@ified.ca>
+**
+** This program is free software ; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation ; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program ; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2019 - 2021 Arthur Taylor <art@ified.ca>
+** Copyright (C) 2019 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software ; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation ; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program ; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2020 Arthur Taylor <art@ified.ca>
+** Copyright (C) 2019 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software ; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation ; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program ; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 1999-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2002-2011 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<fcntl.h>
+#include	<string.h>
+#include	<ctype.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+#if (ENABLE_EXPERIMENTAL_CODE == 0)
+
+int
+new_open	(SF_PRIVATE *psf)
+{	if (psf)
+		return SFE_UNIMPLEMENTED ;
+	return (psf && 0) ;
+} /* new_open */
+
+#else
+
+/*------------------------------------------------------------------------------
+** Macros to handle big/little endian issues.
+*/
+
+/*------------------------------------------------------------------------------
+** Typedefs.
+*/
+
+/*------------------------------------------------------------------------------
+** Private static functions.
+*/
+
+static int	new_read_header (SF_PRIVATE *psf) ;
+
+/*------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 1999-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*
+**	Some of the information used to read NIST files was gleaned from
+**	reading the code of Bill Schottstaedt's sndlib library
+**		ftp://ccrma-ftp.stanford.edu/pub/Lisp/sndlib.tar.gz
+**	However, no code from that package was used.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<fcntl.h>
+#include	<string.h>
+#include	<ctype.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+/*------------------------------------------------------------------------------
+*/
+
+#define	NIST_HEADER_LENGTH	1024
+
+/*------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 1999-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2017 Arthur Taylor <art@ified.ca>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2002-2019 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2007 John ffitch
+** Copyright (C) 2018 Arthur Taylor <art@ified.ca>
+**
+** This program is free software ; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation ; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program ; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*
+** This file contains code based on OpusFile and Opus-Tools, both by
+** Xiph.Org. COPYING from each is identical and is as follows:
+**
+
+
+--------------------------------------------------------------------------------
+
+** Copyright (c) 1994-2013 Xiph.Org Foundation and contributors
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+**
+** - Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+**
+** - Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** - Neither the name of the Xiph.Org Foundation nor the names of its
+** contributors may be used to endorse or promote products derived from
+** this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION
+** OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2008-2011 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2018 Arthur Taylor <art@ified.ca>
+**
+** This program is free software ; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation ; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program ; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2013-2020 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2018 Arthur Taylor <art@ified.ca>
+**
+** This program is free software ; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation ; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program ; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*
+** This file contains code based on OpusFile and Opus-Tools, both by
+** Xiph.Org. COPYING from each is identical and is as follows:
+**
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2008-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software ; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation ; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program ; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2008-2019 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2018 Arthur Taylor <art@ified.ca>
+**
+** This program is free software ; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation ; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program ; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2008-2018 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2018 Arthur Taylor <art@ified.ca>
+**
+** This program is free software ; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation ; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program ; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2018-2021 Arthur Taylor <art@ified.ca>
+** Copyright (C) 2002-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2002-2005 Michael Smith <msmith@xiph.org>
+** Copyright (C) 2007 John ffitch
+**
+** This program is free software ; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation ; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program ; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*
+** Much of this code is based on the examples in libvorbis from the
+** XIPHOPHORUS Company http://www.xiph.org/ which has a BSD-style Licence
+
+
+--------------------------------------------------------------------------------
+
+** Copyright (c) 2002, Xiph.org Foundation
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+**
+** - Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+**
+** - Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** - Neither the name of the Xiph.org Foundation nor the names of its
+** contributors may be used to endorse or promote products derived from
+** this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION
+** OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE,
+** DATA, OR PROFITS ; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 1999-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+
+#include "sndfile.h"
+#include "sfendian.h"
+#include "common.h"
+
+/*------------------------------------------------------------------------------
+** Macros to handle big/little endian issues.
+*/
+
+#define FAP_MARKER	(MAKE_MARKER ('f', 'a', 'p', ' '))
+#define PAF_MARKER	(MAKE_MARKER (' ', 'p', 'a', 'f'))
+
+/*------------------------------------------------------------------------------
+** Other defines.
+*/
+
+#define	PAF_HEADER_LENGTH 			2048
+
+#define	PAF24_SAMPLES_PER_BLOCK		10
+#define	PAF24_BLOCK_SIZE			32
+
+/*------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2002-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<fcntl.h>
+#include	<string.h>
+#include	<ctype.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+/*------------------------------------------------------------------------------
+** Macros to handle big/little endian issues.
+*/
+
+#define PVF1_MARKER		(MAKE_MARKER ('P', 'V', 'F', '1'))
+
+/*------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 1999-2011 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+
+#include	"sndfile.h"
+#include	"common.h"
+
+/*------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2008-2018 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2009      Uli Franke <cls@nebadje.org>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*
+**	This format documented at:
+**	http://www.sr.se/utveckling/tu/bwf/prog/RF_64v1_4.pdf
+**
+**	But this may be a better reference:
+**	http://www.ebu.ch/CMSimages/en/tec_doc_t3306-2007_tcm6-42570.pdf
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<fcntl.h>
+#include	<string.h>
+#include	<ctype.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+#include	"wavlike.h"
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2001-2012 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<string.h>
+#include	<ctype.h>
+#include	<stdarg.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+#if (ENABLE_EXPERIMENTAL_CODE == 0)
+
+int
+rx2_open	(SF_PRIVATE *psf)
+{	if (psf)
+		return SFE_UNIMPLEMENTED ;
+	return 0 ;
+} /* rx2_open */
+
+#else
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2001-2020 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2004 Paavo Jumppanen
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*
+** The sd2 support implemented in this file was partially sponsored
+** (financially) by Paavo Jumppanen.
+*/
+
+/*
+** Documentation on the Mac resource fork was obtained here :
+** http://developer.apple.com/documentation/mac/MoreToolbox/MoreToolbox-99.html
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<stdlib.h>
+#include	<string.h>
+#include	<ctype.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+/*------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2002-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+
+#include "sndfile.h"
+#include "sfendian.h"
+#include "common.h"
+
+/*------------------------------------------------------------------------------
+*/
+
+#define	SDS_DATA_OFFSET				0x15
+#define SDS_BLOCK_SIZE				127
+
+#define SDS_AUDIO_BYTES_PER_BLOCK	120
+
+#define SDS_3BYTE_TO_INT_DECODE(x) (((x) & 0x7F) | (((x) & 0x7F00) >> 1) | (((x) & 0x7F0000) >> 2))
+#define SDS_INT_TO_3BYTE_ENCODE(x) (((x) & 0x7F) | (((x) << 1) & 0x7F00) | (((x) << 2) & 0x7F0000))
+
+/*------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2002-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2005-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 1999-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdarg.h>
+
+#include "sndfile.h"
+#include "sfendian.h"
+#include "common.h"
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2007-2011 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#else
+#include "sf_unistd.h"
+#endif
+
+#include <string.h>
+#include <errno.h>
+
+#include "common.h"
+#include "sfendian.h"
+
+#include "test_main.h"
+
+static unsigned char float_le_mono [] =
+{	0x36, 0x86, 0x21, 0x44, 0xB5, 0xB4, 0x49, 0x44, 0xA2, 0xC0, 0x71, 0x44, 0x7B, 0xD1, 0x8C, 0x44,
+	0x54, 0xAA, 0xA0, 0x44, 0x60, 0x67, 0xB4, 0x44, 0x22, 0x05, 0xC8, 0x44, 0x29, 0x80, 0xDB, 0x44,
+	0x04, 0xD5, 0xEE, 0x44, 0x27, 0x00, 0x01, 0x45, 0x50, 0x7F, 0x0A, 0x45, 0x53, 0xE6, 0x13, 0x45,
+	0x85, 0x33, 0x1D, 0x45, 0x43, 0x65, 0x26, 0x45, 0xEC, 0x79, 0x2F, 0x45, 0xE3, 0x6F, 0x38, 0x45,
+	0x98, 0x45, 0x41, 0x45, 0x77, 0xF9, 0x49, 0x45, 0xF6, 0x89, 0x52, 0x45, 0x8F, 0xF5, 0x5A, 0x45,
+	0xC9, 0x3A, 0x63, 0x45, 0x28, 0x58, 0x6B, 0x45, 0x3C, 0x4C, 0x73, 0x45, 0x9F, 0x15, 0x7B, 0x45,
+	0x75, 0x59, 0x81, 0x45, 0x64, 0x11, 0x85, 0x45, 0xF1, 0xB1, 0x88, 0x45, 0x78, 0x3A, 0x8C, 0x45,
+	0x58, 0xAA, 0x8F, 0x45, 0xF2, 0x00, 0x93, 0x45, 0xB2, 0x3D, 0x96, 0x45, 0x01, 0x60, 0x99, 0x45,
+	0x50, 0x67, 0x9C, 0x45, 0x15, 0x53, 0x9F, 0x45, 0xCC, 0x22, 0xA2, 0x45, 0xF0, 0xD5, 0xA4, 0x45,
+	0x07, 0x6C, 0xA7, 0x45, 0x9C, 0xE4, 0xA9, 0x45, 0x3D, 0x3F, 0xAC, 0x45, 0x7A, 0x7B, 0xAE, 0x45,
+	0xF2, 0x98, 0xB0, 0x45, 0x3C, 0x97, 0xB2, 0x45, 0x02, 0x76, 0xB4, 0x45, 0xEC, 0x34, 0xB6, 0x45,
+	0xA8, 0xD3, 0xB7, 0x45, 0xEB, 0x51, 0xB9, 0x45, 0x6F, 0xAF, 0xBA, 0x45, 0xF5, 0xEB, 0xBB, 0x45,
+	0x41, 0x07, 0xBD, 0x45, 0x21, 0x01, 0xBE, 0x45, 0x64, 0xD9, 0xBE, 0x45, 0xE3, 0x8F, 0xBF, 0x45,
+	0x7E, 0x24, 0xC0, 0x45, 0x15, 0x97, 0xC0, 0x45, 0x92, 0xE7, 0xC0, 0x45, 0xE8, 0x15, 0xC1, 0x45,
+	0x7E, 0x24, 0xC0, 0x45, 0x15, 0x97, 0xC0, 0x45, 0x92, 0xE7, 0xC0, 0x45, 0xE8, 0x15, 0xC1, 0x45,
+	0x7E, 0x24, 0xC0, 0x45, 0x15, 0x97, 0xC0, 0x45, 0x92, 0xE7, 0xC0, 0x45, 0xE8, 0x15, 0xC1, 0x45,
+} ;
+
+static unsigned char int24_32_le_stereo [] =
+{
+	0x00, 0xE7, 0xFB, 0xFF, 0x00, 0x7C, 0xFD, 0xFF, 0x00, 0xA2, 0xFC, 0xFF, 0x00, 0x2B, 0xFC, 0xFF,
+	0x00, 0xF3, 0xFD, 0xFF, 0x00, 0x19, 0xFB, 0xFF, 0x00, 0xA5, 0xFE, 0xFF, 0x00, 0x8D, 0xFA, 0xFF,
+	0x00, 0x91, 0xFF, 0xFF, 0x00, 0xB5, 0xFA, 0xFF, 0x00, 0x91, 0x00, 0x00, 0x00, 0x5E, 0xFB, 0xFF,
+	0x00, 0xD9, 0x01, 0x00, 0x00, 0x82, 0xFB, 0xFF, 0x00, 0xDF, 0x03, 0x00, 0x00, 0x44, 0xFC, 0xFF,
+	0x00, 0x1C, 0x05, 0x00, 0x00, 0x77, 0xFC, 0xFF, 0x00, 0x8D, 0x06, 0x00, 0x00, 0x4F, 0xFC, 0xFF,
+	0x00, 0x84, 0x07, 0x00, 0x00, 0x74, 0xFC, 0xFF, 0x00, 0x98, 0x08, 0x00, 0x00, 0x33, 0xFD, 0xFF,
+	0x00, 0xB9, 0x09, 0x00, 0x00, 0x48, 0xFF, 0xFF, 0x00, 0xD1, 0x0A, 0x00, 0x00, 0x10, 0x02, 0x00,
+	0x00, 0x28, 0x0C, 0x00, 0x00, 0xA2, 0x05, 0x00, 0x00, 0xA7, 0x0C, 0x00, 0x00, 0x45, 0x08, 0x00,
+	0x00, 0x44, 0x0D, 0x00, 0x00, 0x1A, 0x0A, 0x00, 0x00, 0x65, 0x0D, 0x00, 0x00, 0x51, 0x0B, 0x00,
+	0x00, 0x8B, 0x0D, 0x00, 0x00, 0x18, 0x0B, 0x00, 0x00, 0x37, 0x0E, 0x00, 0x00, 0x24, 0x0B, 0x00,
+	0x00, 0x00, 0x0F, 0x00, 0x00, 0xDD, 0x0A, 0x00, 0x00, 0x83, 0x10, 0x00, 0x00, 0x31, 0x0A, 0x00,
+	0x00, 0x07, 0x12, 0x00, 0x00, 0xC0, 0x08, 0x00, 0x00, 0xF7, 0x12, 0x00, 0x00, 0x47, 0x06, 0x00,
+	0x00, 0xDD, 0x12, 0x00, 0x00, 0x6A, 0x03, 0x00, 0x00, 0xD5, 0x11, 0x00, 0x00, 0x99, 0x00, 0x00,
+	0x00, 0x01, 0x10, 0x00, 0x00, 0xC5, 0xFE, 0xFF, 0x00, 0xF4, 0x0D, 0x00, 0x00, 0x97, 0xFD, 0xFF,
+	0x00, 0x62, 0x0B, 0x00, 0x00, 0x75, 0xFC, 0xFF, 0x00, 0xE9, 0x08, 0x00, 0x00, 0xC0, 0xFB, 0xFF,
+	0x00, 0x80, 0x06, 0x00, 0x00, 0x3C, 0xFB, 0xFF, 0x00, 0xDA, 0x03, 0x00, 0x00, 0xE4, 0xFA, 0xFF,
+	0x00, 0xEB, 0x01, 0x00, 0x00, 0x21, 0xFB, 0xFF, 0x00, 0x20, 0x00, 0x00, 0x00, 0xE7, 0xFB, 0xFF,
+} ;
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2010-2012 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2010-2013 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2006-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+[+ AutoGen5 template c +]
+/*
+** Copyright (C) 2002-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#else
+#include "sf_unistd.h"
+#endif
+
+#include "common.h"
+#include "sfendian.h"
+
+#include "test_main.h"
+
+#define	FMT_SHORT	"0x%04x\n"
+#define	FMT_INT		"0x%08x\n"
+#define	FMT_INT64	"0x%016" PRIx64 "\n"
+
+/*==============================================================================
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2002-2011 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2006-2012 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2003-2012 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2008-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2007-2018 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2017-2018 Arthur Taylor <art@ified.ca>
+**
+** This library is free software; you can redistribute it and/or modify it
+** under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or (at
+** your option) any later version.
+**
+** This library 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 Lesser
+** General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this library.  If not, write to the Free Software Foundation,
+** Fifth Floor, 51 Franklin Street, Boston, MA 02111-1301, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2002-2012 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*===========================================================================
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2001-2018 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2002-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 1999-2018 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<string.h>
+#include	<ctype.h>
+#include	<time.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+#include	"wavlike.h"
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 1999-2019 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2004-2005 David Viens <davidv@plogue.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<stdlib.h>
+#include	<string.h>
+#include	<ctype.h>
+#include	<time.h>
+#include	<inttypes.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+#include	"wavlike.h"
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 1999-2020 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2004-2005 David Viens <davidv@plogue.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 1999-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/* This file contains definitions commong to WAV and W64 files. */
+
+
+#ifndef WAVLIKE_H_INCLUDED
+#define WAVLIKE_H_INCLUDED
+
+/*------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2009-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2002-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2007 Reuben Thomas
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<fcntl.h>
+#include	<string.h>
+#include	<ctype.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+/*------------------------------------------------------------------------------
+** Macros to handle big/little endian issues, and other magic numbers.
+*/
+
+#define ALAW_MARKER			MAKE_MARKER ('A', 'L', 'a', 'w')
+#define SOUN_MARKER			MAKE_MARKER ('S', 'o', 'u', 'n')
+#define DFIL_MARKER			MAKE_MARKER ('d', 'F', 'i', 'l')
+#define ESSN_MARKER			MAKE_MARKER ('e', '*', '*', '\0')
+#define PSION_VERSION		((unsigned short) 3856)
+#define PSION_DATAOFFSET	0x20
+
+/*------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+
+/*
+** Copyright (C) 2003-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+
+#include "sndfile.h"
+#include "sfendian.h"
+#include "common.h"
+
+#define	MAX_XI_SAMPLES	16
+
+/*------------------------------------------------------------------------------
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..54035f2
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,546 @@
+## Process this file with automake to produce Makefile.in
+
+AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/include -I$(top_builddir)/include $(OS_SPECIFIC_CFLAGS)
+
+DISTCHECK_CONFIGURE_FLAGS = --enable-werror
+
+CLEANFILES = programs/*.wav octave-workspace
+
+if FULL_SUITE
+if BUILD_OCTAVE_MOD
+SUBDIRS = Octave
+endif
+endif
+
+EXTRA_DIST = sndfile.pc.in Scripts/android-configure.sh \
+	NEWS.OLD CHANGELOG.md Scripts/linux-to-win-cross-configure.sh \
+	CMakeLists.txt $(cmake_files) Win32 SECURITY.md
+
+cmake_files = cmake/ClipMode.cmake cmake/FindFLAC.cmake \
+	cmake/CMakeAutoGen.cmake cmake/CMakeAutoGenScript.cmake \
+	cmake/FindOgg.cmake cmake/FindVorbis.cmake cmake/FindSndio.cmake \
+	cmake/FindSpeex.cmake cmake/sqlite/FindSQLite3.cmake \
+	cmake/SndFileChecks.cmake cmake/TestInline.cmake \
+	cmake/TestLargeFiles.cmake cmake/TestInline.c.in \
+	cmake/FindOpus.cmake cmake/SndFileConfig.cmake.in \
+	cmake/CheckCPUArch.cmake cmake/CheckCPUArch.c.in \
+	cmake/Findmp3lame.cmake cmake/Findmpg123.cmake \
+	cmake/SetupABIVersions.cmake
+
+pkgconfig_DATA = sndfile.pc
+
+noinst_PROGRAMS =
+
+#===============================================================================
+
+test: check
+
+# Need this target to force building of test programs.
+checkprograms: $(check_PROGRAMS)
+
+########
+# src/ #
+########
+
+BUILT_SOURCES = src/test_endswap.c
+
+SYMBOL_FILES = src/Symbols.gnu-binutils src/Symbols.darwin src/libsndfile-1.def src/Symbols.os2 src/Symbols.static
+
+EXTRA_DIST += src/config.h.in src/test_endswap.tpl src/test_endswap.def \
+	$(SYMBOL_FILES) src/create_symbols_file.py src/binheader_writef_check.py \
+	src/GSM610/README src/GSM610/COPYRIGHT src/GSM610/ChangeLog \
+	src/G72x/README src/G72x/README.original src/G72x/ChangeLog \
+	src/make-static-lib-hidden-privates.sh \
+	src/config.h.cmake
+
+if USE_WIN_VERSION_FILE
+WIN_VERSION_FILE = src/version-metadata.rc
+else
+WIN_VERSION_FILE =
+endif
+
+#===============================================================================
+
+lib_LTLIBRARIES = src/libsndfile.la
+include_HEADERS = include/sndfile.h include/sndfile.hh
+src_libsndfile_la_CFLAGS = $(EXTERNAL_XIPH_CFLAGS) $(MPEG_CFLAGS)
+# MinGW requires -no-undefined if a DLL is to be built.
+src_libsndfile_la_LDFLAGS = -no-undefined -version-info $(SHARED_VERSION_INFO) $(SHLIB_VERSION_ARG)
+src_libsndfile_la_SOURCES = src/sndfile.c src/aiff.c src/au.c src/avr.c src/caf.c src/dwd.c src/flac.c src/g72x.c src/htk.c src/ircam.c \
+	src/macos.c src/mat4.c src/mat5.c src/nist.c src/paf.c src/pvf.c src/raw.c src/rx2.c src/sd2.c \
+	src/sds.c src/svx.c src/txw.c src/voc.c src/wve.c src/w64.c src/wavlike.c src/wav.c src/xi.c src/mpc2k.c src/rf64.c \
+	src/ogg.c src/ogg.h src/ogg_vorbis.c src/ogg_speex.c src/ogg_pcm.c src/ogg_opus.c src/ogg_vcomment.c src/ogg_vcomment.h \
+	src/common.h src/sfconfig.h src/sfendian.h src/wavlike.h src/sf_unistd.h src/chanmap.h src/mpeg.c
+nodist_src_libsndfile_la_SOURCES = $(nodist_include_HEADERS)
+src_libsndfile_la_LIBADD = src/GSM610/libgsm.la src/G72x/libg72x.la src/ALAC/libalac.la \
+	src/libcommon.la $(EXTERNAL_XIPH_LIBS) -lm $(MPEG_LIBS)
+EXTRA_src_libsndfile_la_DEPENDENCIES = $(SYMBOL_FILES)
+
+noinst_LTLIBRARIES = src/libcommon.la
+src_libcommon_la_CFLAGS = $(EXTERNAL_XIPH_CFLAGS) $(MPEG_CFLAGS)
+src_libcommon_la_SOURCES = src/common.c src/file_io.c src/command.c src/pcm.c src/ulaw.c src/alaw.c \
+	src/float32.c src/double64.c src/ima_adpcm.c src/ms_adpcm.c src/gsm610.c src/dwvw.c src/vox_adpcm.c \
+	src/interleave.c src/strings.c src/dither.c src/cart.c src/broadcast.c src/audio_detect.c \
+	src/ima_oki_adpcm.c src/ima_oki_adpcm.h src/alac.c src/chunk.c src/chanmap.c \
+	src/windows.c src/id3.c src/id3.h src/nms_adpcm.c src/mpeg_decode.c src/mpeg_l3_encode.c src/mpeg.h $(WIN_VERSION_FILE)
+
+check_PROGRAMS = src/test_main
+src_test_main_SOURCES = src/test_main.c src/test_main.h src/test_conversions.c src/test_float.c src/test_endswap.c \
+	src/test_audio_detect.c src/test_log_printf.c src/test_file_io.c src/test_ima_oki_adpcm.c \
+	src/test_strncpy_crlf.c src/test_broadcast_var.c src/test_cart_var.c \
+	src/test_binheader_writef.c src/test_nms_adpcm.c
+src_test_main_LDADD = src/libcommon.la
+
+##############
+# src/GSM610 #
+##############
+
+noinst_LTLIBRARIES += src/GSM610/libgsm.la
+src_GSM610_libgsm_la_SOURCES = src/GSM610/config.h src/GSM610/gsm.h src/GSM610/gsm610_priv.h \
+	src/GSM610/add.c src/GSM610/code.c src/GSM610/decode.c src/GSM610/gsm_create.c \
+	src/GSM610/gsm_decode.c src/GSM610/gsm_destroy.c src/GSM610/gsm_encode.c \
+	src/GSM610/gsm_option.c src/GSM610/long_term.c src/GSM610/lpc.c src/GSM610/preprocess.c \
+	src/GSM610/rpe.c src/GSM610/short_term.c src/GSM610/table.c
+
+############
+# src/G72x #
+############
+
+noinst_LTLIBRARIES += src/G72x/libg72x.la
+src_G72x_libg72x_la_SOURCES = src/G72x/g72x.h src/G72x/g72x_priv.h \
+	src/G72x/g721.c src/G72x/g723_16.c src/G72x/g723_24.c src/G72x/g723_40.c src/G72x/g72x.c
+
+check_PROGRAMS += src/G72x/g72x_test
+src_G72x_g72x_test_SOURCES = src/G72x/g72x_test.c
+src_G72x_g72x_test_LDADD = src/G72x/libg72x.la
+
+############
+# src/ALAC #
+############
+
+noinst_LTLIBRARIES += src/ALAC/libalac.la
+src_ALAC_libalac_la_SOURCES = src/ALAC/ALACAudioTypes.h src/ALAC/ALACBitUtilities.h \
+	src/ALAC/EndianPortable.h src/ALAC/aglib.h src/ALAC/dplib.h src/ALAC/matrixlib.h \
+	src/ALAC/alac_codec.h src/ALAC/shift.h \
+	src/ALAC/ALACBitUtilities.c src/ALAC/ag_dec.c \
+	src/ALAC/ag_enc.c src/ALAC/dp_dec.c src/ALAC/dp_enc.c src/ALAC/matrix_dec.c \
+	src/ALAC/matrix_enc.c src/ALAC/alac_decoder.c src/ALAC/alac_encoder.c
+
+#===============================================================================
+# Generate an OS specific Symbols files. This is done when the author
+# builds the distribution tarball. There should be not need for the
+# end user to create these files.
+
+# "$<" cannot portably be used in the recipe across Make implementations
+# https://www.gnu.org/software/autoconf/manual/autoconf.html#g_t_0024_003c-in-Ordinary-Make-Rules
+SYMBOL_SCRIPT = $(top_srcdir)/src/create_symbols_file.py
+
+src/Symbols.gnu-binutils: $(SYMBOL_SCRIPT)
+	$(PYTHON) $(SYMBOL_SCRIPT) linux $(ABI_VERSION) > $(top_srcdir)/$@
+
+src/Symbols.darwin: $(SYMBOL_SCRIPT)
+	$(PYTHON) $(SYMBOL_SCRIPT) darwin $(ABI_VERSION) > $(top_srcdir)/$@
+
+src/libsndfile-1.def: $(SYMBOL_SCRIPT)
+	$(PYTHON) $(SYMBOL_SCRIPT) win32 $(ABI_VERSION) > $(top_srcdir)/$@
+
+src/Symbols.os2: $(SYMBOL_SCRIPT)
+	$(PYTHON) $(SYMBOL_SCRIPT) os2 $(ABI_VERSION) > $(top_srcdir)/$@
+
+src/Symbols.static: $(SYMBOL_SCRIPT)
+	$(PYTHON) $(SYMBOL_SCRIPT) static $(ABI_VERSION) > $(top_srcdir)/$@
+
+#===============================================================================
+# Building windows resource files (if needed).
+
+.rc.lo:
+	$(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --tag=RC --mode=compile $(RC) $(RCFLAGS) $< -o $@
+
+#===============================================================================
+# Disable autoheader.
+AUTOHEADER=echo
+
+########
+# docs/ #
+########
+
+if FULL_SUITE
+dist_doc_DATA = docs/index.md docs/libsndfile.jpg docs/libsndfile.css docs/print.css docs/api.md \
+	docs/command.md docs/bugs.md docs/formats.md docs/sndfile_info.md docs/new_file_type_howto.md \
+	docs/win32.md docs/FAQ.md docs/lists.md docs/embedded_files.md docs/octave.md \
+	docs/tutorial.md
+endif
+
+#############
+# examples/ #
+#############
+
+check_PROGRAMS += examples/make_sine examples/sfprocess examples/list_formats examples/generate examples/sndfilehandle \
+	examples/sndfile-to-text examples/sndfile-loopify
+
+examples_sndfile_to_text_SOURCES = examples/sndfile-to-text.c
+examples_sndfile_to_text_LDADD = src/libsndfile.la
+
+examples_sndfile_loopify_SOURCES = examples/sndfile-loopify.c
+examples_sndfile_loopify_LDADD = src/libsndfile.la
+
+examples_make_sine_SOURCES = examples/make_sine.c
+examples_make_sine_LDADD = src/libsndfile.la
+
+examples_sfprocess_SOURCES = examples/sfprocess.c
+examples_sfprocess_LDADD = src/libsndfile.la
+
+examples_list_formats_SOURCES = examples/list_formats.c
+examples_list_formats_LDADD = src/libsndfile.la
+
+examples_generate_SOURCES = examples/generate.c
+examples_generate_LDADD = src/libsndfile.la
+
+examples_sndfilehandle_SOURCES = examples/sndfilehandle.cc
+examples_sndfilehandle_LDADD = src/libsndfile.la
+
+##########
+# tests/ #
+##########
+
+TESTS_ENVIRONMENT = $(SHELL) tests/test_wrapper.sh
+
+if ENABLE_TEST_COVERAGE
+CPP_TEST =
+else
+CPP_TEST = tests/cpp_test
+endif
+
+TESTS = tests/pedantic-header-test.sh
+
+check_PROGRAMS += tests/sfversion tests/floating_point_test tests/write_read_test \
+	tests/lossy_comp_test tests/error_test tests/ulaw_test tests/alaw_test tests/dwvw_test \
+	tests/peak_chunk_test tests/command_test tests/stdio_test \
+	tests/pcm_test tests/headerless_test tests/pipe_test tests/header_test tests/misc_test \
+	tests/raw_test tests/string_test tests/multi_file_test tests/chunk_test \
+	tests/scale_clip_test tests/win32_test tests/aiff_rw_test tests/virtual_io_test \
+	tests/locale_test tests/win32_ordinal_test tests/ogg_test tests/compression_size_test \
+	tests/checksum_test tests/external_libs_test tests/rdwr_test tests/format_check_test $(CPP_TEST) \
+	tests/channel_test tests/long_read_write_test tests/stdin_test tests/stdout_test \
+	tests/dither_test tests/fix_this tests/largefile_test tests/benchmark tests/ogg_opus_test \
+	tests/mpeg_test
+
+BUILT_SOURCES += \
+	tests/write_read_test.c \
+	tests/pcm_test.c \
+	tests/header_test.c \
+	tests/utils.c \
+	tests/scale_clip_test.c \
+	tests/pipe_test.c \
+	tests/rdwr_test.c \
+	tests/floating_point_test.c \
+	tests/benchmark.c
+
+EXTRA_DIST += $(BUILT_SOURCES) \
+	tests/write_read_test.tpl     tests/write_read_test.def \
+	tests/pcm_test.tpl            tests/pcm_test.def \
+	tests/header_test.tpl         tests/header_test.def \
+	tests/utils.tpl               tests/utils.def \
+	tests/scale_clip_test.tpl     tests/scale_clip_test.def \
+	tests/pipe_test.tpl           tests/pipe_test.def \
+	tests/rdwr_test.tpl           tests/rdwr_test.def \
+	tests/floating_point_test.tpl tests/floating_point_test.def \
+	tests/benchmark.tpl           tests/benchmark.def
+
+# If we're cross compiling from Linux to Windows and running the test suite
+# under Wine, we need a symbolic link to the generated libsndfile DLL.
+
+if LINUX_MINGW_CROSS_TEST
+
+$(check_PROGRAMS) : libsndfile-1.dll
+
+libsndfile-1.dll :
+	$(LN_S) src/.libs/$@ $@
+
+clean-local :
+	-rm -f libsndfile-1.dll
+
+endif
+
+#===============================================================================
+
+tests_sfversion_SOURCES = tests/sfversion.c
+tests_sfversion_LDADD = src/libsndfile.la
+
+tests_write_read_test_SOURCES = tests/utils.c tests/generate.c tests/generate.h tests/write_read_test.c tests/utils.h
+tests_write_read_test_LDADD = src/libsndfile.la
+
+tests_lossy_comp_test_SOURCES = tests/utils.c tests/lossy_comp_test.c tests/utils.h
+tests_lossy_comp_test_LDADD = src/libsndfile.la
+
+tests_fix_this_SOURCES = tests/utils.c tests/fix_this.c tests/utils.h
+tests_fix_this_LDADD = src/libsndfile.la
+
+tests_error_test_SOURCES = tests/error_test.c tests/utils.c tests/utils.h
+tests_error_test_LDADD = src/libsndfile.la
+
+tests_ulaw_test_SOURCES = tests/utils.c tests/ulaw_test.c tests/utils.h
+tests_ulaw_test_LDADD = src/libsndfile.la
+
+tests_alaw_test_SOURCES = tests/utils.c tests/alaw_test.c tests/utils.h
+tests_alaw_test_LDADD = src/libsndfile.la
+
+tests_aiff_rw_test_SOURCES = tests/utils.c tests/aiff_rw_test.c tests/utils.h
+tests_aiff_rw_test_LDADD = src/libsndfile.la
+
+tests_command_test_SOURCES = tests/command_test.c tests/utils.c tests/utils.h
+tests_command_test_LDADD = src/libsndfile.la
+
+tests_locale_test_SOURCES = tests/locale_test.c tests/utils.c tests/utils.h
+tests_locale_test_LDADD = src/libsndfile.la
+
+tests_largefile_test_SOURCES = tests/largefile_test.c tests/utils.c tests/utils.h
+tests_largefile_test_LDADD = src/libsndfile.la
+
+tests_pcm_test_SOURCES = tests/pcm_test.c tests/utils.c tests/utils.h
+tests_pcm_test_LDADD = src/libsndfile.la
+
+tests_headerless_test_SOURCES = tests/utils.c tests/headerless_test.c tests/utils.h
+tests_headerless_test_LDADD = src/libsndfile.la
+
+tests_stdin_test_SOURCES = tests/stdin_test.c tests/utils.c tests/utils.h
+tests_stdin_test_LDADD = src/libsndfile.la
+
+tests_stdout_test_SOURCES = tests/stdout_test.c tests/utils.h
+tests_stdout_test_LDADD = src/libsndfile.la
+
+tests_stdio_test_SOURCES = tests/stdio_test.c tests/utils.c tests/utils.h
+tests_stdio_test_LDADD = src/libsndfile.la
+
+tests_pipe_test_SOURCES = tests/pipe_test.c tests/utils.c tests/utils.h
+tests_pipe_test_LDADD = src/libsndfile.la
+
+tests_benchmark_SOURCES = tests/benchmark.c tests/utils.h
+tests_benchmark_LDADD = src/libsndfile.la
+
+tests_header_test_SOURCES = tests/header_test.c tests/utils.c tests/utils.h
+tests_header_test_LDADD = src/libsndfile.la
+
+tests_misc_test_SOURCES = tests/misc_test.c tests/utils.c tests/utils.h
+tests_misc_test_LDADD = src/libsndfile.la
+
+tests_raw_test_SOURCES = tests/raw_test.c tests/utils.c tests/utils.h
+tests_raw_test_LDADD = src/libsndfile.la
+
+tests_string_test_SOURCES = tests/string_test.c tests/utils.c tests/utils.h
+tests_string_test_LDADD = src/libsndfile.la
+
+tests_dither_test_SOURCES = tests/dither_test.c tests/utils.c tests/utils.h
+tests_dither_test_LDADD = src/libsndfile.la
+
+tests_chunk_test_SOURCES = tests/chunk_test.c tests/utils.c tests/utils.h
+tests_chunk_test_LDADD = src/libsndfile.la
+
+tests_multi_file_test_SOURCES = tests/multi_file_test.c tests/utils.c tests/utils.h
+tests_multi_file_test_LDADD = src/libsndfile.la
+
+tests_virtual_io_test_SOURCES = tests/virtual_io_test.c tests/utils.c tests/utils.h
+tests_virtual_io_test_LDADD = src/libsndfile.la
+
+tests_ogg_test_SOURCES = tests/ogg_test.c tests/utils.c tests/utils.h
+tests_ogg_test_LDADD = src/libsndfile.la
+
+tests_ogg_opus_test_SOURCES = tests/ogg_opus_test.c tests/utils.c tests/utils.h
+tests_ogg_opus_test_LDADD = src/libsndfile.la
+
+tests_compression_size_test_SOURCES = tests/compression_size_test.c tests/utils.c tests/utils.h tests/dft_cmp.h
+tests_compression_size_test_LDADD = src/libsndfile.la
+
+tests_rdwr_test_SOURCES = tests/rdwr_test.c tests/utils.c tests/utils.h
+tests_rdwr_test_LDADD = src/libsndfile.la
+
+tests_win32_test_SOURCES = tests/win32_test.c
+# Link lib here so that generating the testsuite tarball works correctly.
+tests_win32_test_LDADD = src/libsndfile.la
+
+tests_win32_ordinal_test_SOURCES = tests/win32_ordinal_test.c tests/utils.c tests/utils.h
+tests_win32_ordinal_test_LDADD = src/libsndfile.la
+
+tests_external_libs_test_SOURCES = tests/external_libs_test.c tests/utils.c tests/utils.h
+tests_external_libs_test_LDADD = src/libsndfile.la
+
+tests_format_check_test_SOURCES = tests/format_check_test.c tests/utils.c tests/utils.h
+tests_format_check_test_LDADD = src/libsndfile.la
+
+tests_channel_test_SOURCES = tests/channel_test.c tests/utils.c tests/utils.h
+tests_channel_test_LDADD = src/libsndfile.la
+
+tests_long_read_write_test_SOURCES = tests/long_read_write_test.c tests/utils.c tests/utils.h tests/dft_cmp.h
+tests_long_read_write_test_LDADD = src/libsndfile.la
+
+tests_cpp_test_SOURCES = tests/cpp_test.cc tests/utils.c tests/utils.h
+tests_cpp_test_LDADD = src/libsndfile.la
+
+tests_checksum_test_SOURCES = tests/checksum_test.c tests/utils.c tests/utils.h
+tests_checksum_test_LDADD = src/libsndfile.la
+
+tests_mpeg_test_SOURCES = tests/mpeg_test.c tests/utils.c tests/utils.h
+tests_mpeg_test_LDADD = src/libsndfile.la
+
+# Lite remove start
+tests_dwvw_test_SOURCES = tests/dwvw_test.c tests/utils.c tests/utils.h
+tests_dwvw_test_LDADD = src/libsndfile.la
+
+tests_floating_point_test_SOURCES = tests/utils.c tests/utils.h tests/dft_cmp.c tests/dft_cmp.h tests/floating_point_test.c
+tests_floating_point_test_LDADD = src/libsndfile.la
+
+tests_peak_chunk_test_SOURCES = tests/peak_chunk_test.c tests/utils.c tests/utils.h
+tests_peak_chunk_test_LDADD = src/libsndfile.la
+
+tests_scale_clip_test_SOURCES = tests/scale_clip_test.c tests/utils.c tests/utils.h
+tests_scale_clip_test_LDADD = src/libsndfile.la
+# Lite remove end
+
+#===============================================================================
+# Autogen generated sources.
+# Coerce the multiple inputs -> multiple outputs problem
+# into suffix rules by "linearising" the dependency graph.
+# Yes, this sucks, but GNU make patterns aren't portable,
+# see also https://github.com/libsndfile/libsndfile/issues/369
+
+SUFFIXES = .tpl .def
+
+.tpl.def:
+	touch $@
+
+# unconditionally running touch on the .def
+# files is necessary to keep all timestamps
+# consistent, in order to prevent stale files
+# from calling autogen in tarball releases.
+.def.c:
+	$(MAKE) $(AM_MAKEFLAGS) $<
+	cd $(top_srcdir)/$(@D) && autogen --writable $(<F)
+
+# recommended Automake way for multi-output targets:
+# https://www.gnu.org/software/automake/manual/html_node/Multiple-Outputs.html
+tests/utils.h : tests/utils.c
+	@if test -f $@; then :; else \
+	  cd $(top_srcdir) && \
+	  rm -f tests/utils.c && \
+	  $(MAKE) $(AM_MAKEFLAGS) tests/utils.c; \
+	fi
+
+########
+# man/ #
+########
+
+if FULL_SUITE
+dist_man_MANS = man/sndfile-info.1 man/sndfile-play.1 man/sndfile-convert.1 man/sndfile-cmp.1 \
+	man/sndfile-metadata-get.1 man/sndfile-metadata-set.1 man/sndfile-concat.1 \
+	man/sndfile-interleave.1 man/sndfile-deinterleave.1 man/sndfile-salvage.1
+
+# Same manpage for both programs.
+man/sndfile-metadata-set.1: man/sndfile-metadata-get.1
+	-rm -f $@
+	cd $(top_srcdir)/man && $(LN_S) sndfile-metadata-get.1 sndfile-metadata-set.1
+
+man/sndfile-deinterleave.1: man/sndfile-interleave.1
+	-rm -f $@
+	cd $(top_srcdir)/man && $(LN_S) sndfile-interleave.1 sndfile-deinterleave.1
+
+############
+# ossfuzz/ #
+############
+
+if USE_OSSFUZZ_FLAG
+FUZZ_FLAG = $(LIB_FUZZING_ENGINE)
+FUZZ_LDADD =
+else
+if USE_OSSFUZZ_STATIC
+FUZZ_LDADD = $(LIB_FUZZING_ENGINE)
+FUZZ_FLAG =
+else
+FUZZ_LDADD = libstandaloneengine.la
+FUZZ_FLAG =
+endif
+endif
+
+if USE_OSSFUZZERS
+noinst_PROGRAMS += \
+	ossfuzz/sndfile_fuzzer \
+	ossfuzz/sndfile_alt_fuzzer
+
+noinst_LTLIBRARIES += \
+	ossfuzz/libstandaloneengine.la
+endif
+
+ossfuzz_sndfile_fuzzer_SOURCES = ossfuzz/sndfile_fuzzer.cc
+ossfuzz_sndfile_fuzzer_CXXFLAGS = $(AM_CXXFLAGS) $(FUZZ_FLAG)
+ossfuzz_sndfile_fuzzer_LDFLAGS = $(AM_LDFLAGS) -static
+ossfuzz_sndfile_fuzzer_LDADD = src/libsndfile.la $(FUZZ_LDADD)
+
+ossfuzz_sndfile_alt_fuzzer_SOURCES = ossfuzz/sndfile_alt_fuzzer.cc
+ossfuzz_sndfile_alt_fuzzer_CXXFLAGS = $(AM_CXXFLAGS) $(FUZZ_FLAG)
+ossfuzz_sndfile_alt_fuzzer_LDFLAGS = $(AM_LDFLAGS) -static
+ossfuzz_sndfile_alt_fuzzer_LDADD = src/libsndfile.la $(FUZZ_LDADD)
+
+ossfuzz_libstandaloneengine_la_SOURCES = ossfuzz/standaloneengine.cc ossfuzz/testinput.h
+ossfuzz_libstandaloneengine_la_CXXFLAGS = $(AM_CXXFLAGS)
+
+#############
+# programs/ #
+#############
+
+bin_PROGRAMS = programs/sndfile-info programs/sndfile-play programs/sndfile-convert programs/sndfile-cmp \
+	programs/sndfile-metadata-set programs/sndfile-metadata-get programs/sndfile-interleave \
+	programs/sndfile-deinterleave programs/sndfile-concat programs/sndfile-salvage
+endif
+
+# required by test-sndfile-metadata-set.py
+check_PROGRAMS += programs/sndfile-metadata-set programs/sndfile-metadata-get
+
+EXTRA_DIST += programs/test-sndfile-metadata-set.py
+
+programs_sndfile_info_SOURCES = programs/sndfile-info.c programs/common.c programs/common.h
+programs_sndfile_info_LDADD = src/libsndfile.la
+
+programs_sndfile_play_SOURCES = programs/sndfile-play.c programs/common.c programs/common.h
+programs_sndfile_play_LDADD = src/libsndfile.la $(OS_SPECIFIC_LINKS) $(ALSA_LIBS) $(SNDIO_LIBS)
+
+programs_sndfile_convert_SOURCES = programs/sndfile-convert.c programs/common.c programs/common.h
+programs_sndfile_convert_LDADD = src/libsndfile.la
+
+programs_sndfile_cmp_SOURCES = programs/sndfile-cmp.c programs/common.c programs/common.h
+programs_sndfile_cmp_LDADD = src/libsndfile.la
+
+programs_sndfile_metadata_set_SOURCES = programs/sndfile-metadata-set.c programs/common.c programs/common.h
+programs_sndfile_metadata_set_LDADD = src/libsndfile.la
+
+programs_sndfile_metadata_get_SOURCES = programs/sndfile-metadata-get.c programs/common.c programs/common.h
+programs_sndfile_metadata_get_LDADD = src/libsndfile.la
+
+programs_sndfile_interleave_SOURCES = programs/sndfile-interleave.c programs/common.c programs/common.h
+programs_sndfile_interleave_LDADD = src/libsndfile.la
+
+programs_sndfile_deinterleave_SOURCES = programs/sndfile-deinterleave.c programs/common.c programs/common.h
+programs_sndfile_deinterleave_LDADD = src/libsndfile.la
+
+programs_sndfile_concat_SOURCES = programs/sndfile-concat.c programs/common.c programs/common.h
+programs_sndfile_concat_LDADD = src/libsndfile.la
+
+programs_sndfile_salvage_SOURCES = programs/sndfile-salvage.c programs/common.c programs/common.h
+programs_sndfile_salvage_LDADD = src/libsndfile.la
+
+############
+# regtest/ #
+############
+
+if HAVE_SQLITE3
+check_PROGRAMS += regtest/sndfile-regtest
+endif
+
+regtest_sndfile_regtest_SOURCES = regtest/regtest.h regtest/sndfile-regtest.c regtest/database.c regtest/checksum.c
+regtest_sndfile_CPPFLAGS = -I$(top_srcdir)/src $(SQLITE3_CFLAGS) $(OS_SPECIFIC_CFLAGS)
+regtest_sndfile_regtest_LDADD = src/libsndfile.la $(SQLITE3_LIBS)
+
+################
+# test-tarball #
+################
+test-tarball : Scripts/build-test-tarball.mk
+	$(MAKE) $(bin_PROGRAMS) $(check_PROGRAMS)
+	$(MAKE) -f Scripts/build-test-tarball.mk
diff --git a/NEWS.OLD b/NEWS.OLD
new file mode 100644
index 0000000..2169e8f
--- /dev/null
+++ b/NEWS.OLD
@@ -0,0 +1,303 @@
+Version 1.0.31 (2021-01-24)
+
+  * The releaser of libsndfile starting from this version is the libsndfile team
+    member @SoapGentoo, see AUTHORS for details.
+  * Implement fast SSE2 optimized psf_lrintf() and psf_lrintf() functions to
+    improve perfomance when libsndfile is built using Visual C++ (especially)
+    and other compilers on x86 and AMD64 platforms. See also description of
+    ENABLE_SSE2 CMake option in README.md.
+  * Documentation:
+    * Move site to new URL: http://libsndfile.github.io/libsndfile/
+    * Convert documentation pages from HTML to Markdown
+    * Use GitHub's Jekyll static site generator to generate static HTML pages
+      for site
+    * Fix api.md table error, thanks to @zodf0055980
+    * Other docuemntation fixes and updates
+  * Change CMake's project name from sndfile to libsndfile as it should be.
+  * Change behaviour of ENABLE_STATIC_RUNTIME option. In short:
+    * You can use ENABLE_STATIC_RUNTIME for CMake >= 3.15 without error.
+    * You can use your our method to set MSVC runtime library flags if none of
+      ENABLE_STATIC_RUNTIME and CMAKE_MSVC_RUNTIME_LIBRARY were set.
+    Advanced information:
+    * If this option is defined (set to ON or OFF), set CMP0091 policy to OLD
+      (we handle MSVC runtime library flags using compiler flags), set
+      corresponding compiler flags for user.
+    * NEW: If this option is not defined, set CMP0091 policy to OLD (we handle
+      MSVC runtime library flags using compiler flags), don't touch compiler
+      options, allow user to set it manually.
+    * NEW: If new CMake option CMAKE_MSVC_RUNTIME_LIBRARY is set, change
+      CMP0091 policy to NEW (we handle MSVC runtime library flags using that
+      option), don't touch compiler flags.
+    * NEW: If both ENABLE_STATIC_RUNTIME and CMAKE_MSVC_RUNTIME_LIBRARY are set,
+      terminate configuration with fatal error.
+    * For MinGW toolchain this option is experimental. If you enabled it and
+      then disabled again, you need to clear CMake cache (delete
+      CMakeCache.txt).
+  * Make CMake clip test faster.
+  * Fix CMake bug with sndio library dependency, thanks to @drhenault.
+  * Fix memory leak in wav_read_smpl_chunk() function, credit to OSS-Fuzz.
+  * Fix aiff_read_header() memory leak(), credit to OSS-Fuzz.
+  * Fix leak in wav_read_header(), credit to OSS-Fuzz.
+  * Fix leak in wavlike_read_cart_chunk(), credit to OSS-Fuzz.
+  * Fix memory leak in wav_read_acid_chunk(), credit to OSS-Fuzz.
+  * Fix memory leak in aiff_read_basc_chunk(), credit to OSS-Fuzz.
+  * Fix memory leak in wavlike_read_peak_chunk(), credit to OSS-Fuzz.
+  * Fix memory leak in aiff_read_header(), credit to OSS-Fuzz.
+  * Fix use of uninitialized value in exif_subchunk_parse(), credit to OSS-Fuzz.
+  * Fix use of uninitialized value in endswap_int64_t_array(), credit to
+    OSS-Fuzz.
+  * Fix up the fuzzer so that it can't under or overseek,
+    thanks to Max Dymond <cmeister2@gmail.com>.
+  * Fix Autotools configure on macOS, thanks to @tmcguire and @nwh.
+  * Exclude repository-configuration from git-archive, thanks to @umlaeute.
+  * Use version-script when compiling with clang on Unix with Autotools, thanks
+    to @tstellar.
+  * Improve handling of SMPL chunks in WAV files, thanks to @zodf0055980.
+
+Version 1.0.30 (2020-09-18)
+
+  * Fix critical CMake bug with broken ABI of shared libsndfile library.
+  * CMake build system considered to be stable.
+  * Move sndfile.h.in from src/ to include/ directory. To avoid problems,
+    delete old generated sndfile.h from $(top_builddir)/src.
+  * Huge documentation update.
+  * Fix opus test failures on BE platforms, thanks to
+    Arthur Taylor <art@ified.ca>.
+  * Fix bug when sf_open_fd() function sometimes leaves filehandle open, even
+    if `close_desc` parameter is TRUE, thanks to @umläute.
+  * Fix infinite loops on some pathological SD2 files, thanks to
+    Jeremy Friesner <jaf@meyersound.com>.
+  * Switch to GitHub Actions for continuous integration.
+  * Add OSS-Fuzz tests to GitHub Actions workflow, thanks to
+    Max Dymond <cmeister2@gmail.com>.
+  * Fix memory leak in wavlike_read_bext_chunk() function, credit to OSS-Fuzz.
+  * Fix undefined behavior in avr-read_header() function, credit to OSS-Fuzz.
+  * Add INSTALL_PKGCONFIG_MODULE CMake option to control sndfile.pc file
+    installation, see README.md for details.
+  * Add INSTALL_MANPAGES CMake option, see README.md for details.
+  * Fix ENABLE_COMPATIBLE_LIBSNDFILE_NAME CMake option, now it works on MinGW
+    platform too.
+  * Fix ENABLE_CPACK CMake option, see README.md for details.
+  * Fix ENABLE_STATIC_RUNTIME and CMAKE_MSVC_RUNTIME_LIBRARY behavior, see
+    README.md for details.
+  * Fix CMake man pages installation bug when sndfile-deinterleave.1 and
+    sndfile-metadata-set.1 were not installed.
+  * Fix sndfile-regtest paths handling on Windows platform, thanks to
+    Gisle Vanem <gvanem@yahoo.no>.
+
+Version 1.0.29 (2020-08-15)
+  * Add support for Opus files.
+  * Autotool build system improvements.
+  * CMake build system improvements.
+  * Fixes for: CVE-2017-12562, CVE-2017-17456, CVE-2017-17457, CVE-2018-19661,
+    CVE-2018-19662, CVE-2018-19758 and CVE-2019-3832.
+  * Add BWF v2 loudness parameters.
+  * Wave64: Permit and skip arbitrary chunks prior to the data chunk.
+  * Fix ASAN crash in wavlike_ima_seek().
+  * Fix IMA-ADPCM encoding for AIFF files.
+  * sndfile-convert: Handle gsm, vox and opus extensions the same way.
+  * Add SFC_SET_OGG_PAGE_LATENCY_MS command to get Ogg page latency for Ogg Opus
+    files.
+  * Fix parsing of some SD2 files.
+  * Documentation updates.
+  * Minor bug fixes and improvements.
+
+Version 1.0.28 (2017-04-02)
+  * Fix buffer overruns in FLAC and ID3 handling code.
+  * Move to variable length header storage.
+  * Fix detection of Large File Support for 32 bit systems.
+  * Remove large stack allocations in ALAC handling code.
+  * Remove all use of Variable Length Arrays.
+  * Minor bug fixes and improvements.
+
+Version 1.0.27 (2016-06-19)
+  * Fix an SF_INFO seekable flag regression introduced in 1.0.26.
+  * Fix potential infinite loops on malformed input files.
+  * Add string metadata read/write for CAF and RF64.
+  * Add handling of CUE chunks.
+  * Fix endian-ness issues in PAF files.
+  * Minor bug fixes and improvements.
+
+Version 1.0.26 (2015-11-22)
+  * Fix for CVE-2014-9496, SD2 buffer read overflow.
+  * Fix for CVE-2014-9756, file_io.c divide by zero.
+  * Fix for CVE-2015-7805, AIFF heap write overflow.
+  * Add support for ALAC encoder in a CAF container.
+  * Add support for Cart chunks in WAV files.
+  * Minor bug fixes and improvements.
+
+Version 1.0.25 (2011-07-13)
+  * Fix for Secunia Advisory SA45125, heap overflow in PAF file handler.
+  * Accept broken WAV files with blockalign == 0.
+  * Minor bug fixes and improvements.
+
+Version 1.0.24 (2011-03-23)
+  * WAV files now have an 18 byte u-law and A-law fmt chunk.
+  * Document virtual I/O functionality.
+  * Two new methods rawHandle() and takeOwnership() in sndfile.hh.
+  * AIFF fix for non-zero offset value in SSND chunk.
+  * Minor bug fixes and improvements.
+
+Version 1.0.23 (2010-10-10)
+  * Add version metadata to Windows DLL.
+  * Add a missing 'inline' to sndfile.hh.
+  * Update docs.
+  * Minor bug fixes and improvements.
+
+Version 1.0.22 (2010-10-04)
+  * Couple of fixes for SDS file writer.
+  * Fixes arising from static analysis.
+  * Handle FLAC files with ID3 meta data at start of file.
+  * Handle FLAC files which report zero length.
+  * Other minor bug fixes and improvements.
+
+Version 1.0.21 (2009-12-13)
+  * Add a couple of new binary programs to programs/ dir.
+  * Remove sndfile-jackplay (now in sndfile-tools package).
+  * Add windows only function sf_wchar_open().
+  * Bunch of minor bug fixes.
+
+Version 1.0.20 (2009-05-14)
+  * Fix potential heap overflow in VOC file parser (Tobias Klein, http://www.trapkit.de/).
+
+Version 1.0.19 (2009-03-02)
+  * Fix for CVE-2009-0186 (Alin Rad Pop, Secunia Research).
+  * Huge number of minor bug fixes as a result of static analysis.
+
+Version 1.0.18 (2009-02-07)
+  * Add Ogg/Vorbis support (thanks to John ffitch).
+  * Remove captive FLAC library.
+  * Many new features and bug fixes.
+  * Generate Win32 and Win64 pre-compiled binaries.
+
+Version 1.0.17 (2006-08-31)
+  * Add sndfile.hh C++ wrapper.
+  * Update Win32 MinGW build instructions.
+  * Minor bug fixes and cleanups.
+
+Version 1.0.16 (2006-04-30)
+  * Add support for Broadcast (BEXT) chunks in WAV files.
+  * Implement new commands SFC_GET_SIGNAL_MAX and SFC_GET_MAX_ALL_CHANNELS.
+  * Add support for RIFX (big endian WAV variant).
+  * Fix configure script bugs.
+  * Fix bug in INST and MARK chunk writing for AIFF files.
+
+Version 1.0.15 (2006-03-16)
+  * Fix some ia64 issues.
+  * Fix precompiled DLL.
+  * Minor bug fixes.
+
+Version 1.0.14 (2006-02-19)
+  * Really fix MinGW compile problems.
+  * Minor bug fixes.
+
+Version 1.0.13 (2006-01-21)
+  * Fix for MinGW compiler problems.
+  * Allow readin/write of instrument chunks from WAV and AIFF files.
+  * Compile problem fix for Solaris compiler.
+  * Minor cleanups and bug fixes.
+
+Version 1.0.12 (2005-09-30)
+  * Add support for FLAC and Apple's Core Audio Format (CAF).
+  * Add virtual I/O interface (still needs docs).
+  * Cygwin and other Win32 fixes.
+  * Minor bug fixes and cleanups.
+
+Version 1.0.11 (2004-11-15)
+  * Add support for SD2 files.
+  * Add read support for loop info in WAV and AIFF files.
+  * Add more tests.
+  * Improve type safety.
+  * Minor optimisations and bug fixes.
+
+Version 1.0.10 (2004-06-15)
+  * Fix AIFF read/write mode bugs.
+  * Add support for compiling Win32 DLLS using MinGW.
+  * Fix problems resulting in failed compiles with gcc-2.95.
+  * Improve test suite.
+  * Minor bug fixes.
+
+Version 1.0.9 (2004-03-30)
+  * Add handling of AVR (Audio Visual Research) files.
+  * Improve handling of WAVEFORMATEXTENSIBLE WAV files.
+  * Fix for using pipes on Win32.
+
+Version 1.0.8 (2004-03-14)
+  * Correct peak chunk handing for files with > 16 tracks.
+  * Fix for WAV files with huge number of CUE chunks.
+
+Version 1.0.7 (2004-02-25)
+  * Fix clip mode detection on ia64, MIPS and other CPUs.
+  * Fix two MacOSX build problems.
+
+Version 1.0.6 (2004-02-08)
+  * Added support for native Win32 file access API (Ross Bencina).
+  * New mode to add clippling then a converting from float/double to integer
+    would otherwise wrap around.
+  * Fixed a bug in reading/writing files > 2Gig on Linux, Solaris and others.
+  * Many minor bug fixes.
+  * Other random fixes for Win32.
+
+Version 1.0.5 (2003-05-03)
+  * Added support for HTK files.
+  * Added new function sf_open_fd() to allow for secure opening of temporary
+    files as well as reading/writing sound files embedded within larger
+    container files.
+  * Added string support for AIFF files.
+  * Minor bug fixes and code cleanups.
+
+Version 1.0.4 (2003-02-02)
+  * Added suport of PVF and XI files.
+  * Added functionality for setting and retreiving strings from sound files.
+  * Minor code cleanups and bug fixes.
+
+Version 1.0.3 (2002-12-09)
+  * Minor bug fixes.
+
+Version 1.0.2 (2002-11-24)
+  * Added support for VOX ADPCM.
+  * Improved error reporting.
+  * Added version scripting on Linux and Solaris.
+  * Minor bug fixes.
+
+Version 1.0.1 (2002-09-14)
+  * Added MAT and MAT5 file formats.
+  * Minor bug fixes.
+
+Version 1.0.0 (2002-08-16)
+  * Final release for 1.0.0.
+
+Version 1.0.0rc6 (2002-08-14)
+  * Release candidate 6 for the 1.0.0 series.
+  * MacOS9 fixes.
+
+Version 1.0.0rc5 (2002-08-10)
+  * Release candidate 5 for the 1.0.0 series.
+  * Changed the definition of sf_count_t which was causing problems when
+    libsndfile was compiled with other libraries (ie WxWindows).
+  * Minor bug fixes.
+  * Documentation cleanup.
+
+Version 1.0.0rc4 (2002-08-03)
+  * Release candidate 4 for the 1.0.0 series.
+  * Minor bug fixes.
+  * Fix broken Win32 "make check".
+
+Version 1.0.0rc3 (2002-08-02)
+  * Release candidate 3 for the 1.0.0 series.
+  * Fix bug where libsndfile was reading beyond the end of the data chunk.
+  * Added on-the-fly header updates on write.
+  * Fix a couple of documentation issues.
+
+Version 1.0.0rc2 (2002-06-24)
+  * Release candidate 2 for the 1.0.0 series.
+  * Fix compile problem for Win32.
+
+Version 1.0.0rc1 (2002-06-24)
+  * Release candidate 1 for the 1.0.0 series.
+
+Version 0.0.28 (2002-04-27)
+  * Last official release of 0.0.X series of the library.
+
+Version 0.0.8 (1999-02-16)
+  * First official release.
diff --git a/README b/README
new file mode 100644
index 0000000..cb580f1
--- /dev/null
+++ b/README
@@ -0,0 +1,74 @@
+libsndfile is a library of C routines for reading and writing
+files containing sampled audio data.
+
+The src/ directory contains the source code for library itself.
+
+The doc/ directory contains the libsndfile documentation.
+
+The examples/ directory contains examples of how to write code using
+libsndfile.
+
+The tests/ directory contains programs which link against libsndfile
+and test its functionality.
+
+The src/GSM610 directory contains code written by Jutta Degener and Carsten
+Bormann. Their original code can be found at :
+    http://kbs.cs.tu-berlin.de/~jutta/toast.html
+
+The src/G72x directory contains code written and released by Sun Microsystems
+under a suitably free license.
+
+The src/ALAC directory contains code written and released by Apple Inc and
+released under the Apache license.
+
+
+LINUX
+-----
+Wherever possible, you should use the packages supplied by your Linux
+distribution.
+
+If you really do need to compile from source it should be as easy as:
+
+		./configure
+		make
+		make install
+
+Since libsndfile optionally links against libFLAC, libogg, libvorbis and
+libopus, you will need to install appropriate versions of these libraries
+before running configure as above.
+
+You can use CMake now to build under Linux, also under Windows and MacOS, see
+README.md for details. CMake toolchain is usable, but still exterimental.
+
+
+UNIX
+----
+Compile as for Linux.
+
+
+Win32/Win64
+-----------
+
+Official Windows binaries are compiled with Autotools and MinGW, but you can use
+CMake to generate solution for Visual Studio. Details are described in
+README.md.
+
+It is still recommended to use the libsndfile binaries available on the
+libsndfile web site.
+
+MacOSX
+------
+Building on MacOSX should be the same as building it on any other Unix.
+
+
+CONTACTS
+--------
+
+libsndfile was written by Erik de Castro Lopo (erikd AT mega-nerd DOT com).
+The libsndfile home page is at :
+
+	http://libsndfile.github.io/libsndfile/
+
+Bugs and support questions can be raised at :
+
+	https://github.com/libsndfile/libsndfile/issues
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..f14afcd
--- /dev/null
+++ b/README.md
@@ -0,0 +1,315 @@
+# libsndfile
+
+![C/C++ CI](https://github.com/libsndfile/libsndfile/workflows/C/C++%20CI/badge.svg)
+
+libsndfile is a C library for reading and writing files containing sampled audio
+data.
+
+## Authors
+
+The libsndfile project was originally developed and maintained by
+Erik de Castro Lopo <erikd@mega-nerd.com> aka @erikd. The project was developed
+on Github at <https://github.com/erikd/libsndfile>.
+
+After the release of version 1.0.30, @erikd transferred the project to
+[the libsndfile team](https://github.com/libsndfile), see [AUTHORS](AUTHORS)
+for details.
+
+## Hacking
+
+The canonical source code repository for libsndfile is at
+<https://github.com/libsndfile/libsndfile>.
+
+You can grab the source code using:
+
+    git clone https://github.com/libsndfile/libsndfile.git
+
+For building for Android see [BuildingForAndroid][BuildingForAndroid].
+
+There are currently two build systems: the traditional GNU autotool based one and
+modern CMake based build system. Use of the CMake build system is documented
+below.
+
+Setting up a build environment for libsndfile on Debian or Ubuntu is as simple as:
+
+    sudo apt install autoconf autogen automake build-essential libasound2-dev \
+      libflac-dev libogg-dev libtool libvorbis-dev libopus-dev libmp3lame-dev \
+      libmpg123-dev pkg-config python
+
+For other Linux distributions or any of the *BSDs, the setup should be similar
+although the package install tools and package names may be slightly different.
+
+Similarly on Mac OS X, assuming [brew] is already installed:
+
+    brew install autoconf autogen automake flac libogg libtool libvorbis opus mpg123 pkg-config
+
+Once the build environment has been set up, building and testing libsndfile is
+as simple as:
+
+    autoreconf -vif
+    ./configure --enable-werror
+    make
+    make check
+
+## The CMake build system
+
+Although Autotools is the primary and recommended build toolchain, CMake meta
+build generator is also available. The build process with CMake takes
+place in two stages. First, standard build files are created from configuration
+scripts. Then the platform's native build tools are used for the actual
+building. CMake can produce Microsoft Visual Studio project and solution files,
+Unix Makefiles, Xcode projects and [many more](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html).
+
+Some IDE support CMake natively or with plugins, check you IDE documentation
+ for details.
+
+### Requirements
+
+1. C99-compliant compiler toolchain (tested with GCC, Clang and Visual
+   Studio 2015)
+2. CMake 3.1.3 or newer
+
+There are some recommended packages to enable all features of libsndfile:
+
+1. Ogg, Vorbis and FLAC libraries and headers to enable these formats support
+2. ALSA development package under Linux to build sndfile-play utility
+3. Sndio development package under BSD to build sndfile-play utility
+
+### Building from command line
+
+CMake can handle out-of-place builds, enabling several builds from
+the same source tree, and cross-compilation. The ability to build a directory
+tree outside the source tree is a key feature, ensuring that if a build
+directory is removed, the source files remain unaffected.
+
+    mkdir CMakeBuild
+    cd CMakeBuild
+
+Then run `cmake` command with directory where CMakeLists.txt script is located
+as argument (relative paths are supported):
+
+    cmake ..
+
+This command will configure and write build script or solution to CMakeBuild
+directory. CMake is smart enough to create Unix makefiles under Linux or Visual
+Studio solution if you have Visual Studio installed, but you can configure
+[generator](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html)
+with `-G` command line parameter:
+
+    cmake .. -G"Unix Makefiles"
+
+The build procedure depends on the selected generator. With "Unix Makefiles" you
+can type:
+
+    make & make install
+
+With "Visual Studio" and some other generators you can open solution or project
+from `CMakeBuild` directory and build using IDE.
+
+Finally, you can use unified command:
+
+    cmake --build .
+
+CMake also provides Qt-based cross platform GUI, cmake-gui. Using it is trivial
+and does not require detailed explanations.
+
+### Configuring CMake
+
+You can pass additional options with `/D<parameter>=<value>` when you run
+`cmake` command. Some useful system options:
+
+* `CMAKE_C_FLAGS` - additional C compiler flags
+* `CMAKE_BUILD_TYPE` - configuration type, `DEBUG`, `RELEASE`, `RELWITHDEBINFO`
+  or `MINSIZEREL`. `DEBUG` is default
+* `CMAKE_INSTALL_PREFIX` - build install location, the same as `--prefix` option
+  of `configure` script
+
+ Useful libsndfile options:
+
+* `BUILD_SHARED_LIBS` - build shared library (DLL under Windows) when `ON`,
+  build static library othervise. This option is `OFF` by default.
+* `BUILD_PROGRAMS` - build libsndfile's utilities from `programs/` directory,
+  `ON` by default.
+* `BUILD_EXAMPLES` - build examples, `ON` by default.
+* `BUILD_TESTING` - build tests. Then you can run tests with `ctest` command,
+  `ON` by default. Setting `BUILD_SHARED_LIBS` to `ON` disables this option.
+* `ENABLE_EXTERNAL_LIBS` - enable Ogg, Vorbis, FLAC and Opus support. This
+  option is available and set to `ON` if all dependency libraries were found.
+* `ENABLE_MPEG` - MP3 support. This option is available and set to `ON` if all
+  dependency libraries were found.
+* `ENABLE_CPU_CLIP` - enable tricky cpu specific clipper. Enabled and set to
+  `ON` when CPU clips negative\positive. Don't touch it if you are not sure
+* `ENABLE_BOW_DOCS` - enable black-on-white documentation theme, `OFF` by
+  default.
+* `ENABLE_EXPERIMENTAL` - enable experimental code. Don't use it if you are
+  not sure. This option is `OFF` by default.
+* `ENABLE_CPACK` - enable [CPack](https://cmake.org/cmake/help/latest/module/CPack.html) support.
+  This option is `ON` by default.
+* `ENABLE_PACKAGE_CONFIG` - generate and install [package config file](https://cmake.org/cmake/help/latest/manual/cmake-packages.7.html#config-file-packages).
+* `INSTALL_PKGCONFIG_MODULE` - generate and install [pkg-config module](https://people.freedesktop.org/~dbn/pkg-config-guide.html).
+* `INSTALL_MANPAGES` - install [man pages](https://en.wikipedia.org/wiki/Man_page) for programs. This option is `ON` by  default
+* `ENABLE_STATIC_RUNTIME` - enable static runtime on Windows platform (MSVC and
+  MinGW), `OFF` by default.
+
+  **Note**: For MSVC compiler this option is deprecated for CMake >= 3.15, see
+  policy [CMP0091](https://cmake.org/cmake/help/latest/policy/CMP0091.html).
+  Use `CMAKE_MSVC_RUNTIME_LIBRARY` option instead.
+
+  **Note**: For MinGW toolchain this option is experimental. If you enabled it
+  and then disabled again, you need to clear CMake cache (delete CMakeCache.txt).
+* `ENABLE_COMPATIBLE_LIBSNDFILE_NAME` - set DLL name to `libsndfile-1.dll`
+  (canonical name) on Windows platform, `sndfile.dll` otherwise, `OFF` by
+  default. Library name can be different depending on platform. The well known
+  DLL name on Windows platform is `libsndfile-1.dll`, because the only way to
+  build Windows library before was MinGW toolchain with Autotools. This name
+  is native for MinGW ecosystem, Autotools constructs it using MinGW platform
+  rules from `sndfile` target. But when you build with CMake using native
+  Windows compiler, the name is `sndfile.dll`. This is name for native Windows
+  platform, because Windows has no library naming rules. It is preffered
+  because you can search library using package manager or CMake's
+  `find_library` command on any platform using the same `sndfile` name.
+
+* `ENABLE_SSE2` - add compiler flag to enable SSE2 if required, `ON` by default.
+
+  This option is for X86 and GCC compatible compilers configurations only.
+
+  If you compile for other SIMD set, e.g. AVX2, you may want to set
+  `ENABLE_SSE2` to `OFF`.
+
+  **Note**: This option is not active for X64 configuration, because SSE2 is
+  always available in this mode and all optimizations are enabled by default.
+
+Deprecated options:
+
+* `DISABLE_EXTERNAL_LIBS` - disable Ogg, Vorbis and FLAC support. Replaced by
+  `ENABLE_EXTERNAL_LIBS`
+* `DISABLE_CPU_CLIP` - disable tricky cpu specific clipper. Replaced by
+  `ENABLE_CPU_CLIP`
+* `BUILD_STATIC_LIBS` - build static library. Use `BUILD_SHARED_LIBS` instead
+
+### Linking from CMake projects
+
+First you need to add `FindOgg.cmake`, `FindVorbis.cmake`, `FindFLAC.cmake` and
+`FindOpus.cmake` files to some directory inside your CMake project (usually
+`cmake`) and add it to `CMAKE_MODULE_PATH`:
+
+    project(SomeApplication)
+    
+    list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
+
+Now you can search `libsndfile` library from your `CMakeLists.txt`
+ with this command:
+
+    find_package(SndFile)
+
+`SndFile_FOUND` is set to `ON` when library is found.
+
+If `libsndfile` dependency is critical, you can add `REQUIRED` to
+ `find_package`:
+
+    find_package(SndFile REQUIRED)
+
+With with option `find_package` will terminate configuration process
+ if `libsndfile` is not found.
+
+You can also add version check:
+
+    find_package(SndFile 1.0.29)
+
+`find_package` will report error, if `libsndfile` version is < 1.0.29.
+
+You can combine `REQUIRED` and version if you need.
+
+To link `libsndfile` library use:
+
+    target_link_libraries(my_application PRIVATE SndFile::sndfile)
+
+### Notes for Windows users
+
+#### System CRT library
+
+First advice about Visual Studio [system CRT libraries](https://docs.microsoft.com/en-us/cpp/c-runtime-library/c-run-time-library-reference?view=vs-2019),
+it is system code linked as static or dynamic library to every C application.
+
+You can find related option in Visual Studio project properties:
+
+    C/C++ -> Code Generation -> Runtime Library
+
+Dynamic version of system CRT library is default and it means that end user needs
+to have the same runtime library installed on his system. Most likely it is so,
+but if it is not, the user will see this error message using libsndfile DLL:
+
+    "The program can't start because <crt-dll-name>.dll is missing from your computer. Try reinstalling the program to fix this problem. "
+
+To avoid this, you may want to enable static CRT library linking. In this case
+the size of your DLL will increase slightly the size will increase slightly, but
+you can redistribute the libsndfile DLL without having to install the correct
+version of the system CRT library.
+
+CMake project will use dynamic system CRT libraries by default, just like
+Visual Studio does. But you can change it using `ENABLE_STATIC_RUNTIME` or
+`CMAKE_MSVC_RUNTIME_LIBRARY` options.
+
+**Note**: You cannot use both options at the same time, it will lead to a
+configuration error.
+
+If you have CMake >= 3.15 you should use
+[`CMAKE_MSVC_RUNTIME_LIBRARY`](https://cmake.org/cmake/help/v3.15/variable/CMAKE_MSVC_RUNTIME_LIBRARY.html) option.
+
+This will enable static linking:
+
+    cmake .. -D"MultiThreaded$<$<CONFIG:Debug>:Debug>"
+
+You can use libsndfile `ENABLE_STATIC_RUNTIME` option to to control CRT library
+linking for CMake project: `OFF` or unset (default) for dynamic, and `ON` for
+static linking:
+
+    cmake .. -DENABLE_STATIC_RUNTIME=ON
+
+**Note**: This option is deprecated and may be removed in far future because we
+have standard option `CMAKE_MSVC_RUNTIME_LIBRARY` now.
+
+#### Using Vcpkg package manager
+
+Second advice is about Ogg, Vorbis FLAC and Opus support. Searching external
+libraries under Windows is a little bit tricky. The best way is to use
+[Vcpkg](https://github.com/Microsoft/vcpkg).
+
+Install Vcpkg and then add this parameter to cmake command line:
+
+    -DCMAKE_TOOLCHAIN_FILE=<path-to-vcpkg>/scripts/buildsystems/vcpkg.cmake
+
+You also need to set `VCPKG_TARGET_TRIPLET` if you want to use static libraries:
+
+    -DVCPKG_TARGET_TRIPLET=x64-windows-static
+
+Then you need to install static libogg, libvorbis, libflac, libopus, mpg123 and
+mp3lame Vcpkg packages.
+
+After 1.1.0beta2 you don't need to install dependencies manually. Libsndfile
+now supports [Vcpkg manifest mode](https://vcpkg.readthedocs.io/en/latest/users/manifests/)
+and all dependencies are installed automatically.
+
+However, you can turn off the manifest mode and return to the classic mode using
+the `VCPKG_MANIFEST_MODE` parameter from the command line:
+
+    -DVCPKG_MANIFEST_MODE=OFF
+
+In classic mode, you need to install the required libraries manually:
+
+    vcpkg install libvorbis:x64-windows-static libflac:x64-windows-static
+    opus:x64-windows-static mp3lame:x86-windows-static mpg123:x86-windows-static
+    libvorbis:x86-windows-static libflac:x86-windows-static
+    opus:x86-windows-static mp3lame:x86-windows-static mpg123:x86-windows-static
+
+**Note**: Use must use the same CRT library for external libraries and the
+libsndfile library itself. For `*-static` triplets Vcpkg uses
+[static CRT](https://vcpkg.readthedocs.io/en/latest/users/triplets/).
+
+## Submitting Patches
+
+See [CONTRIBUTING.md](CONTRIBUTING.md) for details.
+
+[brew]: http://brew.sh/
+[github]: http://libsndfile.github.io/libsndfile/
+[BuildingForAndroid]: https://github.com/libsndfile/libsndfile/blob/master/Building-for-Android.md
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..b75e7be
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,11 @@
+# Security Policy
+
+## Supported Versions
+
+| Version | Supported          |
+| ------- | ------------------ |
+| 1.1.x   | :white_check_mark: |
+
+## Reporting a Vulnerability
+
+Please send report privately to evpobr@gmail.com, and include how would you like to be credited.
diff --git a/Scripts/android-configure.sh b/Scripts/android-configure.sh
new file mode 100755
index 0000000..accb386
--- /dev/null
+++ b/Scripts/android-configure.sh
@@ -0,0 +1,96 @@
+#!/bin/bash -e
+
+# Copyright (C) 2013-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Neither the author nor the names of any contributors may be used
+#       to endorse or promote products derived from this software without
+#       specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Android NDK version number; eg r10, r10b  etc
+ANDROID_NDK_VER=${ANDROID_NDK_VER:-r10}
+
+# Android NDK gcc version; eg 4.8, 4.9 etc.
+ANDROID_GCC_VER=${ANDROID_GCC_VER:-4.9}
+
+# Android API version; eg 14 (Android 4.0), 21 (Android 5.0)  etc.
+ANDROID_API_VER=${ANDROID_API_VER:-14}
+
+ANDROID_TARGET=${ANDROID_TARGET:-arm-linux-androideabi}
+
+if test -z ${ANDROID_TOOLCHAIN_HOME} ; then
+	echo "Environment variable ANDROID_TOOLCHAIN_HOME not defined."
+	echo "This should point to the directory containing the Android NDK."
+    exit 1
+	fi
+
+#-------------------------------------------------------------------------------
+# No more user config beyond here.
+
+BUILD_MACHINE=$(uname -s | tr 'A-Z' 'a-z')-$(uname -m)
+
+function die_with {
+	echo $1
+	exit 1
+}
+
+export CROSS_COMPILE=${ANDROID_TARGET}
+
+# Don't forget to adjust this to your NDK path
+export ANDROID_NDK=${ANDROID_TOOLCHAIN_HOME}/android-ndk-${ANDROID_NDK_VER}
+test -d ${ANDROID_NDK} || die_with "Error : ANDROID_NDK '$ANDROID_NDK' does not exist."
+
+export ANDROID_PREFIX=${ANDROID_NDK}/toolchains/arm-linux-androideabi-${ANDROID_GCC_VER}/prebuilt/${BUILD_MACHINE}
+test -d ${ANDROID_PREFIX} || die_with "Error : ANDROID_PREFIX '$ANDROID_PREFIX' does not exist."
+
+export SYSROOT=${ANDROID_NDK}/platforms/android-${ANDROID_API_VER}/arch-arm
+test -d ${SYSROOT} || die_with "Error : SYSROOT '$SYSROOT' does not exist."
+
+export CROSS_PREFIX=${ANDROID_PREFIX}/bin/${CROSS_COMPILE}
+test -f ${CROSS_PREFIX}-gcc || die_with "Error : CROSS_PREFIX compiler '${CROSS_PREFIX}-gcc' does not exist."
+
+
+# Non-exhaustive lists of compiler + binutils
+# Depending on what you compile, you might need more binutils than that
+export CPP=${CROSS_PREFIX}-cpp
+export AR=${CROSS_PREFIX}-ar
+export AS=${CROSS_PREFIX}-as
+export NM=${CROSS_PREFIX}-nm
+export CC=${CROSS_PREFIX}-gcc
+export CXX=${CROSS_PREFIX}-g++
+export LD=${CROSS_PREFIX}-ld
+export RANLIB=${CROSS_PREFIX}-ranlib
+
+# Don't mix up .pc files from your host and build target
+export PKG_CONFIG_PATH=${PREFIX}/lib/pkgconfig
+
+# Set up the needed FLAGS.
+export CFLAGS="${CFLAGS} -gstabs --sysroot=${SYSROOT} -I${SYSROOT}/usr/include -I${ANDROID_PREFIX}/include"
+export CXXFLAGS="${CXXFLAGS} -gstabs -fno-exceptions --sysroot=${SYSROOT} -I${SYSROOT}/usr/include -I${ANDROID_PREFIX}/include -I${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/${ANDROID_GCC_VER}/include/ -I${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/${ANDROID_GCC_VER}/libs/armeabi/include"
+
+export CPPFLAGS="${CFLAGS}"
+export LDFLAGS="${LDFLAGS} -L${SYSROOT}/usr/lib -L${ANDROID_PREFIX}/lib"
+
+# Create a symlink to the gdbclient.
+test -h gdbclient || ln -s ${ANDROID_PREFIX}/bin/arm-linux-androideabi-gdb gdbclient
+
+./configure --host=${CROSS_COMPILE} --with-sysroot=${SYSROOT} "$@"
diff --git a/Scripts/asan-configure.sh b/Scripts/asan-configure.sh
new file mode 100755
index 0000000..0ac43b9
--- /dev/null
+++ b/Scripts/asan-configure.sh
@@ -0,0 +1,4 @@
+#!/bin/bash -eu
+
+CFLAGS="-fsanitize=address -g" CXXFLAGS=${CFLAGS} ./configure
+
diff --git a/Scripts/build-test-tarball.mk.in b/Scripts/build-test-tarball.mk.in
new file mode 100644
index 0000000..5dac9d9
--- /dev/null
+++ b/Scripts/build-test-tarball.mk.in
@@ -0,0 +1,61 @@
+#!/usr/bin/make -f
+
+# This is probably only going to work with GNU Make.
+# This in a separate file instead of in Makefile.am because Automake complains
+# about the GNU Make-isms.
+
+EXEEXT = @EXEEXT@
+
+PACKAGE_VERSION = @PACKAGE_VERSION@
+
+HOST_TRIPLET = @HOST_TRIPLET@
+
+SRC_BINDIR = @SRC_BINDIR@
+TEST_BINDIR = @TEST_BINDIR@
+
+LIBRARY := $(SRC_BINDIR)libsndfile.so.$(LIB_VERSION)
+
+LIB_VERSION := $(shell echo $(PACKAGE_VERSION) | sed -e 's/[a-z].*//')
+
+TESTNAME = libsndfile-testsuite-$(HOST_TRIPLET)-$(PACKAGE_VERSION)
+
+TARBALL = $(TESTNAME).tar.gz
+
+# Find the test programs by grepping the script for the programs it executes.
+testprogs := $(shell grep '^\./' tests/test_wrapper.sh | sed -e "s|./||" -e "s/ .*//" | sort | uniq)
+# Also add the programs not found by the above.
+testprogs += tests/sfversion$(EXEEXT) tests/stdin_test$(EXEEXT) tests/stdout_test$(EXEEXT) \
+				tests/cpp_test$(EXEEXT) tests/win32_test$(EXEEXT)
+
+libfiles := $(shell if test ! -z $(EXEEXT) ; then echo "src/libsndfile-1.def src/.libs/libsndfile-1.dll" ; elif test -f $(LIBRARY) ; then echo $(LIBRARY) ; fi  ; fi)
+
+testbins := $(testprogs) $(libfiles)
+
+all : $(TARBALL)
+
+clean :
+	rm -rf $(TARBALL) $(TESTNAME)/
+
+check : $(TESTNAME)/test_wrapper.sh
+	(cd ./$(TESTNAME)/ && ./test_wrapper.sh)
+
+$(TARBALL) : $(TESTNAME)/test_wrapper.sh
+	tar zcf $@ $(TESTNAME)
+	rm -rf $(TESTNAME)
+	@echo
+	@echo "Created : $(TARBALL)"
+	@echo
+
+$(TESTNAME)/test_wrapper.sh : tests/test_wrapper.sh tests/pedantic-header-test.sh
+	rm -rf $(TESTNAME)
+	mkdir -p $(TESTNAME)/tests/
+	echo
+	echo $(testbins)
+	echo
+	cp $(testbins) $(TESTNAME)/tests/
+	cp tests/test_wrapper.sh $(TESTNAME)/
+	cp tests/pedantic-header-test.sh $(TESTNAME)/tests/
+	chmod u+x $@
+
+tests/test_wrapper.sh : tests/test_wrapper.sh.in
+	make $@
diff --git a/Scripts/clang-sanitize.sh b/Scripts/clang-sanitize.sh
new file mode 100755
index 0000000..2898883
--- /dev/null
+++ b/Scripts/clang-sanitize.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+# This is known to work with clang-3.4 from Debian testing/unstable.
+# 2013/07/14
+
+export CC=clang
+export CXX=clang++
+export CFLAGS="-O3 -fsanitize=address,integer,undefined"
+export CXXFLAGS="-O3 -fsanitize=address,integer,undefined"
+
+./configure --enable-gcc-werror
+
+make clean all check
diff --git a/Scripts/cmake-build.sh b/Scripts/cmake-build.sh
new file mode 100755
index 0000000..10260f3
--- /dev/null
+++ b/Scripts/cmake-build.sh
@@ -0,0 +1,9 @@
+#!/bin/bash -eu
+
+rm -rf CMakeCache.txt CMakeFiles/ _Build
+
+mkdir _Build
+cd _Build
+cmake ..
+cmake --build .
+ctest -V
diff --git a/Scripts/cstyle.py b/Scripts/cstyle.py
new file mode 100755
index 0000000..8e1df27
--- /dev/null
+++ b/Scripts/cstyle.py
@@ -0,0 +1,256 @@
+#!/usr/bin/python -tt
+#
+# Copyright (C) 2005-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+#
+# Released under the 2 clause BSD license.
+
+"""
+This program checks C code for compliance to coding standards used in
+libsndfile and other projects I run.
+"""
+
+import re
+import sys
+
+
+class Preprocessor:
+	"""
+	Preprocess lines of C code to make it easier for the CStyleChecker class to
+	test for correctness. Preprocessing works on a single line at a time but
+	maintains state between consecutive lines so it can preprocessess multi-line
+	comments.
+	Preprocessing involves:
+	  - Strip C++ style comments from a line.
+	  - Strip C comments from a series of lines. When a C comment starts and
+	    ends on the same line it will be replaced with 'comment'.
+	  - Replace arbitrary C strings with the zero length string.
+	  - Replace '#define f(x)' with '#define f (c)' (The C #define requires that
+	    there be no space between defined macro name and the open paren of the
+	    argument list).
+	Used by the CStyleChecker class.
+	"""
+	def __init__ (self):
+		self.comment_nest = 0
+		self.leading_space_re = re.compile ('^(\t+| )')
+		self.trailing_space_re = re.compile ('(\t+| )$')
+		self.define_hack_re = re.compile ("(#\s*define\s+[a-zA-Z0-9_]+)\(")
+
+	def comment_nesting (self):
+		"""
+		Return the currect comment nesting. At the start and end of the file,
+		this value should be zero. Inside C comments it should be 1 or
+		(possibly) more.
+		"""
+		return self.comment_nest
+
+	def __call__ (self, line):
+		"""
+		Strip the provided line of C and C++ comments. Stripping of multi-line
+		C comments works as expected.
+		"""
+
+		line = self.define_hack_re.sub (r'\1 (', line)
+
+		line = self.process_strings (line)
+
+		# Strip C++ style comments.
+		if self.comment_nest == 0:
+			line = re.sub ("( |\t*)//.*", '', line)
+
+		# Strip C style comments.
+		open_comment = line.find ('/*')
+		close_comment = line.find ('*/')
+
+		if self.comment_nest > 0 and close_comment < 0:
+			# Inside a comment block that does not close on this line.
+			return ""
+
+		if open_comment >= 0 and close_comment < 0:
+			# A comment begins on this line but doesn't close on this line.
+			self.comment_nest += 1
+			return self.trailing_space_re.sub ('', line [:open_comment])
+
+		if open_comment < 0 and close_comment >= 0:
+			# Currently open comment ends on this line.
+			self.comment_nest -= 1
+			return self.trailing_space_re.sub ('', line [close_comment + 2:])
+
+		if open_comment >= 0 and close_comment > 0 and self.comment_nest == 0:
+			# Comment begins and ends on this line. Replace it with 'comment'
+			# so we don't need to check whitespace before and after the comment
+			# we're removing.
+			newline = line [:open_comment] + "comment" + line [close_comment + 2:]
+			return self.__call__ (newline)
+
+		return line
+
+	def process_strings (self, line):
+		"""
+		Given a line of C code, return a string where all literal C strings have
+		been replaced with the empty string literal "".
+		"""
+		for k in range (0, len (line)):
+			if line [k] == '"':
+				start = k
+				for k in range (start + 1, len (line)):
+					if line [k] == '"' and line [k - 1] != '\\':
+						return line [:start + 1] + '"' + self.process_strings (line [k + 1:])
+		return line
+
+
+class CStyleChecker:
+	"""
+	A class for checking the whitespace and layout of a C code.
+	"""
+	def __init__ (self, debug):
+		self.debug = debug
+		self.filename = None
+		self.error_count = 0
+		self.line_num = 1
+		self.orig_line = ''
+		self.trailing_newline_re = re.compile ('[\r\n]+$')
+		self.indent_re = re.compile ("^\s*")
+		self.last_line_indent = ""
+		self.last_line_indent_curly = False
+		self.re_checks = \
+			[ ( re.compile ("  "),		"multiple space instead of tab" )
+			, ( re.compile ("\t "), 	"space after tab" )
+			, ( re.compile ("[^ ];"),	"missing space before semi-colon" )
+			, ( re.compile ("{[^\s}]"),	"missing space after open brace" )
+			, ( re.compile ("[^{\s]}"),	"missing space before close brace" )
+			, ( re.compile ("[ \t]+$"),	"contains trailing whitespace" )
+
+			, ( re.compile (",[^\s\n]"),		"missing space after comma" )
+			, ( re.compile (";[^\s]"), 	"missing space after semi-colon" )
+			, ( re.compile ("=[^\s\"'=]"),		"missing space after assignment" )
+
+			# Open and close parenthesis.
+			, ( re.compile ("[^\s\(\[\*&']\("),				"missing space before open parenthesis" )
+			, ( re.compile ("\)(-[^>]|[^,'\s\n\)\]-])"),	"missing space after close parenthesis" )
+			, ( re.compile ("\s(do|for|if|when)\s.*{$"),	"trailing open parenthesis at end of line" )
+			, ( re.compile ("\( [^;]"),						"space after open parenthesis" )
+			, ( re.compile ("[^;] \)"),						"space before close parenthesis" )
+
+			# Open and close square brace.
+			, ( re.compile ("[^\s\(\]]\["),			"missing space before open square brace" )
+			, ( re.compile ("\][^,\)\]\[\s\.-]"),	"missing space after close square brace" )
+			, ( re.compile ("\[ "),					"space after open square brace" )
+			, ( re.compile (" \]"),					"space before close square brace" )
+
+			# Space around operators.
+			, ( re.compile ("[^\s][\*/%+-][=][^\s]"),		"missing space around opassign" )
+			, ( re.compile ("[^\s][<>!=^/][=]{1,2}[^\s]"),	"missing space around comparison" )
+
+			# Parens around single argument to return.
+			, ( re.compile ("\s+return\s+\([a-zA-Z0-9_]+\)\s+;"),	"parens around return value" )
+
+			# Parens around single case argument.
+			, ( re.compile ("\s+case\s+\([a-zA-Z0-9_]+\)\s+:"),	"parens around single case argument" )
+
+			# Open curly at end of line.
+			, ( re.compile ("\)\s*{\s*$"),	"open curly brace at end of line" )
+
+			# Pre and post increment/decrment.
+			, ( re.compile ("[^\(\[][+-]{2}[a-zA-Z0-9_]"),       "space after pre increment/decrement" )
+			, ( re.compile ("[a-zA-Z0-9_][+-]{2}[^\)\,]]"),       "space before post increment/decrement" )
+			]
+
+	def get_error_count (self):
+		"""
+		Return the current error count for this CStyleChecker object.
+		"""
+		return self.error_count
+
+	def check_files (self, files):
+		"""
+		Run the style checker on all the specified files.
+		"""
+		for filename in files:
+			self.check_file (filename)
+
+	def check_file (self, filename):
+		"""
+		Run the style checker on the specified file.
+		"""
+		self.filename = filename
+		cfile = open (filename, "r")
+
+		self.line_num = 1
+
+		preprocess = Preprocessor ()
+		while 1:
+			line = cfile.readline ()
+			if not line:
+				break
+
+			line = self.trailing_newline_re.sub ('', line)
+			self.orig_line = line
+
+			self.line_checks (preprocess (line))
+
+			self.line_num += 1
+
+		cfile.close ()
+		self.filename = None
+
+		# Check for errors finding comments.
+		if preprocess.comment_nesting () != 0:
+			print ("Weird, comments nested incorrectly.")
+			sys.exit (1)
+
+		return
+
+	def line_checks (self, line):
+		"""
+		Run the style checker on provided line of text, but within the context
+		of how the line fits within the file.
+		"""
+
+		indent = len (self.indent_re.search (line).group ())
+		if re.search ("^\s+}", line):
+			if not self.last_line_indent_curly and indent != self.last_line_indent:
+				None	# self.error ("bad indent on close curly brace")
+			self.last_line_indent_curly = True
+		else:
+			self.last_line_indent_curly = False
+
+		# Now all the regex checks.
+		for (check_re, msg) in self.re_checks:
+			if check_re.search (line):
+				self.error (msg)
+
+		if re.search ("[a-zA-Z0-9][<>!=^/&\|]{1,2}[a-zA-Z0-9]", line):
+			if not re.search (".*#include.*[a-zA-Z0-9]/[a-zA-Z]", line):
+				self.error ("missing space around operator")
+
+		self.last_line_indent = indent
+		return
+
+	def error (self, msg):
+		"""
+		Print an error message and increment the error count.
+		"""
+		print ("%s (%d) : %s" % (self.filename, self.line_num, msg))
+		if self.debug:
+			print ("'" + self.orig_line + "'")
+		self.error_count += 1
+
+#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+if len (sys.argv) < 1:
+	print ("Usage : yada yada")
+	sys.exit (1)
+
+# Create a new CStyleChecker object
+if sys.argv [1] == '-d' or sys.argv [1] == '--debug':
+	cstyle = CStyleChecker (True)
+	cstyle.check_files (sys.argv [2:])
+else:
+	cstyle = CStyleChecker (False)
+	cstyle.check_files (sys.argv [1:])
+
+
+if cstyle.get_error_count ():
+	sys.exit (1)
+
+sys.exit (0)
diff --git a/Scripts/git-pre-commit-hook b/Scripts/git-pre-commit-hook
new file mode 100755
index 0000000..dc70e73
--- /dev/null
+++ b/Scripts/git-pre-commit-hook
@@ -0,0 +1,84 @@
+#!/bin/sh
+
+if git rev-parse --verify HEAD >/dev/null 2>&1 ; then
+	against=HEAD
+else
+	# Initial commit: diff against an empty tree object
+	against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
+	fi
+
+# Redirect output to stderr.
+exec 1>&2
+
+#-------------------------------------------------------------------------------
+# Prevent files with non-ascii filenames from being committed.
+
+if test $(git diff --cached --name-only --diff-filter=A -z $against | LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0 ; then
+	echo "Error: Attempt to add a non-ascii file name."
+	echo
+	echo "This can cause problems if you want to work"
+	echo "with people on other platforms."
+	echo
+	echo "To be portable it is advisable to rename the file ..."
+	echo
+	echo "Commit aborted."
+	exit 1
+	fi
+
+#-------------------------------------------------------------------------------
+# Check the formatting of all C files.
+
+# http://man.openbsd.org/sed#r
+# http://man.openbsd.org/sed#E
+# http://netbsd.gw.com/cgi-bin/man-cgi?sed++NetBSD-current
+# https://github.com/freebsd/freebsd/blob/master/usr.bin/sed/main.c
+# http://git.savannah.gnu.org/gitweb/?p=sed.git;a=blob;f=sed/sed.c
+# GNU has -r and -E (undocumented); MacOS has -E but not -r; Sunos has neither.
+files=$(git diff-index --name-status --cached HEAD | grep -v ^D | sed -E "s/^[A-Z]+[A-Z0-9]*[ \t]+/ /")
+
+cfiles=""
+for f in $files ; do
+	if test `dirname $f` = "src/ALAC" ; then
+		echo "Skipping cstyle checking on $f"
+	elif test `echo $f | grep -c "\.[ch]$"` -gt 0 ; then
+		cfiles="$cfiles $f"
+		fi
+	done
+
+if test -n "$cfiles" ; then
+	Scripts/cstyle.py $cfiles
+	if test $? -ne 0 ; then
+		echo
+		echo "Commit aborted. Fix the above error before trying again."
+		exit 1
+		fi
+	fi
+
+#-------------------------------------------------------------------------------
+# Check the copyright notice of all files to be commited.
+
+user=`git config --global user.email`
+year=`date +"%Y"`
+
+missing_copyright_year=""
+if test $user = "erikd@mega-nerd.com" ; then
+	for f in $files ; do
+		if test `head -5 $f | grep -i copyright | grep -c -i $user` -gt 0 ; then
+			user_copyright=`grep -i copyright $f | grep $user | grep -c $year`
+			if test $user_copyright -lt 1 ; then
+				missing_copyright_year="$missing_copyright_year $f"
+				fi
+			fi
+		done
+	fi
+
+if test -n "$missing_copyright_year" ; then
+	echo "Missing current year in the copyright notice of the following files:"
+	for f in $missing_copyright_year ; do
+		echo "    $f"
+		done
+	echo "Commit aborted."
+	exit 1
+	fi
+
+exit 0
diff --git a/Scripts/linux-to-win-cross-configure.sh b/Scripts/linux-to-win-cross-configure.sh
new file mode 100755
index 0000000..c1fdc07
--- /dev/null
+++ b/Scripts/linux-to-win-cross-configure.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+case "$1" in
+	w32)
+		compiler_name=i686-w64-mingw32
+		;;
+	w64)
+		compiler_name=x86_64-w64-mingw32
+		;;
+	*)
+		echo "$0 (w32|w64) <other args>"
+		exit 0
+		;;
+	esac
+
+shift
+
+build_cpu=$(dpkg-architecture -qDEB_BUILD_GNU_CPU)
+build_host=$build_cpu-linux
+
+./configure --host=$compiler_name --target=$compiler_name --build=$build_host \
+		--program-prefix='' --disable-sqlite --disable-static $@
diff --git a/Scripts/static-deps-build.mk b/Scripts/static-deps-build.mk
new file mode 100755
index 0000000..bbf2f0d
--- /dev/null
+++ b/Scripts/static-deps-build.mk
@@ -0,0 +1,125 @@
+#!/usr/bin/make -f
+
+# If this is set to true (via the environment) then CRC checking will be
+# disabled in libogg giving fuzzers a better chance at finding something.
+disable_ogg_crc ?= false
+
+# Build libsndfile as a dynamic/shared library, but statically link to
+# libFLAC, libogg, libopus and libvorbis
+
+ogg_version = libogg-1.3.4
+ogg_sha256sum = c163bc12bc300c401b6aa35907ac682671ea376f13ae0969a220f7ddf71893fe
+
+vorbis_version = libvorbis-1.3.7
+vorbis_sha256sum = b33cc4934322bcbf6efcbacf49e3ca01aadbea4114ec9589d1b1e9d20f72954b
+
+flac_version = flac-1.3.3
+flac_sha256sum = 213e82bd716c9de6db2f98bcadbc4c24c7e2efe8c75939a1a84e28539c4e1748
+
+opus_version = opus-1.3.1
+opus_sha256sum = 65b58e1e25b2a114157014736a3d9dfeaad8d41be1c8179866f144a2fb44ff9d
+
+#-------------------------------------------------------------------------------
+# Code follows.
+
+ogg_tarball = $(ogg_version).tar.xz
+vorbis_tarball = $(vorbis_version).tar.xz
+flac_tarball = $(flac_version).tar.xz
+opus_tarball = $(opus_version).tar.gz
+
+download_url = http://downloads.xiph.org/releases/
+tarball_dir = Build/Tarballs
+stamp_dir = Build/Stamp
+
+build_dir = $(shell pwd)/Build
+config_options = --prefix=$(build_dir) --disable-shared --enable-option-checking
+
+pwd = $(shell pwd)
+
+help :
+	@echo
+	@echo "This script will build libsndfile as a dynamic/shared library but statically linked"
+	@echo "to libFLAC, libogg and libvorbis. It should work on Linux and Mac OS X. It might"
+	@echo "work on Windows with a correctly set up MinGW."
+	@echo
+	@echo "It requires all the normal build tools require to build libsndfile plus wget."
+	@echo
+
+config : Build/Stamp/configure
+
+build : Build/Stamp/build
+
+clean :
+	rm -rf Build/flac-* Build/libogg-* Build/libvorbis-* Build/opus-*
+	rm -rf Build/bin Build/include Build/lib Build/share
+	rm -f Build/Stamp/install Build/Stamp/extract Build/Stamp/sha256sum Build/Stamp/build-ogg
+
+Build/Stamp/init :
+	mkdir -p $(stamp_dir) $(tarball_dir)
+	touch $@
+
+Build/Tarballs/$(flac_tarball) : Build/Stamp/init
+	(cd $(tarball_dir) && wget $(download_url)flac/$(flac_tarball) -O $(flac_tarball))
+	touch $@
+
+Build/Tarballs/$(ogg_tarball) : Build/Stamp/init
+	(cd $(tarball_dir) && wget $(download_url)ogg/$(ogg_tarball) -O $(ogg_tarball))
+	touch $@
+
+Build/Tarballs/$(vorbis_tarball) : Build/Stamp/init
+	(cd $(tarball_dir) && wget $(download_url)vorbis/$(vorbis_tarball) -O $(vorbis_tarball))
+	touch $@
+
+Build/Tarballs/$(opus_tarball) : Build/Stamp/init
+	(cd $(tarball_dir) && wget https://archive.mozilla.org/pub/opus/$(opus_tarball) -O $(opus_tarball))
+	touch $@
+
+Build/Stamp/tarballs : Build/Tarballs/$(flac_tarball) Build/Tarballs/$(ogg_tarball) Build/Tarballs/$(vorbis_tarball) Build/Tarballs/$(opus_tarball)
+	touch $@
+
+Build/Stamp/sha256sum : Build/Stamp/tarballs
+	test `sha256sum $(tarball_dir)/$(ogg_tarball) | sed "s/ .*//"` = $(ogg_sha256sum)
+	test `sha256sum $(tarball_dir)/$(vorbis_tarball) | sed "s/ .*//"` = $(vorbis_sha256sum)
+	test `sha256sum $(tarball_dir)/$(flac_tarball) | sed "s/ .*//"` = $(flac_sha256sum)
+	test `sha256sum $(tarball_dir)/$(opus_tarball) | sed "s/ .*//"` = $(opus_sha256sum)
+	touch $@
+
+Build/Stamp/extract : Build/Stamp/sha256sum
+	# (cd Build && tar xf Tarballs/$(ogg_tarball))
+	(cd Build && tar xf Tarballs/$(flac_tarball))
+	(cd Build && tar xf Tarballs/$(vorbis_tarball))
+	(cd Build && tar xf Tarballs/$(opus_tarball))
+	touch $@
+
+Build/Stamp/build-ogg : Build/Stamp/sha256sum
+ifeq ($(disable_ogg_crc), true)
+	echo "Ogg/CRC enabled"
+	(cd Build && git clone https://github.com/xiph/ogg $(ogg_version))
+	(cd Build/$(ogg_version) && autoreconf -vif && CFLAGS=-fPIC ./configure $(config_options) --disable-crc && make all install)
+else
+	echo "Ogg/CRC disabled"
+	(cd Build && tar xf Tarballs/$(ogg_tarball))
+	(cd Build/$(ogg_version) && CFLAGS=-fPIC ./configure $(config_options) && make all install)
+endif
+	touch $@
+
+Build/Stamp/install-libs : Build/Stamp/extract Build/Stamp/build-ogg
+	(cd Build/$(vorbis_version) && CFLAGS=-fPIC ./configure $(config_options) && make all install)
+	(cd Build/$(flac_version) && CFLAGS=-fPIC ./configure $(config_options) && make all install)
+	(cd Build/$(opus_version) && CFLAGS=-fPIC ./configure $(config_options) && make all install)
+	touch $@
+
+configure : configure.ac
+	autoreconf -vif
+
+Build/Stamp/configure : Build/Stamp/install-libs configure
+	PKG_CONFIG_LIBDIR=Build/lib/pkgconfig ./configure
+	sed -i 's#^EXTERNAL_XIPH_CFLAGS.*#EXTERNAL_XIPH_CFLAGS = -I$(pwd)/Build/include#' Makefile
+	sed -i 's#^EXTERNAL_XIPH_LIBS.*#EXTERNAL_XIPH_LIBS = -static $(pwd)/Build/lib/libFLAC.la $(pwd)/Build/lib/libvorbis.la $(pwd)/Build/lib/libvorbisenc.la $(pwd)/Build/lib/libopus.la $(pwd)/Build/lib/libogg.la -dynamic #' Makefile
+	make clean
+	touch $@
+
+Build/Stamp/build : Build/Stamp/configure
+	make all check
+	touch $@
+
diff --git a/Win32/README-precompiled-dll.txt b/Win32/README-precompiled-dll.txt
new file mode 100644
index 0000000..bde8124
--- /dev/null
+++ b/Win32/README-precompiled-dll.txt
@@ -0,0 +1,40 @@
+Notes on Using the Pre-compiled libsndfile DLL.
+===============================================
+
+In order to use this pre-compiled DLL with Visual Studio, you will need to
+generate a .LIB file from the DLL.
+
+This can be achieved as follows:
+
+  1) In a CMD window, change to the directory containing this file and
+     run the command:
+
+          lib /machine:i386 /def:libsndfile-1.def
+
+You now have two files:
+
+        libsndfile-1.dll
+        libsndfile-1.lib
+
+to be used with VisualStudio.
+
+If the lib command fails with a command saying "'lib' is not recognized as
+an internal or external command, operable program or batch file", you need
+to find the location of "lib.exe" and add that directory to your PATH
+environment variable. Another alternative is to use the "Visual Studio 2005
+Command Prompt" Start menu item:
+
+   Start ->
+   		All Programs ->
+			Visual Studio 2005 ->
+				Visual Studio Tools ->
+					Visual Studio 2005 Command Prompt
+
+If for some reason these instructions don't work for you or you are still
+not able to use the libsndfile DLL with you project, please do not contact
+the main author of libsndfile. Instead, join the libsndfile-users mailing
+list :
+
+        http://www.mega-nerd.com/libsndfile/lists.html
+
+and ask a question there.
diff --git a/Win32/testprog.c b/Win32/testprog.c
new file mode 100644
index 0000000..d26d844
--- /dev/null
+++ b/Win32/testprog.c
@@ -0,0 +1,16 @@
+/*	Simple test program to make sure that Win32 linking to libsndfile is
+**	working.
+*/
+
+#include <stdio.h>
+
+#include "sndfile.h"
+
+int
+main (void)
+{	static char strbuffer [256] ;
+	sf_command (NULL, SFC_GET_LIB_VERSION, strbuffer, sizeof (strbuffer)) ;
+	puts (strbuffer) ;
+	return 0 ;
+}
+
diff --git a/cmake/CMakeAutoGen.cmake b/cmake/CMakeAutoGen.cmake
new file mode 100644
index 0000000..f82dc13
--- /dev/null
+++ b/cmake/CMakeAutoGen.cmake
@@ -0,0 +1,46 @@
+# CMake implementation of AutoGen
+# Copyright (C) 2017 Anonymous Maarten <anonymous.maarten@gmail.com>
+
+set(AUTOGEN_SCRIPT "${CMAKE_MODULE_PATH}/CMakeAutoGenScript.cmake")
+
+function(lsf_autogen DIR_REL NAME_WE)
+	set(EXTS ${ARGN})
+	set(INPUT "${CMAKE_CURRENT_SOURCE_DIR}/${DIR_REL}/${NAME_WE}.def")
+	set(OUTPUTS)
+	foreach(EXT ${EXTS})
+		list(APPEND OUTPUTS "${NAME_WE}.${EXT}")
+	endforeach()
+	add_autogen_target("${INPUT}" "${CMAKE_CURRENT_BINARY_DIR}/${DIR_REL}" ${OUTPUTS})
+endfunction()
+
+function(add_autogen_target INPUT OUTPUTDIR)
+	set(OUTPUTFILES "${ARGN}")
+
+	if (OUTPUTDIR)
+		set(PREFIX "${OUTPUTDIR}/")
+	else()
+		set(PREFIX "")
+	endif()
+
+	set(ARTIFACTS)
+	foreach(OUTPUTFILE ${OUTPUTFILES})
+		list(APPEND ARTIFACTS "${PREFIX}${OUTPUTFILE}")
+	endforeach()
+
+	set(EXTRA_ARGS)
+	if (AUTOGEN_DEBUG)
+		list(APPEND EXTRA_ARGS "-DDEBUG=1")
+	endif()
+	if (OUTPUTDIR)
+		list(APPEND EXTRA_ARGS "-DOUTPUTDIR=${OUTPUTDIR}")
+	endif()
+
+	add_custom_command(
+		OUTPUT ${ARTIFACTS}
+		COMMAND ${CMAKE_COMMAND} "-DDEFINITION=${INPUT}" ${EXTRA_ARGS} -P "${AUTOGEN_SCRIPT}"
+		MAIN_DEPENDENCY "${INPUT}"
+		DEPENDS "${AUTOGEN_SCRIPT}"
+		COMMENT "CMakeAutoGen: generating ${OUTPUTFILES}"
+		WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
+	)
+endfunction()
diff --git a/cmake/CMakeAutoGenScript.cmake b/cmake/CMakeAutoGenScript.cmake
new file mode 100644
index 0000000..ade53e1
--- /dev/null
+++ b/cmake/CMakeAutoGenScript.cmake
@@ -0,0 +1,442 @@
+# CMake implementation of AutoGen
+# Copyright (C) 2017 Anonymous Maarten <anonymous.maarten@gmail.com>
+
+set(WS " \t\r\n")
+
+function(cutoff_first_occurrence TEXT OCCURRENCE RESULT)
+    string(FIND "${TEXT}" "${OCCURRENCE}" OCCURRENCE_INDEX)
+    if (OCCURRENCE_INDEX EQUAL -1)
+        set(${TEXT} "" PARENT_SCOPE)
+        return()
+    endif()
+
+    string(LENGTH "${OCCURRENCE}" OCCURRENCE_LENGTH)
+    math(EXPR CUTOFF_INDEX "${OCCURRENCE_INDEX}+${OCCURRENCE_LENGTH}")
+    string(SUBSTRING "${TEXT}" ${CUTOFF_INDEX} -1 TEXT_REMAINDER)
+    set(${RESULT} "${TEXT_REMAINDER}" PARENT_SCOPE)
+
+    endfunction()
+
+function(read_definition DEFINITION_FILENAME TEMPLATE_FILENAME DATA)
+    file(READ "${DEFINITION_FILENAME}" DEFINITION_CONTENTS)
+
+    string(REGEX MATCH "autogen definitions ([a-zA-Z\\._-]+);[${WS}]*" TEMPLATE_MATCH "${DEFINITION_CONTENTS}")
+    if (NOT TEMPLATE_MATCH)
+        message(FATAL_ERROR "${DEFINITION_FILENAME} doest not contain an AutoGen definition.")
+    endif()
+
+    get_filename_component(DEFINITION_DIR "${DEFINITION_FILENAME}" PATH)
+    set(${TEMPLATE_FILENAME} "${DEFINITION_DIR}/${CMAKE_MATCH_1}" PARENT_SCOPE)
+    if (DEBUG)
+        message("found: TEMPLATE_FILENAME=${CMAKE_MATCH_1}")
+    endif()
+
+    cutoff_first_occurrence("${DEFINITION_CONTENTS}" "${TEMPLATE_MATCH}" DEFINITION_CONTENTS)
+
+    set(DEFINITION "")
+
+    while (1)
+        string(REGEX MATCH "([a-zA-Z_][a-zA-Z0-9_]*)[${WS}]*=[${WS}]*{[${WS}]*" GROUPSTART_MATCH "${DEFINITION_CONTENTS}")
+        if (NOT GROUPSTART_MATCH)
+            break()
+        endif()
+        set(GROUPNAME "${CMAKE_MATCH_1}")
+        cutoff_first_occurrence("${DEFINITION_CONTENTS}" "${GROUPSTART_MATCH}" DEFINITION_CONTENTS)
+        if (DEBUG)
+            message("found: GROUPNAME=${GROUPNAME}")
+        endif()
+        set(NBKEYS 0)
+        set(GROUP_KEY_VALUES "")
+        while (1)
+            string(REGEX MATCH "^([a-zA-Z_][a-zA-Z0-9_]*)[${WS}]*=[${WS}]*(([\"']([${WS}a-zA-Z0-9_%\\\"<>\(\)\\.*+/?:,\\-]+)[\"'])|([a-zA-Z0-9_%\\]+))[${WS}]*;[${WS}]*" KEY_VALUE_MATCH "${DEFINITION_CONTENTS}")
+            if (NOT KEY_VALUE_MATCH)
+                break()
+            endif()
+            set(KEY "${CMAKE_MATCH_1}")
+            if ("${CMAKE_MATCH_4}" STREQUAL "")
+                set(VALUE "${CMAKE_MATCH_5}")
+            else()
+                string(REPLACE "\\\"" "\"" VALUE "${CMAKE_MATCH_4}")
+                #set(VALUE "${CMAKE_MATCH_4}")
+            endif()
+
+            if (DEBUG)
+                message("found: KEY=${KEY}, VALUE=${VALUE}")
+            endif()
+            math(EXPR NBKEYS "${NBKEYS}+1")
+            list(APPEND GROUP_KEY_VALUES "${KEY}" "${VALUE}")
+            cutoff_first_occurrence("${DEFINITION_CONTENTS}" "${KEY_VALUE_MATCH}" DEFINITION_CONTENTS)
+        endwhile()
+        string(REGEX MATCH "^[${WS}]*}[${WS}]*;[${WS}]*" GROUPEND_MATCH "${DEFINITION_CONTENTS}")
+        if (NOT GROUPEND_MATCH)
+            message(FATAL_ERROR "Group ${GROUPNAME} did not finish.")
+        endif()
+        cutoff_first_occurrence("${DEFINITION_CONTENTS}" "${GROUPEND_MATCH}" DEFINITION_CONTENTS)
+        list(APPEND DEFINITION "${GROUPNAME}" ${NBKEYS} ${GROUP_KEY_VALUES})
+    endwhile()
+    set(${DATA} "${DEFINITION}" PARENT_SCOPE)
+endfunction()
+
+function(match_autogen_group TEXT START POS0 POS1 MATCH FOUND)
+    string(SUBSTRING "${TEXT}" "${START}" -1 TEXT)
+    string(REGEX MATCH "\\[\\+[${WS}]*([ a-zA-Z0-9=_$%\\(\\)\"\\+\\-]+)[${WS}]*\\+\\]" MATCH_GROUP "${TEXT}")
+    if ("${MATCH_GROUP}" STREQUAL "")
+        set(${FOUND} 0 PARENT_SCOPE)
+        return()
+    endif()
+    string(FIND "${TEXT}" "${MATCH_GROUP}" START_TEXT)
+    math(EXPR POS0_var "${START}+${START_TEXT}")
+    string(LENGTH "${MATCH_GROUP}" MATCH_LENGTH)
+    math(EXPR POS1_var "${POS0_var}+${MATCH_LENGTH}")
+    set(${POS0} "${POS0_var}" PARENT_SCOPE)
+    set(${POS1} "${POS1_var}" PARENT_SCOPE)
+    set(${FOUND} 1 PARENT_SCOPE)
+    string(STRIP "${CMAKE_MATCH_1}" CONTENT)
+    set("${MATCH}" "${CONTENT}" PARENT_SCOPE)
+endfunction()
+
+function(append_output SUFFICES_FILENAMES TEXT POS0 POS1 FILTER)
+    math(EXPR POS_LENGTH "${POS1}-${POS0}")
+    string(LENGTH "${TEXT}" TEXT_LENGTH)
+    string(SUBSTRING "${TEXT}" "${POS0}" "${POS_LENGTH}" TEXT_APPEND)
+    if (DEBUG)
+        message("appending ${POS0}:${POS1}, length=${POS_LENGTH}")
+    endif()
+    append_output_text("${SUFFICES_FILENAMES}" "${TEXT_APPEND}" "${FILTER}")
+endfunction()
+
+function(append_output_text SUFFICES_FILENAMES TEXT_APPEND FILTER)
+    string(LENGTH "${TEXT_APPEND}" TEXT_LENGTH)
+    list(LENGTH SUFFICES_FILENAMES NB)
+    math(EXPR NB_END "${NB}-1")
+    foreach(INDEX RANGE 0 ${NB_END} 3)
+        math(EXPR INDEX_1 "${INDEX}+1")
+        math(EXPR INDEX_2 "${INDEX}+2")
+        list(GET SUFFICES_FILENAMES ${INDEX} SUFFIX)
+        list(GET SUFFICES_FILENAMES ${INDEX_1} FILENAME)
+        list(GET SUFFICES_FILENAMES ${INDEX_2} TEMPFILENAME)
+        set(WRITE_OK 1)
+        if (FILTER)
+            if (NOT "${SUFFIX}" STREQUAL "${FILTER}")
+                set(WRITE_OK 0)
+            endif()
+        endif()
+        if (WRITE_OK)
+            if (DEBUG)
+                message("Write: ${TEXT_LENGTH} characters to ${FILENAME}")
+            endif()
+            file(APPEND "${TEMPFILENAME}" "${TEXT_APPEND}")
+        endif()
+    endforeach()
+endfunction()
+
+function(output_finish SUFFICES_FILENAMES)
+    list(LENGTH SUFFICES_FILENAMES NB)
+    math(EXPR NB_END "${NB}-1")
+    foreach(INDEX RANGE 0 ${NB_END} 3)
+        math(EXPR INDEX_1 "${INDEX}+1")
+        math(EXPR INDEX_2 "${INDEX}+2")
+        list(GET SUFFICES_FILENAMES ${INDEX_1} FILENAME)
+        list(GET SUFFICES_FILENAMES ${INDEX_2} TEMPFILENAME)
+        file(RENAME "${TEMPFILENAME}" "${FILENAME}")
+    endforeach()
+endfunction()
+
+function(stack_push STACK_ARG)
+    set(STACK_LIST "${${STACK_ARG}}")
+    string(REPLACE ";" " " NEWITEM "${ARGN}")
+    if (DEBUG)
+        list(LENGTH STACK_LIST STACK_LENGTH)
+        message("Pushing \"${NEWITEM}\" onto stack (length=${STACK_LENGTH})")
+    endif()
+    list(APPEND STACK_LIST "${NEWITEM}")
+    set(${STACK_ARG} "${STACK_LIST}" PARENT_SCOPE)
+endfunction()
+
+function(stack_pop STACK_ARG ITEM)
+    set(STACK_LIST "${${STACK_ARG}}")
+    list(LENGTH STACK_LIST STACK_LENGTH)
+    if (STACK_LENGTH EQUAL 0)
+        message(FATAL_ERROR "ENDFOR: stack is empty")
+    endif()
+    math(EXPR LAST_ITEM_INDEX "${STACK_LENGTH}-1")
+    list(GET STACK_LIST "${LAST_ITEM_INDEX}" LAST_ITEM)
+    list(REMOVE_AT STACK_LIST "${LAST_ITEM_INDEX}")
+    string(REPLACE " " ";" LAST_ITEM_LIST "${LAST_ITEM}")
+    if (DEBUG)
+        message("Popping \"${LAST_ITEM}\" from stack (length=${STACK_LENGTH})")
+    endif()
+    set(${ITEM} "${LAST_ITEM_LIST}" PARENT_SCOPE)
+    set(${STACK_ARG} "${STACK_LIST}" PARENT_SCOPE)
+endfunction()
+
+function(stack_top STACK_ARG ITEM)
+    set(STACK_LIST "${${STACK_ARG}}")
+    list(LENGTH STACK_LIST STACK_LENGTH)
+    if (STACK_LENGTH EQUAL 0)
+        message(FATAL_ERROR "ENDFOR: stack is empty")
+    endif()
+    math(EXPR LAST_ITEM_INDEX "${STACK_LENGTH}-1")
+    list(GET STACK_LIST "${LAST_ITEM_INDEX}" LAST_ITEM)
+    string(REPLACE " " ";" LAST_ITEM_LIST "${LAST_ITEM}")
+    if (DEBUG)
+        message("Top of stack: \"${LAST_ITEM}\" from stack (length=${STACK_LENGTH})")
+    endif()
+    set(${ITEM} "${LAST_ITEM_LIST}" PARENT_SCOPE)
+endfunction()
+
+function(stack_find_key STACK_LIST TEMPLATE_PARAMETERS KEY VALUE)
+    list(REVERSE STACK_LIST)
+    foreach(STACK_ITEM ${STACK_LIST})
+        string(REPLACE " " ";" STACK_ITEM_LIST "${STACK_ITEM}")
+        list(GET STACK_ITEM_LIST 3 TP_INDEX)
+        math(EXPR TP_SIZE_INDEX "${TP_INDEX}+1")
+        list(GET TEMPLATE_PARAMETERS ${TP_SIZE_INDEX} TP_SIZE)
+        math(EXPR TP_KV_INDEX_START "${TP_INDEX}+2")
+        math(EXPR TP_KV_INDEX_END "${TP_KV_INDEX_START}+2*${TP_SIZE}-1")
+        foreach(TP_KV_INDEX RANGE ${TP_KV_INDEX_START} ${TP_KV_INDEX_END} 2)
+            list(GET TEMPLATE_PARAMETERS ${TP_KV_INDEX} TP_KEY)
+            if("${TP_KEY}" STREQUAL "${KEY}")
+                math(EXPR TP_VALUE_INDEX "${TP_KV_INDEX}+1")
+                list(GET TEMPLATE_PARAMETERS ${TP_VALUE_INDEX} TP_VALUE)
+                set(${VALUE} "${TP_VALUE}" PARENT_SCOPE)
+                return()
+            endif()
+        endforeach()
+    endforeach()
+    message(FATAL_ERROR "Unknown KEY=${KEY}")
+endfunction()
+
+function(template_parameters_find_next_groupname_index TEMPLATE_PARAMETERS GROUPNAME INDEX_LAST INDEX_NEXT)
+    if (${INDEX_LAST} LESS 0)
+        set(INDEX 0)
+    else ()
+        math(EXPR INDEX_1 "1+(${INDEX_LAST})")
+        list(GET TEMPLATE_PARAMETERS ${INDEX_1} GROUPNAME_INDEX_SIZE)
+        math(EXPR INDEX "${INDEX_LAST}+1+2*${GROUPNAME_INDEX_SIZE}+1")
+    endif()
+    list(LENGTH TEMPLATE_PARAMETERS PARAMETERS_LENGTH)
+    while (${INDEX} LESS ${PARAMETERS_LENGTH})
+        list(GET TEMPLATE_PARAMETERS ${INDEX} GROUPNAME_AT_INDEX)
+        if ("${GROUPNAME}" STREQUAL "${GROUPNAME_AT_INDEX}")
+            set("${INDEX_NEXT}" ${INDEX} PARENT_SCOPE)
+            return()
+        endif()
+        math(EXPR INDEX_1 "${INDEX}+1")
+        list(GET TEMPLATE_PARAMETERS ${INDEX_1} GROUPNAME_INDEX_SIZE)
+        math(EXPR INDEX "${INDEX}+1+2*${GROUPNAME_INDEX_SIZE}+1")
+    endwhile()
+    set("${INDEX_NEXT}" -1 PARENT_SCOPE)
+endfunction()
+
+function(calculate_line_number TEXT POSITION LINENUMBER_ARG)
+    #math(EXPR INDEX_MAX "${POSITION}-1")
+    string(SUBSTRING "${TEXT}" 0 ${POSITION} SUBTEXT)
+    string(REGEX MATCHALL "\n" MATCH_NEWLINES "${SUBTEXT}")
+    list(LENGTH MATCH_NEWLINES NBLINES)
+    math(EXPR NBLINES "${NBLINES}+1")
+    set(${LINENUMBER_ARG} ${NBLINES} PARENT_SCOPE)
+endfunction()
+
+function(parse_template TEMPLATE_FILENAME OUTPUT_DIR TEMPLATE_PARAMETERS)
+    file(READ ${TEMPLATE_FILENAME} TEMPLATE_CONTENTS)
+    set(POSITION 0)
+    match_autogen_group("${TEMPLATE_CONTENTS}" "${POSITION}" POS0 POS1 AUTOGEN FOUND)
+    if (NOT FOUND)
+        message(FATAL_ERROR "Header of template not found")
+    endif()
+    string(REGEX MATCH "AutoGen5 template ([ a-zA-Z0-9]*)" SUFFICES_MATCH "${AUTOGEN}")
+    if (NOT SUFFICES_MATCH)
+        message(FATAL_ERROR "No output suffices found")
+    endif()
+    string(STRIP "${CMAKE_MATCH_1}" SUFFICES)
+    string(REPLACE " " ";" SUFFICES "${SUFFICES}")
+    set(SUFFICES_FILENAMES "")
+    get_filename_component(TEMPLATE_NAME_WE "${TEMPLATE_FILENAME}" NAME_WE)
+    foreach(SUFFIX ${SUFFICES})
+        if ("${OUTPUT_DIR}" STREQUAL "")
+            set(DIR_PREFIX "")
+        else()
+            set(DIR_PREFIX "${OUTPUT_DIR}/")
+        endif()
+        string(RANDOM LENGTH 64 RANDOMSTRING)
+        set(FILENAME "${DIR_PREFIX}${TEMPLATE_NAME_WE}.${SUFFIX}")
+        set(TEMPFILENAME "${DIR_PREFIX}${TEMPLATE_NAME_WE}${RANDOMSTRING}.${SUFFIX}")
+        list(APPEND SUFFICES_FILENAMES "${SUFFIX}" "${FILENAME}" "${TEMPFILENAME}")
+        file(WRITE "${FILENAME}" "")
+    endforeach()
+    if (DEBUG)
+        message("Output files: ${SUFFICES_FILENAMES}")
+    endif()
+    set(WRITE_FILTER "")
+    append_output("${SUFFICES_FILENAMES}" "${TEMPLATE_CONTENTS}" 0 "${POS0}" "${WRITE_FILTER}")
+    math(EXPR POS1 "${POS1}+1")
+    set(POSITION "${POS1}")
+    if (DEBUG)
+        message("Output: ${SUFFICES_FILENAMES}")
+    endif()
+
+    set(STACK "")
+    while (1)
+        match_autogen_group("${TEMPLATE_CONTENTS}" "${POSITION}" POS0 POS1 GROUP_MATCH FOUND)
+        if (NOT FOUND)
+            if (DEBUG)
+                message("No group found. Dumping rest of file.")
+            endif()
+            if (NOT "${STACK}" STREQUAL "")
+                message(FATAL_ERROR "Stack not empty at end of file")
+            endif()
+            string(LENGTH "${TEMPLATE_CONTENTS}" TEXT_LENGTH)
+            append_output("${SUFFICES_FILENAMES}" "${TEMPLATE_CONTENTS}" ${POSITION} ${TEXT_LENGTH} "${WRITE_FILTER}")
+            break()
+        endif()
+        append_output("${SUFFICES_FILENAMES}" "${TEMPLATE_CONTENTS}" ${POSITION} ${POS0} "${WRITE_FILTER}")
+        set(POSITION "${POS1}")
+
+        if (GROUP_MATCH MATCHES "^FOR")
+            string(REPLACE " " ";" GROUP_MATCH_LIST "${GROUP_MATCH}")
+            list(GET GROUP_MATCH_LIST 1 FOR_KEY)
+            template_parameters_find_next_groupname_index("${TEMPLATE_PARAMETERS}" "${FOR_KEY}" -1 FOR_INDEX)
+            if (DEBUG)
+                message("FOR_KEY: ${FOR_KEY}")
+                message("FOR_INDEX: ${FOR_INDEX}")
+            endif()
+            if (${FOR_KEY} LESS 0)
+                message(FATAL_ERROR "FOR has key with empty list. Not implemented yet..")
+            endif()
+            stack_push(STACK FOR ${POSITION} ${FOR_KEY} ${FOR_INDEX})
+        elseif (GROUP_MATCH MATCHES "^ENDFOR")
+            string(REPLACE " " ";" GROUP_MATCH_LIST "${GROUP_MATCH}")
+            list(GET GROUP_MATCH_LIST 1 ENDFOR_KEY)
+            stack_pop(STACK FOR_ITEM)
+            list(GET FOR_ITEM 0 FOR_FOR)
+            if (NOT "${FOR_FOR}" STREQUAL "FOR")
+                message(FATAL_ERROR "ENDFOR does not match last item: ${FOR_FOR}")
+            endif()
+            list(GET FOR_ITEM 1 FOR_POSITION)
+            list(GET FOR_ITEM 2 FOR_KEY)
+            if (NOT "${FOR_KEY}" STREQUAL "${ENDFOR_KEY}")
+                calculate_line_number("${TEMPLATE_CONTENTS}" "${POSITION}" LINENUMBER)
+                message("FOR and ENDFOR do not match. (line number ${LINENUMBER}) (FOR:${FOR_KEY}, ENDFOR:${ENDFOR_KEY})")
+            endif()
+            list(GET FOR_ITEM 3 FOR_INDEX_PREV)
+            template_parameters_find_next_groupname_index("${TEMPLATE_PARAMETERS}" "${FOR_KEY}" ${FOR_INDEX_PREV} FOR_INDEX)
+            if (DEBUG)
+                message("FOR_INDEX was ${FOR_INDEX_PREV}, is now ${FOR_INDEX}")
+            endif()
+            if (${FOR_INDEX} LESS 0)
+                if (DEBUG)
+                    message("ENDFOR: FOR_INDEX < 0 (no more key) ==> Continue")
+                endif()
+            else()
+                set(POSITION ${FOR_POSITION})
+                stack_push(STACK FOR ${FOR_POSITION} ${FOR_KEY} ${FOR_INDEX})
+                if (DEBUG)
+                    message("ENDFOR: FOR_INDEX >= 0 (more keys available) ==> Back to position=${FOR_POSITION}")
+                endif()
+            endif()
+        elseif (GROUP_MATCH MATCHES "^CASE")
+            string(REGEX MATCH "^CASE[${WS}]+\\(([a-zA-Z]+)\\)" CASE_MATCH "${GROUP_MATCH}")
+            if ("${CASE_MATCH}" STREQUAL "")
+                message(FATAL_ERROR "Wrong CASE syntax")
+            endif()
+            set(CASE_KEY "${CMAKE_MATCH_1}")
+            if (DEBUG)
+                message("CASE: KEY=${CASE_KEY}")
+            endif()
+            stack_push(STACK CASE "${CASE_KEY}" ${POSITION})
+        elseif (GROUP_MATCH MATCHES "^==")
+            math(EXPR POSITION "${POSITION}+1")
+            string(REGEX MATCH "^==[${WS}]+([a-zA-Z_][a-zA-Z0-9_]*)" CASE_MATCH "${GROUP_MATCH}")
+            if ("${CASE_MATCH}" STREQUAL "")
+                message(FATAL_ERROR "Wrong == syntax")
+            endif()
+            stack_top(STACK CASE_ITEM)
+            list(GET CASE_ITEM 0 CASE_CASE)
+            if(NOT "${CASE_CASE}" STREQUAL "CASE")
+                message(FATAL_ERROR "== block must be in CASE. Top of stack=${CASE_CASE}")
+            endif()
+            set(CASE_VALUE "${CMAKE_MATCH_1}")
+            if (DEBUG)
+                message("case: == VALUE=${CASE_VALUE}")
+            endif()
+            list(GET CASE_ITEM 1 CASE_KEY)
+            if ("${CASE_KEY}" STREQUAL "suffix")
+                if (DEBUG)
+                    message("Setting write filter to ${CASE_VALUE}")
+                endif()
+                set(WRITE_FILTER "${CASE_VALUE}")
+            else()
+                message(FATAL_ERROR "CASE: unsupported argument ${CASE_KEY}")
+            endif()
+        elseif (GROUP_MATCH MATCHES "^ESAC")
+            stack_pop(STACK CASE_ITEM)
+            if (DEBUG)
+                message("ESAC")
+            endif()
+            list(GET CASE_ITEM 0 CASE_CASE)
+            if (NOT "${CASE_CASE}" STREQUAL "CASE")
+                message(FATAL_ERROR "ESAC does not match last item: ${CASE_CASE}")
+            endif()
+            if ("${CASE_KEY}" STREQUAL "suffix")
+                if (DEBUG)
+                    message("Removing write filter")
+                endif()
+                set(WRITE_FILTER "")
+            else()
+                message(FATAL_ERROR "CASE: unsupported argument ${CASE_KEY}")
+            endif()
+        else()
+            string(REGEX MATCH "\\(([a-zA-Z0-9_$%\"${WS}\\+\\-]+)\\)" PARENTHESE_MATCH "${GROUP_MATCH}")
+            if (NOT "${PARENTHESE_MATCH}" STREQUAL "")
+                set(PARENTHESE_CONTENT "${CMAKE_MATCH_1}")
+                string(REPLACE " " ";" PARENTHESE_LIST "${PARENTHESE_CONTENT}")
+                list(GET PARENTHESE_LIST 0 PARENTHESE_COMMAND)
+                if ("${PARENTHESE_COMMAND}" STREQUAL "get")
+                    list(GET PARENTHESE_LIST 1 KEY_QUOTED)
+                    string(REGEX MATCH "\\\"([a-zA-Z_${WS}]+)\\\"" KEY_MATCH "${KEY_QUOTED}")
+                    if ("${KEY_MATCH}" STREQUAL "")
+                        message(FATAL_ERROR "get: empty key")
+                    endif()
+                    set(KEY "${CMAKE_MATCH_1}")
+                    if (DEBUG)
+                        message("Get: key=${KEY}")
+                    endif()
+                    stack_find_key("${STACK}" "${TEMPLATE_PARAMETERS}" "${KEY}" VALUE)
+                    if (DEBUG)
+                        message("Get key=${KEY} ==> value=${VALUE}")
+                    endif()
+                    append_output_text("${SUFFICES_FILENAMES}" "${VALUE}" "${WRITE_FILTER}")
+                elseif("${PARENTHESE_COMMAND}" STREQUAL "tpl-file-line")
+                    list(GET PARENTHESE_LIST 1 FORMAT_LINE)
+                    calculate_line_number("${TEMPLATE_CONTENTS}" "${POSITION}" LINENUMBER)
+                    append_output_text("${SUFFICES_FILENAMES}" "${LINENUMBER}" "${WRITE_FILTER}")
+                else()
+                    message(FATAL_ERROR "Unknown parenthese command: ${PARENTHESE_COMMAND}")
+                endif()
+            else()
+                message(FATAL_ERROR "Unknown command: ${GROUP_MATCH}")
+            endif()
+        endif()
+
+    endwhile()
+    if (NOT "${STACK}" STREQUAL "")
+        message(FATAL_ERROR "STACK was not empty at EOF")
+    endif()
+    output_finish("${SUFFICES_FILENAMES}")
+endfunction()
+
+if ("${DEFINITION}" STREQUAL "")
+    message(FATAL_ERROR "Need definition file")
+endif()
+if (NOT EXISTS "${DEFINITION}")
+    message(FATAL_ERROR "Definition file does not exist (${DEFINITION})")
+endif()
+
+read_definition("${DEFINITION}" TEMPLATE_FILENAME DATA)
+if (DEBUG)
+    message("${TEMPLATE_FILENAME}")
+    message("${DATA}")
+endif()
+
+parse_template("${TEMPLATE_FILENAME}" "${OUTPUTDIR}" "${DATA}")
diff --git a/cmake/CheckCPUArch.c.in b/cmake/CheckCPUArch.c.in
new file mode 100644
index 0000000..5493139
--- /dev/null
+++ b/cmake/CheckCPUArch.c.in
@@ -0,0 +1,7 @@
+int main(void) {
+#if @CHECK_CPU_ARCH_DEFINES@
+    return 0;
+#else
+    fail
+#endif
+}
diff --git a/cmake/CheckCPUArch.cmake b/cmake/CheckCPUArch.cmake
new file mode 100644
index 0000000..1fdb3c8
--- /dev/null
+++ b/cmake/CheckCPUArch.cmake
@@ -0,0 +1,23 @@
+macro (_CHECK_CPU_ARCH ARCH ARCH_DEFINES VARIABLE)
+	if (NOT DEFINED HAVE_${VARIABLE})
+		message (STATUS "Check CPU architecture is ${ARCH}")
+		set (CHECK_CPU_ARCH_DEFINES ${ARCH_DEFINES})
+		configure_file (${PROJECT_SOURCE_DIR}/cmake/CheckCPUArch.c.in ${PROJECT_BINARY_DIR}/CMakeFiles/CMakeTmp/CheckCPUArch.c @ONLY)
+		try_compile (HAVE_${VARIABLE} "${PROJECT_BINARY_DIR}"
+			"${PROJECT_BINARY_DIR}/CMakeFiles/CMakeTmp/CheckCPUArch.c")
+		if(HAVE_${VARIABLE})
+			message (STATUS "Check CPU architecture is ${ARCH} - yes")
+			set (${VARIABLE} 1 CACHE INTERNAL "Result of CHECK_CPU_ARCH_X64" FORCE)
+		else ()
+			message (STATUS "Check CPU architecture is ${ARCH} - no")
+        endif ()
+    endif ()
+endmacro (_CHECK_CPU_ARCH)
+
+macro (CHECK_CPU_ARCH_X64 VARIABLE)
+    _CHECK_CPU_ARCH (x64 "defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64)" ${VARIABLE})
+endmacro (CHECK_CPU_ARCH_X64)
+
+macro (CHECK_CPU_ARCH_X86 VARIABLE)
+    _CHECK_CPU_ARCH (x86 "defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) ||defined( __i386) || defined(_M_IX86)" ${VARIABLE})
+endmacro (CHECK_CPU_ARCH_X86)
diff --git a/cmake/ClipMode.cmake b/cmake/ClipMode.cmake
new file mode 100644
index 0000000..0241e03
--- /dev/null
+++ b/cmake/ClipMode.cmake
@@ -0,0 +1,92 @@
+include (CheckCSourceRuns)
+include (CMakePushCheckState)
+
+macro (CLIP_MODE)
+	if ((NOT DEFINED CPU_CLIPS_NEGATIVE) AND (NOT DEFINED CPU_CLIPS_POSITIVE))
+		set (CLIP_MODE_POSITIVE_MESSAGE "Target processor clips on positive float to int conversion")
+		set (CLIP_MODE_NEGATIVE_MESSAGE "Target processor clips on negative float to int conversion")
+
+		message (STATUS "Checking processor clipping capabilities...")
+
+		if (CMAKE_CROSSCOMPILING)
+
+			set (CLIP_MSG "disabled")
+			set (CPU_CLIPS_POSITIVE FALSE CACHE BOOL ${CLIP_MODE_POSITIVE_MESSAGE})
+			set (CPU_CLIPS_NEGATIVE FALSE CACHE BOOL ${CLIP_MODE_NEGATIVE_MESSAGE})
+
+		else (NOT CMAKE_CROSSCOMPILING)
+
+			cmake_push_check_state ()
+			
+			set (CMAKE_REQUIRED_QUIET TRUE)
+			if (LIBM_REQUIRED)
+				set (CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${M_LIBRARY})
+			endif ()
+			
+			check_c_source_runs (
+			"
+			#define	_ISOC9X_SOURCE	1
+			#define _ISOC99_SOURCE	1
+			#define	__USE_ISOC99	1
+			#define __USE_ISOC9X	1
+			#include <math.h>
+			int main (void)
+			{	double	fval ;
+				int k, ival ;
+
+				fval = 1.0 * 0x7FFFFFFF ;
+				for (k = 0 ; k < 100 ; k++)
+				{	ival = (lrint (fval)) >> 24 ;
+					if (ival != 127)
+						return 1 ;
+				
+					fval *= 1.2499999 ;
+					} ;
+				
+					return 0 ;
+				}
+			"
+			CPU_CLIPS_POSITIVE)
+			
+			check_c_source_runs (
+			"
+			#define	_ISOC9X_SOURCE	1
+			#define _ISOC99_SOURCE	1
+			#define	__USE_ISOC99	1
+			#define __USE_ISOC9X	1
+			#include <math.h>
+			int main (void)
+			{	double	fval ;
+				int k, ival ;
+
+				fval = -8.0 * 0x10000000 ;
+				for (k = 0 ; k < 100 ; k++)
+				{	ival = (lrint (fval)) >> 24 ;
+					if (ival != -128)
+						return 1 ;
+				
+					fval *= 1.2499999 ;
+					} ;
+				
+					return 0 ;
+				}
+			"
+			CPU_CLIPS_NEGATIVE)
+
+			cmake_pop_check_state ()
+
+			if (CPU_CLIPS_POSITIVE AND (NOT CPU_CLIPS_NEGATIVE))
+				set (CLIP_MSG "positive")
+			elseif (CPU_CLIPS_NEGATIVE AND (NOT CPU_CLIPS_POSITIVE))
+				set (CLIP_MSG "negative")
+			elseif (CPU_CLIPS_POSITIVE AND CPU_CLIPS_NEGATIVE)
+				set (CLIP_MSG "both")
+			else ()
+				set (CLIP_MSG "none")
+			endif ()
+
+		endif (CMAKE_CROSSCOMPILING)
+
+		message (STATUS "Checking processor clipping capabilities... ${CLIP_MSG}")
+	endif ()
+endmacro (CLIP_MODE)
diff --git a/cmake/FindFLAC.cmake b/cmake/FindFLAC.cmake
new file mode 100644
index 0000000..3491706
--- /dev/null
+++ b/cmake/FindFLAC.cmake
@@ -0,0 +1,67 @@
+# - Find FLAC
+# Find the native FLAC includes and libraries
+#
+#  FLAC_INCLUDE_DIRS - where to find FLAC headers.
+#  FLAC_LIBRARIES    - List of libraries when using libFLAC.
+#  FLAC_FOUND        - True if libFLAC found.
+#  FLAC_DEFINITIONS  - FLAC compile definitons 
+
+if (FLAC_INCLUDE_DIR)
+    # Already in cache, be silent
+    set (FLAC_FIND_QUIETLY TRUE)
+endif ()
+
+find_package (Ogg QUIET)
+
+find_package (PkgConfig QUIET)
+pkg_check_modules(PC_FLAC QUIET flac)
+
+set(FLAC_VERSION ${PC_FLAC_VERSION})
+
+find_path (FLAC_INCLUDE_DIR FLAC/stream_decoder.h
+	HINTS
+		${PC_FLAC_INCLUDEDIR}
+		${PC_FLAC_INCLUDE_DIRS}
+		${FLAC_ROOT}
+	)
+
+# MSVC built libraries can name them *_static, which is good as it
+# distinguishes import libraries from static libraries with the same extension.
+find_library (FLAC_LIBRARY
+	NAMES
+		FLAC
+		libFLAC
+		libFLAC_dynamic
+		libFLAC_static
+	HINTS
+		${PC_FLAC_LIBDIR}
+		${PC_FLAC_LIBRARY_DIRS}
+		${FLAC_ROOT}
+	)
+
+# Handle the QUIETLY and REQUIRED arguments and set FLAC_FOUND to TRUE if
+# all listed variables are TRUE.
+include (FindPackageHandleStandardArgs)
+find_package_handle_standard_args (FLAC
+	REQUIRED_VARS
+		FLAC_LIBRARY
+		FLAC_INCLUDE_DIR
+		Ogg_FOUND
+	VERSION_VAR
+        FLAC_VERSION
+	)
+
+if (FLAC_FOUND)
+	set (FLAC_INCLUDE_DIRS ${FLAC_INCLUDE_DIR})
+	set (FLAC_LIBRARIES ${FLAC_LIBRARY} ${OGG_LIBRARIES})
+    if (NOT TARGET FLAC::FLAC)
+		add_library(FLAC::FLAC UNKNOWN IMPORTED)
+		set_target_properties(FLAC::FLAC PROPERTIES
+			INTERFACE_INCLUDE_DIRECTORIES "${FLAC_INCLUDE_DIR}"
+			IMPORTED_LOCATION "${FLAC_LIBRARY}"
+			INTERFACE_LINK_LIBRARIES Ogg::ogg
+			)
+	endif ()
+endif ()
+
+mark_as_advanced(FLAC_INCLUDE_DIR FLAC_LIBRARY)
diff --git a/cmake/FindOgg.cmake b/cmake/FindOgg.cmake
new file mode 100644
index 0000000..9cf5ce4
--- /dev/null
+++ b/cmake/FindOgg.cmake
@@ -0,0 +1,61 @@
+# - Find ogg
+# Find the native ogg includes and libraries
+#
+#  OGG_INCLUDE_DIRS - where to find ogg.h, etc.
+#  OGG_LIBRARIES    - List of libraries when using ogg.
+#  OGG_FOUND        - True if ogg found.
+
+if (OGG_INCLUDE_DIR)
+	# Already in cache, be silent
+	set(OGG_FIND_QUIETLY TRUE)
+endif ()
+
+find_package (PkgConfig QUIET)
+pkg_check_modules (PC_OGG QUIET ogg>=1.3.0)
+
+set (OGG_VERSION ${PC_OGG_VERSION})
+
+find_path (OGG_INCLUDE_DIR ogg/ogg.h
+	HINTS
+		${PC_OGG_INCLUDEDIR}
+		${PC_OGG_INCLUDE_DIRS}
+		${OGG_ROOT}
+	)
+# MSVC built ogg may be named ogg_static.
+# The provided project files name the library with the lib prefix.
+find_library (OGG_LIBRARY
+	NAMES
+		ogg
+		ogg_static
+		libogg
+		libogg_static
+	HINTS
+		${PC_OGG_LIBDIR}
+		${PC_OGG_LIBRARY_DIRS}
+		${OGG_ROOT}
+	)
+# Handle the QUIETLY and REQUIRED arguments and set OGG_FOUND
+# to TRUE if all listed variables are TRUE.
+include (FindPackageHandleStandardArgs)
+find_package_handle_standard_args (Ogg
+	REQUIRED_VARS
+		OGG_LIBRARY
+		OGG_INCLUDE_DIR
+	VERSION_VAR
+		OGG_VERSION
+	)
+
+if (OGG_FOUND)
+	set (OGG_LIBRARIES ${OGG_LIBRARY})
+	set (OGG_INCLUDE_DIRS ${OGG_INCLUDE_DIR})
+	
+	if(NOT TARGET Ogg::ogg)
+	add_library(Ogg::ogg UNKNOWN IMPORTED)
+		set_target_properties(Ogg::ogg PROPERTIES
+			INTERFACE_INCLUDE_DIRECTORIES "${OGG_INCLUDE_DIRS}"
+			IMPORTED_LOCATION "${OGG_LIBRARIES}"
+		)
+  endif ()
+endif ()
+
+mark_as_advanced (OGG_INCLUDE_DIR OGG_LIBRARY)
diff --git a/cmake/FindOpus.cmake b/cmake/FindOpus.cmake
new file mode 100644
index 0000000..a83686b
--- /dev/null
+++ b/cmake/FindOpus.cmake
@@ -0,0 +1,67 @@
+# - Find opus
+# Find the native opus includes and libraries
+#
+#  OPUS_INCLUDE_DIRS - where to find opus.h, etc.
+#  OPUS_LIBRARIES    - List of libraries when using opus.
+#  OPUS_FOUND        - True if Opus found.
+
+if (OPUS_INCLUDE_DIR)
+    # Already in cache, be silent
+    set(OPUS_FIND_QUIETLY TRUE)
+endif ()
+
+find_package (Ogg QUIET)
+
+find_package (PkgConfig QUIET)
+pkg_check_modules(PC_OPUS QUIET opus>=1.1)
+
+set (OPUS_VERSION ${PC_OPUS_VERSION})
+
+find_path (OPUS_INCLUDE_DIR opus/opus.h
+	HINTS
+		${PC_OPUS_INCLUDEDIR}
+		${PC_OPUS_INCLUDE_DIRS}
+		${OPUS_ROOT}
+	)
+
+# MSVC built opus may be named opus_static.
+# The provided project files name the library with the lib prefix.
+
+find_library (OPUS_LIBRARY
+	NAMES
+		opus
+		opus_static
+		libopus
+		libopus_static
+	HINTS
+		${PC_OPUS_LIBDIR}
+		${PC_OPUS_LIBRARY_DIRS}
+		${OPUS_ROOT}
+	)
+
+# Handle the QUIETLY and REQUIRED arguments and set OPUS_FOUND
+# to TRUE if all listed variables are TRUE.
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args (Opus
+	REQUIRED_VARS
+		OPUS_LIBRARY
+		OPUS_INCLUDE_DIR
+		OGG_FOUND
+	VERSION_VAR
+		OPUS_VERSION
+	)
+
+if (OPUS_FOUND)
+	set (OPUS_LIBRARIES ${OPUS_LIBRARY})
+	set (OPUS_INCLUDE_DIRS ${OPUS_INCLUDE_DIR})
+
+	if (NOT TARGET Opus::opus)
+		add_library (Opus::opus UNKNOWN IMPORTED)
+		set_target_properties (Opus::opus PROPERTIES
+			INTERFACE_INCLUDE_DIRECTORIES "${OPUS_INCLUDE_DIRS}"
+			IMPORTED_LOCATION "${OPUS_LIBRARIES}"
+		)
+	endif ()
+endif ()
+
+mark_as_advanced(OPUS_INCLUDE_DIR OPUS_LIBRARY)
diff --git a/cmake/FindSndio.cmake b/cmake/FindSndio.cmake
new file mode 100644
index 0000000..6b9b409
--- /dev/null
+++ b/cmake/FindSndio.cmake
@@ -0,0 +1,61 @@
+# - Find SoundIO (sndio) includes and libraries
+#
+#   SNDIO_FOUND        - True if SNDIO_INCLUDE_DIR & SNDIO_LIBRARY are
+#                        found
+#   SNDIO_LIBRARIES    - Set when SNDIO_LIBRARY is found
+#   SNDIO_INCLUDE_DIRS - Set when SNDIO_INCLUDE_DIR is found
+#
+#   SNDIO_INCLUDE_DIR - where to find sndio.h, etc.
+#   SNDIO_LIBRARY     - the sndio library
+#
+
+if (SNDIO_INCLUDE_DIR)
+	# Already in cache, be silent
+	set (SNDIO_FIND_QUIETLY TRUE)
+endif ()
+
+find_package (PkgConfig QUIET)
+pkg_check_modules (PC_SNDIO QUIET sndio)
+
+set (SNDIO_VERSION ${PC_SNDIO_VERSION})
+
+find_path (SNDIO_INCLUDE_DIR
+	NAMES
+		sndio.h
+	HINTS
+		${PC_SNDIO_INCLUDEDIR}
+		${PC_SNDIO_INCLUDE_DIRS}
+		${SNDIO_ROOT}
+	)
+
+find_library (SNDIO_LIBRARY
+	NAMES
+		sndio
+	HINTS
+		${PC_SNDIO_LIBDIR}
+		${PC_SNDIO_LIBRARY_DIRS}
+		${SNDIO_ROOT}
+	)
+
+include (FindPackageHandleStandardArgs)
+find_package_handle_standard_args (Sndio
+	REQUIRED_VARS
+		SNDIO_LIBRARY
+		SNDIO_INCLUDE_DIR
+	VERSION_VAR
+		SNDIO_VERSION
+	)
+
+if (SNDIO_FOUND)
+	set (SNDIO_LIBRARIES ${SNDIO_LIBRARY})
+	set (SNDIO_INCLUDE_DIRS ${SNDIO_INCLUDE_DIR})
+	if (NOT TARGET Sndio::Sndio)
+		add_library (Sndio::Sndio UNKNOWN IMPORTED)
+		set_target_properties (Sndio::Sndio PROPERTIES
+			INTERFACE_INCLUDE_DIRECTORIES "${SNDIO_INCLUDE_DIRS}"
+			IMPORTED_LOCATION "${SNDIO_LIBRARIES}"
+		)
+	endif()
+endif()
+
+mark_as_advanced (SNDIO_INCLUDE_DIR SNDIO_LIBRARY)
diff --git a/cmake/FindSpeex.cmake b/cmake/FindSpeex.cmake
new file mode 100644
index 0000000..c28fe60
--- /dev/null
+++ b/cmake/FindSpeex.cmake
@@ -0,0 +1,55 @@
+# - Find Speex
+# Find the native Speex includes and libraries
+#
+#	 SPEEX_INCLUDE_DIRS - where to find speex.h, etc.
+#	 SPEEX_LIBRARIES	- List of libraries when using Speex.
+#	 SPEEX_FOUND		- True if Speex found.
+
+if (SPEEX_INCLUDE_DIR)
+	set (SPEEX_FIND_QUIETLY TRUE)
+endif ()
+
+find_package (PkgConfig QUIET)
+pkg_check_modules (PC_SPEEX QUIET speex)
+
+set (SPEEX_VERSION ${PC_SPEEX_VERSION})
+
+find_path (SPEEX_INCLUDE_DIR speex/speex.h
+	HINTS
+		${PC_SPEEX_INCLUDEDIR}
+		${PC_SPEEX_INCLUDE_DIRS}
+		${SPEEX_ROOT}
+	)
+find_library (SPEEX_LIBRARY
+	NAMES
+		speex
+		libspeex
+	HINTS
+		${PC_SPEEX_LIBDIR}
+		${PC_SPEEX_LIBRARY_DIRS}
+		${SPEEX_ROOT}
+	)
+
+include (FindPackageHandleStandardArgs)
+find_package_handle_standard_args (Speex
+	REQUIRED_VARS
+		SPEEX_LIBRARY
+		SPEEX_INCLUDE_DIR
+	VERSION_VAR
+		SPEEX_VERSION
+	)
+
+if (SPEEX_FOUND)
+	set (SPEEX_LIBRARIES ${SPEEX_LIBRARY})
+	set (SPEEX_INCLUDE_DIRS ${SPEEX_INCLUDE_DIR})
+	
+	if (NOT TARGET Speex::Speex)
+		add_library (Speex::Speex UNKNOWN IMPORTED)
+		set_target_properties (Speex::Speex PROPERTIES
+			INTERFACE_INCLUDE_DIRECTORIES "${SPEEX_INCLUDE_DIRS}"
+			IMPORTED_LOCATION "${SPEEX_LIBRARIES}"
+		)
+	endif ()
+endif ()
+
+mark_as_advanced (SPEEX_INCLUDE_DIR SPEEX_LIBRARY)
diff --git a/cmake/FindVorbis.cmake b/cmake/FindVorbis.cmake
new file mode 100644
index 0000000..57e6055
--- /dev/null
+++ b/cmake/FindVorbis.cmake
@@ -0,0 +1,210 @@
+#[=======================================================================[.rst:
+FindVorbis
+----------
+
+Finds the native vorbis, vorbisenc amd vorbisfile includes and libraries.
+
+Imported Targets
+^^^^^^^^^^^^^^^^
+
+This module provides the following imported targets, if found:
+
+``Vorbis::vorbis``
+  The Vorbis library
+``Vorbis::vorbisenc``
+  The VorbisEnc library
+``Vorbis::vorbisfile``
+  The VorbisFile library
+
+Result Variables
+^^^^^^^^^^^^^^^^
+
+This will define the following variables:
+
+``Vorbis_Vorbis_INCLUDE_DIRS``
+  List of include directories when using vorbis.
+``Vorbis_Enc_INCLUDE_DIRS``
+  List of include directories when using vorbisenc.
+``Vorbis_File_INCLUDE_DIRS``
+  List of include directories when using vorbisfile.
+``Vorbis_Vorbis_LIBRARIES``
+  List of libraries when using vorbis.
+``Vorbis_Enc_LIBRARIES``
+  List of libraries when using vorbisenc.
+``Vorbis_File_LIBRARIES``
+  List of libraries when using vorbisfile.
+``Vorbis_FOUND``
+  True if vorbis and requested components found.
+``Vorbis_Vorbis_FOUND``
+  True if vorbis found.
+``Vorbis_Enc_FOUND``
+  True if vorbisenc found.
+``Vorbis_Enc_FOUND``
+  True if vorbisfile found.
+
+Cache variables
+^^^^^^^^^^^^^^^
+
+The following cache variables may also be set:
+
+``Vorbis_Vorbis_INCLUDE_DIR``
+  The directory containing ``vorbis/vorbis.h``.
+``Vorbis_Enc_INCLUDE_DIR``
+  The directory containing ``vorbis/vorbisenc.h``.
+``Vorbis_File_INCLUDE_DIR``
+  The directory containing ``vorbis/vorbisenc.h``.
+``Vorbis_Vorbis_LIBRARY``
+  The path to the vorbis library.
+``Vorbis_Enc_LIBRARY``
+  The path to the vorbisenc library.
+``Vorbis_File_LIBRARY``
+  The path to the vorbisfile library.
+
+Hints
+^^^^^
+
+A user may set ``Vorbis_ROOT`` to a vorbis installation root to tell this module where to look.
+
+#]=======================================================================]
+
+if (Vorbis_Vorbis_INCLUDE_DIR)
+	# Already in cache, be silent
+	set (Vorbis_FIND_QUIETLY TRUE)
+endif ()
+
+set (Vorbis_Vorbis_FIND_QUIETLY TRUE)
+set (Vorbis_Enc_FIND_QUIETLY TRUE)
+set (Vorbis_File_FIND_QUIETLY TRUE)
+
+find_package (Ogg QUIET)
+
+find_package (PkgConfig QUIET)
+pkg_check_modules (PC_Vorbis_Vorbis QUIET vorbis)
+pkg_check_modules (PC_Vorbis_Enc QUIET vorbisenc)
+pkg_check_modules (PC_Vorbis_File QUIET vorbisfile)
+
+set (Vorbis_VERSION ${PC_Vorbis_Vorbis_VERSION})
+
+find_path (Vorbis_Vorbis_INCLUDE_DIR vorbis/codec.h
+	HINTS
+		${PC_Vorbis_Vorbis_INCLUDEDIR}
+		${PC_Vorbis_Vorbis_INCLUDE_DIRS}
+		${Vorbis_ROOT}
+	)
+
+find_path (Vorbis_Enc_INCLUDE_DIR vorbis/vorbisenc.h
+	HINTS
+		${PC_Vorbis_Enc_INCLUDEDIR}
+		${PC_Vorbis_Enc_INCLUDE_DIRS}
+		${Vorbis_ROOT}
+	)
+
+find_path (Vorbis_File_INCLUDE_DIR vorbis/vorbisfile.h
+	HINTS
+		${PC_Vorbis_File_INCLUDEDIR}
+		${PC_Vorbis_File_INCLUDE_DIRS}
+		${Vorbis_ROOT}
+	)
+
+find_library (Vorbis_Vorbis_LIBRARY
+	NAMES
+		vorbis
+		vorbis_static
+		libvorbis
+		libvorbis_static
+	HINTS
+		${PC_Vorbis_Vorbis_LIBDIR}
+		${PC_Vorbis_Vorbis_LIBRARY_DIRS}
+		${Vorbis_ROOT}
+	)
+
+find_library (Vorbis_Enc_LIBRARY
+	NAMES
+		vorbisenc
+		vorbisenc_static
+		libvorbisenc
+		libvorbisenc_static
+	HINTS
+		${PC_Vorbis_Enc_LIBDIR}
+		${PC_Vorbis_Enc_LIBRARY_DIRS}
+		${Vorbis_ROOT}
+	)
+
+find_library (Vorbis_File_LIBRARY
+	NAMES
+		vorbisfile
+		vorbisfile_static
+		libvorbisfile
+		libvorbisfile_static
+	HINTS
+		${PC_Vorbis_File_LIBDIR}
+		${PC_Vorbis_File_LIBRARY_DIRS}
+		${Vorbis_ROOT}
+	)
+
+include (FindPackageHandleStandardArgs)
+
+if (Vorbis_Vorbis_LIBRARY AND Vorbis_Vorbis_INCLUDE_DIR AND Ogg_FOUND)
+    set (Vorbis_Vorbis_FOUND TRUE)
+endif ()
+
+if (Vorbis_Enc_LIBRARY AND Vorbis_Enc_INCLUDE_DIR AND Vorbis_Vorbis_FOUND)
+    set (Vorbis_Enc_FOUND TRUE)
+endif ()
+
+if (Vorbis_Vorbis_FOUND AND Vorbis_File_LIBRARY AND Vorbis_File_INCLUDE_DIR)
+    set (Vorbis_File_FOUND TRUE)
+endif ()
+
+find_package_handle_standard_args (Vorbis
+	REQUIRED_VARS
+		Vorbis_Vorbis_LIBRARY
+		Vorbis_Vorbis_INCLUDE_DIR
+		Ogg_FOUND
+	HANDLE_COMPONENTS
+	VERSION_VAR Vorbis_VERSION)
+
+
+if (Vorbis_Vorbis_FOUND)
+	set (Vorbis_Vorbis_INCLUDE_DIRS ${VORBIS_INCLUDE_DIR})
+	set (Vorbis_Vorbis_LIBRARIES ${VORBIS_LIBRARY} ${OGG_LIBRARIES})
+    if (NOT TARGET Vorbis::vorbis)
+		add_library (Vorbis::vorbis UNKNOWN IMPORTED)
+		set_target_properties (Vorbis::vorbis PROPERTIES
+			INTERFACE_INCLUDE_DIRECTORIES "${Vorbis_Vorbis_INCLUDE_DIR}"
+			IMPORTED_LOCATION "${Vorbis_Vorbis_LIBRARY}"
+			INTERFACE_LINK_LIBRARIES Ogg::ogg
+		)
+	endif ()
+
+	if (Vorbis_Enc_FOUND)
+		set (Vorbis_Enc_INCLUDE_DIRS ${Vorbis_Enc_INCLUDE_DIR})
+		set (Vorbis_Enc_LIBRARIES ${Vorbis_Enc_LIBRARY} ${Vorbis_Enc_LIBRARIES})
+		if (NOT TARGET Vorbis::vorbisenc)
+			add_library (Vorbis::vorbisenc UNKNOWN IMPORTED)
+			set_target_properties (Vorbis::vorbisenc PROPERTIES
+				INTERFACE_INCLUDE_DIRECTORIES "${Vorbis_Enc_INCLUDE_DIR}"
+				IMPORTED_LOCATION "${Vorbis_Enc_LIBRARY}"
+				INTERFACE_LINK_LIBRARIES Vorbis::vorbis
+			)
+		endif ()
+	endif ()
+
+	if (Vorbis_File_FOUND)
+		set (Vorbis_File_INCLUDE_DIRS ${Vorbis_File_INCLUDE_DIR})
+		set (Vorbis_File_LIBRARIES ${Vorbis_File_LIBRARY} ${Vorbis_File_LIBRARIES})
+		if (NOT TARGET Vorbis::vorbisfile)
+			add_library (Vorbis::vorbisfile UNKNOWN IMPORTED)
+			set_target_properties (Vorbis::vorbisfile PROPERTIES
+				INTERFACE_INCLUDE_DIRECTORIES "${Vorbis_File_INCLUDE_DIR}"
+				IMPORTED_LOCATION "${Vorbis_File_LIBRARY}"
+				INTERFACE_LINK_LIBRARIES Vorbis::vorbis
+			)
+		endif ()
+	endif ()
+
+endif ()
+
+mark_as_advanced (Vorbis_Vorbis_INCLUDE_DIR Vorbis_Vorbis_LIBRARY)
+mark_as_advanced (Vorbis_Enc_INCLUDE_DIR Vorbis_Enc_LIBRARY)
+mark_as_advanced (Vorbis_File_INCLUDE_DIR Vorbis_File_LIBRARY)
diff --git a/cmake/Findmp3lame.cmake b/cmake/Findmp3lame.cmake
new file mode 100644
index 0000000..223dd08
--- /dev/null
+++ b/cmake/Findmp3lame.cmake
@@ -0,0 +1,67 @@
+# - Find lame
+# Find the native lame includes and libraries
+#
+#  MP3LAME_INCLUDE_DIRS - where to find lame.h, etc.
+#  MP3LAME_LIBRARIES    - List of libraries when using lame.
+#  MP3LAME_FOUND        - True if Lame found.
+
+if (MP3LAME_INCLUDE_DIR)
+    # Already in cache, be silent
+    set(MP3LAME_FIND_QUIETLY TRUE)
+endif ()
+
+find_path (MP3LAME_INCLUDE_DIR lame/lame.h
+	HINTS
+		${LAME_ROOT}
+	)
+
+# MSVC built lame may be named mp3lame_static.
+# The provided project files name the library with the lib prefix.
+
+find_library (MP3LAME_LIBRARY
+	NAMES
+		mp3lame
+		mp3lame_static
+		libmp3lame
+		libmp3lame_static
+		libmp3lame-static
+	HINTS
+		${MP3LAME_ROOT}
+	)
+
+find_library (MP3LAME_HIP_LIBRARY
+	NAMES
+		mpghip-static
+		libmpghip-static
+	HINTS
+		${MP3LAME_ROOT}
+	)
+
+# Handle the QUIETLY and REQUIRED arguments and set LAME_FOUND
+# to TRUE if all listed variables are TRUE.
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args (mp3lame
+	REQUIRED_VARS
+		MP3LAME_LIBRARY
+		MP3LAME_INCLUDE_DIR
+	)
+
+if (MP3LAME_FOUND)
+	set (MP3LAME_LIBRARIES ${MP3LAME_LIBRARY} ${MP3LAME_HIP_LIBRARY})
+	set (MP3LAME_INCLUDE_DIRS ${MP3LAME_INCLUDE_DIR})
+
+	if (NOT TARGET mp3lame::mp3lame)
+		add_library (mp3lame::mp3lame UNKNOWN IMPORTED)
+		set_target_properties (mp3lame::mp3lame PROPERTIES
+			INTERFACE_INCLUDE_DIRECTORIES "${MP3LAME_INCLUDE_DIRS}"
+			IMPORTED_LOCATION "${MP3LAME_LIBRARY}"
+		)
+		if (MP3LAME_HIP_LIBRARY AND (NOT TARGET mp3lame::mpghip))
+			add_library (mp3lame::mpghip STATIC IMPORTED)
+			set_property (mp3lame::mpghip PROPERTY IMPORTED_LOCATION "${MP3LAME_HIP_LIBRARY}")
+			set_property (TARGET mp3lame::mp3lame PROPERTY INTERFACE_LINK_LIBRARIES "mp3lame::mpghip")
+		endif ()
+	endif ()
+endif ()
+
+mark_as_advanced(MP3LAME_INCLUDE_DIR MP3LAME_LIBRARY MP3LAME_HIP_LIBRARY)
diff --git a/cmake/Findmpg123.cmake b/cmake/Findmpg123.cmake
new file mode 100644
index 0000000..36bb238
--- /dev/null
+++ b/cmake/Findmpg123.cmake
@@ -0,0 +1,95 @@
+#[=======================================================================[.rst:
+Findmpg123
+-------
+
+Finds the mpg123 library.
+
+Imported Targets
+^^^^^^^^^^^^^^^^
+
+This module provides the following imported targets, if found:
+
+``MPG123::libmpg123``
+  The mpg123 library
+
+Result Variables
+^^^^^^^^^^^^^^^^
+
+This will define the following variables:
+
+``mpg123_FOUND``
+  True if the system has the mpg123 package.
+``mpg123_VERSION``
+	The version of mpg123 that was found on the system.
+
+Cache Variables
+^^^^^^^^^^^^^^^
+
+The following cache variables may also be set:
+
+``mpg123_INCLUDE_DIR``
+  The directory containing ``mpg123.h``.
+``mpg123_LIBRARY``
+  The path to the mpg123 library.
+
+#]=======================================================================]
+
+if (mpg123_INCLUDE_DIR)
+    # Already in cache, be silent
+    set(mpg123_FIND_QUIETLY TRUE)
+endif ()
+
+find_package (PkgConfig QUIET)
+pkg_check_modules(PC_MPG123 QUIET libmpg123>=1.25.10)
+
+find_path (mpg123_INCLUDE_DIR mpg123.h
+	HINTS
+		${PC_MPG123_INCLUDEDIR}
+		${PC_MPG123_INCLUDE_DIRS}
+		${mpg123_ROOT}
+	)
+
+# MSVC built mpg123 may be named mpg123_static.
+# The provided project files name the library with the lib prefix.
+
+find_library (mpg123_LIBRARY
+	NAMES
+		mpg123
+		mpg123_static
+		libmpg123
+		libmpg123_static
+	HINTS
+		${PC_MPG123_LIBDIR}
+		${PC_MPG123_LIBRARY_DIRS}
+		${mpg123_ROOT}
+	)
+
+if (PC_MPG123_FOUND)
+	set (mpg123_VERSION ${PC_MPG123_VERSION})
+elseif (mpg123_INCLUDE_DIR)
+	file (READ "${mpg123_INCLUDE_DIR}/mpg123.h" _mpg123_h)
+	string (REGEX MATCH "[0-9]+.[0-9]+.[0-9]+" _mpg123_version_re "${_mpg123_h}")
+	set (mpg123_VERSION "${_mpg123_version_re}")
+endif ()
+
+# Handle the QUIETLY and REQUIRED arguments and set mpg123_FOUND
+# to TRUE if all listed variables are TRUE.
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args (mpg123
+	REQUIRED_VARS
+		mpg123_LIBRARY
+		mpg123_INCLUDE_DIR
+	VERSION_VAR
+		mpg123_VERSION
+	)
+
+if (mpg123_FOUND AND NOT TARGET MPG123::libmpg123)
+	add_library (MPG123::libmpg123 UNKNOWN IMPORTED)
+	set_target_properties (MPG123::libmpg123
+		PROPERTIES 
+			IMPORTED_LOCATION "${mpg123_LIBRARY}"
+			INTERFACE_INCLUDE_DIRECTORIES "${mpg123_INCLUDE_DIR}"
+		)
+endif ()
+
+mark_as_advanced(mpg123_INCLUDE_DIR mpg123_LIBRARY)
diff --git a/cmake/SetupABIVersions.cmake b/cmake/SetupABIVersions.cmake
new file mode 100644
index 0000000..331a2bd
--- /dev/null
+++ b/cmake/SetupABIVersions.cmake
@@ -0,0 +1,56 @@
+# Inspiration: https://github.com/ros2-dotnet/Fast-RTPS
+
+macro (SETUP_ABI_VERSIONS)
+
+    file (STRINGS ${PROJECT_SOURCE_DIR}/configure.ac CONFIGURE_AC_CONTENT)
+    file (STRINGS
+        configure.ac
+        SNDFILE_LT_CURRENT_TMP
+        REGEX "^m4_define\\(\\[?lt_current\\]?, *\\[?[0-9]+\\]?\\)"
+        )
+    string (REGEX REPLACE "m4_define\\(\\[?lt_current\\]?, *\\[?([0-9]+)\\]?\\)"
+        "\\1"
+        SNDFILE_LT_CURRENT
+        ${SNDFILE_LT_CURRENT_TMP}
+        )
+
+    file (STRINGS
+        configure.ac
+        SNDFILE_LT_REVISION_TMP
+        REGEX "^m4_define\\(\\[?lt_revision\\]?, *\\[?[0-9]+\\]?\\)"
+        )
+    string (REGEX REPLACE "m4_define\\(\\[?lt_revision\\]?, *\\[?([0-9]+)\\]?\\)"
+        "\\1"
+        SNDFILE_LT_REVISION
+        ${SNDFILE_LT_REVISION_TMP}
+        )
+
+    file (STRINGS
+        configure.ac
+        SNDFILE_LT_AGE_TMP
+        REGEX "^m4_define\\(\\[?lt_age\\]?, *\\[?[0-9]+\\]?\\)"
+        )
+    string (REGEX REPLACE "m4_define\\(\\[?lt_age\\]?, *\\[?([0-9]+)\\]?\\)"
+        "\\1"
+        SNDFILE_LT_AGE
+        ${SNDFILE_LT_AGE_TMP}
+        )
+
+    #
+    # Calculate CMake compatible ABI version from libtool version.
+    #
+
+    math (EXPR SNDFILE_ABI_VERSION_MAJOR "${SNDFILE_LT_CURRENT} - ${SNDFILE_LT_AGE}")
+    set (SNDFILE_ABI_VERSION_MINOR ${SNDFILE_LT_AGE})
+    set (SNDFILE_ABI_VERSION_PATCH ${SNDFILE_LT_REVISION})
+    set (SNDFILE_ABI_VERSION "${SNDFILE_ABI_VERSION_MAJOR}.${SNDFILE_ABI_VERSION_MINOR}.${SNDFILE_ABI_VERSION_PATCH}")
+
+    #
+    # Apple platform current and compatibility versions.
+    #
+
+    math (EXPR SNDFILE_MACHO_CURRENT_VERSION_MAJOR "${SNDFILE_ABI_VERSION_MAJOR} + ${SNDFILE_ABI_VERSION_MINOR} + 1")
+    set (SNDFILE_MACHO_CURRENT_VERSION "${SNDFILE_MACHO_CURRENT_VERSION_MAJOR}.${SNDFILE_ABI_VERSION_PATCH}.0")
+    set (SNDFILE_MACHO_COMPATIBILITY_VERSION "${SNDFILE_MACHO_CURRENT_VERSION_MAJOR}.0.0")
+
+endmacro (SETUP_ABI_VERSIONS)
diff --git a/cmake/SndFileChecks.cmake b/cmake/SndFileChecks.cmake
new file mode 100644
index 0000000..2ad8ccd
--- /dev/null
+++ b/cmake/SndFileChecks.cmake
@@ -0,0 +1,256 @@
+include (CheckFunctionExists)
+include (CheckIncludeFile)
+include (CheckLibraryExists)
+include (CheckSymbolExists)
+include (CheckTypeSize)
+include (TestBigEndian)
+
+include (TestInline)
+include (ClipMode)
+include (TestLargeFiles)
+include (CheckCPUArch)
+
+test_large_files (_LARGEFILES)
+
+if (LARGE_FILES_DEFINITIONS)
+	add_definitions(${LARGE_FILES_DEFINITIONS})
+endif ()
+
+if (CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
+	find_package (Sndio)
+elseif (NOT WIN32)
+	find_package (ALSA)
+endif ()
+
+if (VCPKG_TOOLCHAIN AND (NOT CMAKE_VERSION VERSION_LESS 3.15))
+	set (CMAKE_FIND_PACKAGE_PREFER_CONFIG ON)
+endif ()
+
+if (CMAKE_FIND_PACKAGE_PREFER_CONFIG)
+	find_package (Ogg 1.3 CONFIG)
+	find_package (Vorbis CONFIG COMPONENTS Enc)
+	find_package (FLAC CONFIG)
+	find_package (Opus CONFIG)
+
+	include (FindPackageHandleStandardArgs)
+	find_package_handle_standard_args (Ogg CONFIG_MODE)
+	find_package_handle_standard_args (Vorbis CONFIG_MODE)
+	find_package_handle_standard_args (FLAC CONFIG_MODE)
+	find_package_handle_standard_args (Opus CONFIG_MODE)
+else ()
+	find_package (Ogg 1.3)
+	find_package (Vorbis COMPONENTS Enc)
+	find_package (FLAC)
+	find_package (Opus)
+endif ()
+if (Vorbis_FOUND AND FLAC_FOUND AND Opus_FOUND)
+	set (HAVE_EXTERNAL_XIPH_LIBS 1)
+else ()
+	set (HAVE_EXTERNAL_XIPH_LIBS 0)
+endif ()
+
+find_package (mp3lame)
+find_package (mpg123 1.25.10)
+if (TARGET mp3lame::mp3lame AND (TARGET MPG123::libmpg123))
+	set (HAVE_MPEG_LIBS 1)
+else ()
+	set (HAVE_MPEG_LIBS 0)
+endif()
+
+find_package (Speex)
+find_package (SQLite3)
+
+check_include_file (byteswap.h		HAVE_BYTESWAP_H)
+check_include_file (dlfcn.h			HAVE_DLFCN_H)
+check_include_file (direct.h		HAVE_DIRECT_H)
+check_include_file (endian.h		HAVE_ENDIAN_H)
+check_include_file (inttypes.h		HAVE_INTTYPES_H)
+check_include_file (io.h			HAVE_IO_H)
+check_include_file (stdint.h		HAVE_STDINT_H)
+check_include_file (sys/time.h		HAVE_SYS_TIME_H)
+check_include_file (sys/types.h		HAVE_SYS_TYPES_H)
+check_include_file (unistd.h		HAVE_UNISTD_H)
+check_include_file (immintrin.h		HAVE_IMMINTRIN_H)
+check_include_file (stdbool.h		HAVE_STDBOOL_H)
+
+check_cpu_arch_x86 (CPU_IS_X86)
+check_cpu_arch_x64 (CPU_IS_X64)
+if ((CPU_IS_X86 OR CPU_IS_X64) AND HAVE_IMMINTRIN_H)
+	set (HAVE_SSE2 1)
+endif ()
+
+# Never checked
+# check_include_file (stdlib.h		HAVE_STDLIB_H)
+# check_include_file (string.h		HAVE_STRING_H)
+# check_include_file (strings.h		HAVE_STRINGS_H)
+# check_include_file (sys/stat.h	HAVE_SYS_STAT_H)
+# check_include_file (memory.h		HAVE_MEMORY_H)
+
+if (BUILD_TESTING)
+	check_include_file (locale.h	HAVE_LOCALE_H)
+	check_include_file (sys/wait.h	HAVE_SYS_WAIT_H)
+endif ()
+
+check_type_size (int64_t			SIZEOF_INT64_T)
+check_type_size (long				SIZEOF_LONG)
+check_type_size (long\ long			SIZEOF_LONG_LONG)
+check_type_size (ssize_t			SIZEOF_SSIZE_T)
+check_type_size (wchar_t			SIZEOF_WCHAR_T)
+
+# Never used
+# check_type_size (loff_t			SIZEOF_LOFF_T)
+# check_type_size (offt64_t			SIZEOF_OFF64_T)
+
+# Never checked
+# check_type_size (size_t			SIZEOF_SIZE_T)
+
+# Used in configre.ac
+# check_type_size (double			SIZEOF_DOUBLE)
+# check_type_size (float			SIZEOF_FLOAT)
+# check_type_size (int				SIZEOF_INT)
+# check_type_size (short			SIZEOF_SHORT)
+
+if (ENABLE_TESTING)
+	check_type_size (void*			SIZEOF_VOIDP)
+endif()
+
+if (NOT WIN32)
+	check_library_exists (m floor "" LIBM_REQUIRED)
+	if (LIBM_REQUIRED)
+		list (APPEND CMAKE_REQUIRED_LIBRARIES m)
+	endif ()
+endif ()
+
+check_library_exists (sqlite3 sqlite3_close "" HAVE_SQLITE3)
+
+check_function_exists (fstat     		HAVE_FSTAT)
+check_function_exists (fstat64			HAVE_FSTAT64)
+check_function_exists (gettimeofday		HAVE_GETTIMEOFDAY)
+check_function_exists (gmtime			HAVE_GMTIME)
+check_function_exists (gmtime_r			HAVE_GMTIME_R)
+check_function_exists (localtime		HAVE_LOCALTIME)
+check_function_exists (localtime_r		HAVE_LOCALTIME_R)
+check_function_exists (lseek      		HAVE_LSEEK)
+check_function_exists (open				HAVE_OPEN)
+check_function_exists (read				HAVE_READ)
+check_function_exists (write			HAVE_WRITE)
+check_function_exists (lrint			HAVE_LRINT)
+check_function_exists (lrintf			HAVE_LRINTF)
+
+if (NOT WIN32)
+	check_function_exists (ftruncate	HAVE_FTRUNCATE)
+	check_function_exists (fsync    	HAVE_FSYNC)
+endif ()
+
+if (BUILD_TESTING)
+	check_function_exists (pipe			HAVE_PIPE)
+	check_function_exists (setlocale	HAVE_SETLOCALE)
+	check_function_exists (waitpid		HAVE_WAITPID)
+endif ()
+
+# Never checked
+# check_function_exists (calloc			HAVE_CALLOC)
+# check_function_exists (free			HAVE_FREE)
+# check_function_exists (getpagesize	HAVE_GETPAGESIZE)
+# check_function_exists (malloc			HAVE_MALLOC)
+# check_function_exists (realloc		HAVE_REALLOC)
+# check_function_exists (snprintf		HAVE_SNPRINTF)
+# check_function_exists (vsnprintf		HAVE_VSNPRINTF)
+# check_function_exists (floor			HAVE_FLOOR)
+# check_function_exists (fmod			HAVE_FMOD)
+
+# Never used
+# check_function_exists (mmap			HAVE_MMAP)
+# check_function_exists (ceil			HAVE_CEIL)
+# check_function_exists (lround			HAVE_LROUND)
+# check_function_exists (lseek64		HAVE_LSEEK64)
+
+
+check_symbol_exists (S_IRGRP sys/stat.h HAVE_DECL_S_IRGRP)
+
+test_big_endian (WORDS_BIGENDIAN)
+if (WORDS_BIGENDIAN)
+	set (CPU_IS_BIG_ENDIAN 1)
+else ()
+	set (CPU_IS_LITTLE_ENDIAN 1)
+endif ()
+
+if (WIN32)
+	set (OS_IS_WIN32 1)
+	set (USE_WINDOWS_API 1)
+	if (BUILD_SHARED_LIBS)
+		set (WIN32_TARGET_DLL 1)
+	endif ()
+	if (MINGW)
+		add_definitions (-D__USE_MINGW_ANSI_STDIO=1)
+	endif ()
+endif ()
+
+if (CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
+	set (OS_IS_OPENBSD 1)
+endif ()
+
+
+if (CMAKE_COMPILER_IS_GNUCC OR (CMAKE_C_COMPILER_ID MATCHES "Clang"))
+	set (COMPILER_IS_GCC 1)
+endif ()
+
+test_inline ()
+clip_mode ()
+
+if (MSVC)
+	add_definitions (-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE)
+endif (MSVC)
+
+if (DEFINED ENABLE_STATIC_RUNTIME)
+	if (MSVC)
+		if (ENABLE_STATIC_RUNTIME)
+			foreach (flag_var
+				CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
+				CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO
+				CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE
+				CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO
+				)
+				if (${flag_var} MATCHES "/MD")
+					string (REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
+				endif ()
+			endforeach (flag_var)
+		else ()
+			foreach (flag_var
+				CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
+				CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO
+				CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE
+				CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO
+				)
+				if (${flag_var} MATCHES "/MT")
+					string (REGEX REPLACE "/MT" "/MD" ${flag_var} "${${flag_var}}")
+				endif (${flag_var} MATCHES "/MT")
+			endforeach (flag_var)
+		endif ( )
+	elseif (MINGW)
+		if (ENABLE_STATIC_RUNTIME)
+			if (CMAKE_C_COMPILER_ID STREQUAL GNU)
+				set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static-libgcc")
+				set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS} -static-libgcc -s")
+			endif ()
+			if (CMAKE_CXX_COMPILER_ID STREQUAL GNU)
+				set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++")
+				set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "${CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS} -static-libgcc -static-libstdc++ -s")
+			endif ()
+			if (CMAKE_C_COMPILER_ID STREQUAL Clang)
+				set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static")
+				set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS} -static")
+			endif ()
+			if (CMAKE_CXX_COMPILER_ID STREQUAL Clang)
+				set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static")
+				set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "${CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS} -static")
+			endif ()
+		endif ()
+	else ()
+		message (AUTHOR_WARNING "ENABLE_STATIC_RUNTIME option is for MSVC or MinGW only.")
+	endif ()
+endif ()
+
+if (BUILD_SHARED_LIBS)
+	find_package (PythonInterp REQUIRED)
+endif()
diff --git a/cmake/SndFileConfig.cmake.in b/cmake/SndFileConfig.cmake.in
new file mode 100644
index 0000000..f392335
--- /dev/null
+++ b/cmake/SndFileConfig.cmake.in
@@ -0,0 +1,46 @@
+set(SndFile_VERSION @PROJECT_VERSION@)
+set(SndFile_VERSION_MAJOR @PROJECT_VERSION_MAJOR@)
+set(SndFile_VERSION_MINOR @PROJECT_VERSION_MINOR@)
+set(SndFile_VERSION_PATCH @PROJECT_VERSION_PATCH@)
+
+set (SndFile_WITH_EXTERNAL_LIBS @SndFile_WITH_EXTERNAL_LIBS@)
+set (SndFile_WITH_MPEG @SndFile_WITH_MPEG@)
+
+@PACKAGE_INIT@
+
+include (CMakeFindDependencyMacro)
+
+if (NOT @BUILD_SHARED_LIBS@)
+	list (APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR})
+endif ()
+
+if (SndFile_WITH_EXTERNAL_LIBS AND NOT @BUILD_SHARED_LIBS@)
+	find_dependency (Ogg 1.3)
+	find_dependency (Vorbis)
+	find_dependency (FLAC)
+	find_dependency (Opus)
+endif ()
+
+if (SndFile_WITH_MPEG AND NOT @BUILD_SHARED_LIBS@)
+	find_dependency (mp3lame)
+	find_dependency (mpg123)
+endif ()
+
+if (NOT @BUILD_SHARED_LIBS@)
+	list (REMOVE_ITEM CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR})
+endif ()
+
+include (${CMAKE_CURRENT_LIST_DIR}/SndFileTargets.cmake)
+
+set_and_check (SndFile_INCLUDE_DIR "@PACKAGE_INCLUDE_INSTALL_DIR@")
+set (SNDFILE_INCLUDE_DIR ${SndFile_INCLUDE_DIR})
+
+set (SndFile_LIBRARY SndFile::sndfile)
+set (SNDFILE_LIBRARY SndFile::sndfile)
+set (SndFile_LIBRARIES SndFile::sndfile)
+set (SNDFILE_LIBRARIES SndFile::sndfile)
+
+
+check_required_components(SndFile)
+
+set (SNDFILE_FOUND 1)
diff --git a/cmake/TestInline.c.in b/cmake/TestInline.c.in
new file mode 100644
index 0000000..2aed643
--- /dev/null
+++ b/cmake/TestInline.c.in
@@ -0,0 +1,10 @@
+static @INLINE_KEYWORD@ void test_inline(void)
+{
+	return;
+}
+
+int main (void)
+{
+	test_inline ();
+	return 0;
+}
diff --git a/cmake/TestInline.cmake b/cmake/TestInline.cmake
new file mode 100644
index 0000000..ef15c07
--- /dev/null
+++ b/cmake/TestInline.cmake
@@ -0,0 +1,54 @@
+macro (TEST_INLINE)
+	if (NOT DEFINED INLINE_CODE)
+		message (STATUS "Checking for inline...")
+		set (INLINE_KEYWORD "inline")
+		configure_file (cmake/TestInline.c.in ${PROJECT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/TestInline.c)
+		try_compile (HAVE_INLINE "${CMAKE_CURRENT_BINARY_DIR}"
+                    "${PROJECT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/TestInline.c")
+		if (HAVE_INLINE)
+			message (STATUS "Checking for inline... supported")
+		else ()
+			message (STATUS "Checking for inline... not supported")
+			
+			message (STATUS "Checking for __inline...")
+			set (INLINE_KEYWORD "__inline")
+			configure_file (cmake/TestInline.c.in ${PROJECT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/TestInline.c)
+			try_compile (HAVE___INLINE "${CMAKE_CURRENT_BINARY_DIR}"
+                    "${PROJECT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/TestInline.c")
+			if (HAVE___INLINE)
+				message (STATUS "Checking for __inline... supported")
+			else ()
+				message (STATUS "Checking for __inline... not supported")
+				
+				message (STATUS "Checking for __inline__...")
+				set (INLINE_KEYWORD "__inline__")
+				configure_file (cmake/TestInline.c.in ${PROJECT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/TestInline.c)
+				try_compile (HAVE___INLINE "${CMAKE_CURRENT_BINARY_DIR}"
+					"${PROJECT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/TestInline.c")
+				if (HAVE___INLINE)
+					message (STATUS "Checking for __inline__... supported")
+					
+					message (STATUS "Checking for __inline__...")
+					set (INLINE_KEYWORD "__inline__")
+					configure_file (cmake/TestInline.c.in ${PROJECT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/cmake/TestInline.c)
+					try_compile (HAVE___INLINE__ "${CMAKE_CURRENT_BINARY_DIR}"
+						"${PROJECT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/TestInline.c")
+				else ()
+					message (STATUS "Checking for __inline__... not supported")
+					set (INLINE_KEYWORD "")
+				endif ()
+				
+			endif ()			
+		endif ()
+		
+		if (HAVE_INLINE)
+			set (INLINE_CODE "/* #undef inline */" CACHE INTERNAL "")
+		elseif (HAVE___INLINE)
+			set (INLINE_CODE "#define inline __inline" CACHE INTERNAL "")
+		elseif (HAVE___INLINE__)
+			set (INLINE_CODE "#define inline __inline__" CACHE INTERNAL "")
+		else ()
+			set (INLINE_CODE "#define inline " CACHE INTERNAL "")
+		endif ()
+	endif ()
+endmacro (TEST_INLINE)
diff --git a/cmake/TestLargeFiles.cmake b/cmake/TestLargeFiles.cmake
new file mode 100644
index 0000000..075407b
--- /dev/null
+++ b/cmake/TestLargeFiles.cmake
@@ -0,0 +1,121 @@
+include (CheckIncludeFile)
+include (CheckTypeSize)
+include (CMakePushCheckState)
+
+macro (TEST_LARGE_FILES VARIABLE)
+
+if (NOT DEFINED ${VARIABLE})
+
+	cmake_push_check_state()
+
+	message (STATUS "")
+	message (STATUS "")
+	message (STATUS "Checking large files support...")
+
+	if (WIN32)
+		set (${VARIABLE} 1 CACHE INTERNAL "Result of tests for large file support" FORCE)
+		message (STATUS "")
+		message (STATUS "Result of checking large files support: supported with WinAPI")
+	else ()
+
+		message (STATUS "")
+		check_include_file(sys/types.h HAVE_SYS_TYPES_H)
+		check_include_file(stdint.h HAVE_STDINT_H)
+		check_include_file(stddef.h HAVE_STDDEF_H)
+		message (STATUS "")
+
+		message (STATUS "Checking size of off_t without any definitions:")
+		check_type_size (off_t SIZEOF_OFF_T)
+		message (STATUS "Checking of off_t without any definitions: ${SIZEOF_OFF_T}")
+		if (SIZEOF_OFF_T EQUAL 8)
+			set (LARGE_FILES_DEFINITIONS "" CACHE INTERNAL "64-bit off_t required definitions")
+			set (FILE64 TRUE)
+		else ()
+			unset (HAVE_SIZEOF_OFF_T CACHE)
+			unset (SIZEOF_OFF_T CACHE)
+			unset (SIZEOF_OFF_T_CODE CACHE)
+			cmake_pop_check_state()
+			set (FILE64 FALSE)
+		endif ()
+
+		if (NOT FILE64)
+			set (CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} /D_FILE_OFFSET_BITS=64)
+			message (STATUS "")
+			message (STATUS "Checking size of off_t with _FILE_OFFSET_BITS=64:")
+			check_type_size (off_t SIZEOF_OFF_T)
+			message (STATUS "Checking size of off_t with _FILE_OFFSET_BITS=64: ${SIZEOF_OFF_T}")
+			if (SIZEOF_OFF_T EQUAL 8)
+				set (_FILE_OFFSET_BITS 64 CACHE INTERNAL "")
+				set (_FILE_OFFSET_BITS_CODE "#define _FILE_OFFSET_BITS 64" CACHE INTERNAL "")
+				set (LARGE_FILES_DEFINITIONS ${LARGE_FILES_DEFINITIONS} "/D_FILE_OFFSET_BITS=64" CACHE INTERNAL "64-bit off_t required definitions")
+				set (FILE64 TRUE)
+			else ()
+				set (_FILE_OFFSET_BITS_CODE "" CACHE INTERNAL "")
+				unset (HAVE_SIZEOF_OFF_T CACHE)
+				unset (SIZEOF_OFF_T CACHE)
+				unset (SIZEOF_OFF_T_CODE CACHE)
+				cmake_pop_check_state()
+				set (FILE64 FALSE)
+			endif ()
+		endif ()
+
+		if (NOT FILE64)
+			set (CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} /D_LARGE_FILES)
+			message (STATUS "")
+			message (STATUS "Checking size of off_t with _LARGE_FILES:")
+			check_type_size (off_t SIZEOF_OFF_T)
+			message (STATUS "Checking size of off_t with _LARGE_FILES: ${SIZEOF_OFF_T}")
+			if (SIZEOF_OFF_T EQUAL 8)
+				set (_LARGE_FILES 1 CACHE INTERNAL "")
+				set (LARGE_FILES_DEFINITIONS ${LARGE_FILES_DEFINITIONS} "/D_LARGE_FILES" CACHE INTERNAL "64-bit off_t required definitions")
+				set (FILE64 TRUE)
+			else ()
+				unset (HAVE_SIZEOF_OFF_T CACHE)
+				unset (SIZEOF_OFF_T CACHE)
+				unset (SIZEOF_OFF_T_CODE CACHE)
+				cmake_pop_check_state()
+				set (FILE64 FALSE)
+			endif ()
+		endif ()
+
+		if (NOT FILE64)
+			set (CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} /D_LARGEFILE_SOURCE)
+			unset (HAVE_SIZEOF_OFF_T CACHE)
+			unset (SIZEOF_OFF_T CACHE)
+			unset (SIZEOF_OFF_T_CODE CACHE)
+			message (STATUS "")
+			message (STATUS "Checking size of off_t with _LARGEFILE_SOURCE:")
+			check_type_size (off_t SIZEOF_OFF_T)
+			message (STATUS "Checking size of off_t with _LARGEFILE_SOURCE: ${SIZEOF_OFF_T}")
+			if (SIZEOF_OFF_T EQUAL 8)
+				set (_LARGEFILE_SOURCE 1 CACHE INTERNAL "")
+				set (LARGE_FILES_DEFINITIONS ${LARGE_FILES_DEFINITIONS} "/D_LARGEFILE_SOURCE"  CACHE INTERNAL "64-bit off_t required definitions")
+				set (FILE64 TRUE)
+			else ()
+				cmake_pop_check_state()
+				set (FILE64 FALSE)
+			endif ()
+		endif ()
+
+		message (STATUS "")
+		if (FILE64)
+			set (${VARIABLE} 1 CACHE INTERNAL "Result of tests for large file support" FORCE)
+			if (NOT SIZEOF_OFF_T_REQURED_DEFINITIONS)
+				message (STATUS "Result of checking large files support: supported")
+			else ()
+				message (STATUS "Result of checking large files support: supported with ${LARGE_FILES_DEFINITIONS}")
+				message (STATUS "Add LARGE_FILES_DEFINITIONS to your compiler definitions or configure with _FILE_OFFSET_BITS,")
+				message (STATUS "_FILE_OFFSET_BITS_CODE, _LARGE_FILES and _LARGEFILE_SOURCE variables.")
+			endif ()
+		else ()
+			message ("Result of checking large files support: not supported")
+			set (${VARIABLE} 0 CACHE INTERNAL "Result of test for large file support" FORCE)
+		endif ()
+		message ("")
+		message ("")
+
+	endif ()
+
+endif (NOT DEFINED ${VARIABLE})
+
+endmacro (TEST_LARGE_FILES VARIABLE)
diff --git a/cmake/sqlite/FindSQLite3.cmake b/cmake/sqlite/FindSQLite3.cmake
new file mode 100644
index 0000000..a0385ca
--- /dev/null
+++ b/cmake/sqlite/FindSQLite3.cmake
@@ -0,0 +1,56 @@
+# - Find SQLite3
+# Find the native SQLite3 includes and libraries
+#
+#  SQLite3_INCLUDE_DIRS - where to find sqlite3.h, etc.
+#  SQLite3_LIBRARIES    - List of libraries when using SQLite3.
+#  SQLite3_FOUND        - True if SQLite3 found.
+
+if (SQLite3_INCLUDE_DIR)
+	# Already in cache, be silent
+	set (SQLite3_FIND_QUIETLY TRUE)
+endif ()
+
+find_package (PkgConfig QUIET)
+pkg_check_modules (PC_SQLite3 QUIET sqlite3)
+
+set (SQLite3_VERSION ${PC_SQLite3_VERSION})
+
+find_path (SQLite3_INCLUDE_DIR sqlite3.h
+	HINTS
+		${PC_SQLite3_INCLUDEDIR}
+		${PC_SQLite3_INCLUDE_DIRS}
+		${SQLite3_ROOT}
+	)
+
+find_library (SQLite3_LIBRARY
+	NAMES
+		sqlite3
+	HINTS
+		${PC_SQLite3_LIBDIR}
+		${PC_SQLite3_LIBRARY_DIRS}
+		${SQLite3_ROOT}
+	)
+
+include (FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args (SQLite3
+	REQUIRED_VARS
+		SQLite3_LIBRARY
+		SQLite3_INCLUDE_DIR
+	VERSION_VAR
+		SQLite3_VERSION
+	)
+
+if (SQLite3_FOUND)
+	set (SQLite3_INCLUDE_DIRS ${SQLite3_INCLUDE_DIR})
+	set (SQLite3_LIBRARIES ${SQLite3_LIBRARY})
+	if (NOT TARGET SQLite::SQLite3)
+		add_library (SQLite::SQLite3 UNKNOWN IMPORTED)
+		set_target_properties (SQLite::SQLite3 PROPERTIES
+			INTERFACE_INCLUDE_DIRECTORIES "${SQLite3_INCLUDE_DIRS}"
+			IMPORTED_LOCATION "${SQLite3_LIBRARIES}"
+		)
+	endif ()
+endif ()
+
+mark_as_advanced (SQLite3_INCLUDE_DIR SQLite3_LIBRARY)
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..dc0d5e1
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,761 @@
+dnl Copyright (C) 1999-2022  Erik de Castro Lopo <erikd@mega-nerd.com>.
+
+dnl Require autoconf version >= 2.69
+AC_PREREQ([2.69])
+
+AC_INIT([libsndfile],[1.2.2],[sndfile@mega-nerd.com],
+		[libsndfile],[http://libsndfile.github.io/libsndfile/])
+
+dnl Check whether we want to set defaults for CFLAGS, CXXFLAGS, CPPFLAGS and LDFLAGS
+AC_MSG_CHECKING([whether configure should try to set CFLAGS/CXXFLAGS/CPPFLAGS/LDFLAGS])
+AS_IF([test "x${CFLAGS+set}" = "xset" || test "x${CXXFLAGS+set}" = "xset" || test "x${CPPFLAGS+set}" = "xset" || test "x${LDFLAGS+set}" = "xset"], [
+		enable_flags_setting=no
+		: ${CFLAGS=""}
+		: ${CXXFLAGS=""}
+	], [
+		enable_flags_setting=yes
+		dnl Set to empty flags so AC_PROG_CC and
+		dnl AC_PROG_CXX do not add -g -O2
+		CFLAGS=""
+		CXXFLAGS=""
+	])
+AC_MSG_RESULT([${enable_flags_setting}])
+
+dnl Put config stuff in 'build-aux'.
+AC_CONFIG_AUX_DIR([build-aux])
+
+AC_CONFIG_SRCDIR([src/sndfile.c])
+AC_CANONICAL_HOST
+
+AC_CONFIG_MACRO_DIR([m4])
+AC_CONFIG_HEADERS([src/config.h])
+
+AM_INIT_AUTOMAKE([1.14 foreign dist-xz no-dist-gzip serial-tests subdir-objects])
+AM_SILENT_RULES([yes])
+
+dnl ====================================================================================
+
+AC_PROG_CC
+AC_PROG_CC_C99
+
+AS_IF([test "x$ac_cv_prog_cc_c99" = "xno"], [
+		AC_MSG_ERROR([libsndfile requires a C99 capable compiler!])
+	])
+
+AC_USE_SYSTEM_EXTENSIONS
+AC_PROG_CXX
+
+AC_LANG([C])
+AX_COMPILER_VENDOR
+AX_COMPILER_VERSION
+
+AC_LANG_PUSH([C++])
+AX_COMPILER_VENDOR
+AX_COMPILER_VERSION
+AC_LANG_POP([C++])
+
+AC_PROG_SED
+AM_PROG_AR
+
+LT_INIT([disable-static win32-dll])
+LT_PROG_RC
+
+AC_PROG_INSTALL
+AC_PROG_LN_S
+
+AM_PATH_PYTHON
+
+AC_CHECK_PROG([HAVE_AUTOGEN], [autogen], [yes], [no])
+AC_CHECK_PROG([HAVE_WINE], [wine], [yes], [no])
+AC_CHECK_PROG([HAVE_XCODE_SELECT], [xcode-select], [yes], [no])
+
+dnl ------------------------------------------------------------------------------------
+dnl  Rules for library version information:
+dnl
+dnl   1. Start with version information of `0:0:0' for each libtool library.
+dnl   2. Update the version information only immediately before a public release of
+dnl      your software. More frequent updates are unnecessary, and only guarantee
+dnl      that the current interface number gets larger faster.
+dnl   3. If the library source code has changed at all since the last update, then
+dnl      increment revision (`c:r:a' becomes `c:r+1:a').
+dnl   4. If any interfaces have been added, removed, or changed since the last update,
+dnl      increment current, and set revision to 0.
+dnl   5. If any interfaces have been added since the last public release, then increment
+dnl      age.
+dnl   6. If any interfaces have been removed since the last public release, then set age
+dnl      to 0.
+
+dnl   This is libtool version of library, we add it to `--version-info` property.
+
+m4_define([lt_current], [1])
+m4_define([lt_revision], [37])
+m4_define([lt_age], [0])
+
+dnl This is ABI version for linker scripts, CMake uses the same format for
+dnl VERSION property of shared library.
+dnl The formula is: c:r:a -> c-a:a:r
+
+m4_define([abi_version_major], [m4_eval(lt_current - lt_age)])
+m4_define([abi_version_minor], [lt_age])
+m4_define([abi_version_patch], [lt_revision])
+
+dnl ------------------------------------------------------------------------------------
+
+AC_CHECK_HEADERS([endian.h])
+AC_CHECK_HEADERS([byteswap.h])
+AC_CHECK_HEADERS([locale.h])
+AC_CHECK_HEADERS([sys/time.h])
+AC_CHECK_HEADERS([immintrin.h])
+AC_CHECK_HEADERS([stdbool.h])
+
+AC_HEADER_SYS_WAIT
+
+AC_CHECK_DECLS([S_IRGRP])
+AS_IF([test "x$ac_cv_have_decl_S_IRGRP" = "xyes"], [
+		AC_DEFINE_UNQUOTED([HAVE_DECL_S_IRGRP], [1], [Set to 1 if S_IRGRP is defined.])
+	], [
+		AC_DEFINE_UNQUOTED([HAVE_DECL_S_IRGRP], [0], [Set to 0 if S_IRGRP is not defined.])
+	])
+
+AM_CONDITIONAL([LINUX_MINGW_CROSS_TEST],
+	[test "x${build_os}:${host_os}:${HAVE_WINE}" = "xlinux-gnu:mingw32msvc:yes"])
+
+dnl ====================================================================================
+dnl  Couple of initializations here. Fill in real values later.
+
+SHLIB_VERSION_ARG=""
+
+dnl ====================================================================================
+dnl  Finished checking, handle options.
+
+AC_ARG_ENABLE(experimental,
+	AS_HELP_STRING([--enable-experimental], [enable experimental code]))
+
+AS_IF([test "x$enable_experimental" = "xyes"], [
+		EXPERIMENTAL_CODE=1
+	], [
+		EXPERIMENTAL_CODE=0
+	])
+AC_DEFINE_UNQUOTED([ENABLE_EXPERIMENTAL_CODE], [${EXPERIMENTAL_CODE}], [Set to 1 to enable experimental code.])
+
+AC_ARG_ENABLE([werror],
+	[AS_HELP_STRING([--enable-werror], [enable -Werror in all Makefiles])])
+
+AC_ARG_ENABLE([cpu-clip],
+	[AS_HELP_STRING([--disable-cpu-clip], [disable tricky cpu specific clipper])])
+
+AC_ARG_ENABLE([bow-docs],
+	[AS_HELP_STRING([--enable-bow-docs], [enable black-on-white html docs])])
+
+AC_ARG_ENABLE([sqlite],
+	[AS_HELP_STRING([--disable-sqlite], [disable use of sqlite])])
+
+AC_ARG_ENABLE([alsa],
+	[AS_HELP_STRING([--disable-alsa], [disable ALSA support (default=autodetect)])], [], [enable_alsa=auto])
+
+AC_ARG_ENABLE([external-libs],
+	[AS_HELP_STRING([--disable-external-libs], [disable use of FLAC, Ogg and Vorbis [[default=no]]])])
+
+AC_ARG_ENABLE([mpeg],
+	[AS_HELP_STRING([--disable-mpeg], [disable use of LAME/MPG123 for MPEG (MP3) [[defaults=no]]])])
+
+AC_ARG_ENABLE(octave,
+	[AS_HELP_STRING([--enable-octave], [enable building of GNU Octave module])])
+
+AC_ARG_ENABLE([full-suite],
+	[AS_HELP_STRING([--disable-full-suite], [disable building and installing programs, documentation, only build library [[default=no]]])])
+AM_CONDITIONAL([FULL_SUITE], [test "x$enable_full_suite" != "xno"])
+
+AC_ARG_ENABLE([test-coverage],
+	[AS_HELP_STRING([--enable-test-coverage], [enable test coverage])])
+AM_CONDITIONAL([ENABLE_TEST_COVERAGE], [test "x$enable_test_coverage" = "xyes"])
+
+AC_ARG_ENABLE([ossfuzzers],
+	[AS_HELP_STRING([--enable-ossfuzzers], [Whether to generate the fuzzers for OSS-Fuzz])])
+AM_CONDITIONAL([USE_OSSFUZZERS], [test "x$enable_ossfuzzers" = "xyes"])
+
+AC_SUBST([LIB_FUZZING_ENGINE])
+AM_CONDITIONAL([USE_OSSFUZZ_FLAG], [test "x$LIB_FUZZING_ENGINE" = "x-fsanitize=fuzzer"])
+AM_CONDITIONAL([USE_OSSFUZZ_STATIC], [test -f "$LIB_FUZZING_ENGINE"])
+
+dnl ====================================================================================
+dnl  Check types and their sizes.
+
+AC_CHECK_SIZEOF([wchar_t], [4])
+AC_CHECK_SIZEOF([short], [2])
+AC_CHECK_SIZEOF([int], [4])
+AC_CHECK_SIZEOF([long], [4])
+AC_CHECK_SIZEOF([float], [4])
+AC_CHECK_SIZEOF([double], [4])
+AC_CHECK_SIZEOF([void*], [8])
+AC_CHECK_SIZEOF([size_t], [4])
+AC_CHECK_SIZEOF([int64_t], [8])
+AC_CHECK_SIZEOF([long long], [8])
+
+dnl  Check for common 64 bit file offset types.
+AC_CHECK_SIZEOF([off_t], [1])
+
+AS_IF([test "x$enable_largefile:$ac_cv_sizeof_off_t" = "xno:8"], [
+		AC_MSG_ERROR(["Error : Cannot disable large file support because sizeof (off_t) == 8."])
+	])
+
+AS_CASE([$host_os],
+	[mingw32*], [
+		AC_DEFINE([__USE_MINGW_ANSI_STDIO], [1], [Set to 1 to use C99 printf/snprintf in MinGW.])
+		],
+
+	[
+		AS_IF([test "x$ac_cv_sizeof_off_t" = "x8"], [
+				dnl If sizeof (off_t) is 8, no further checking is needed.
+			], [
+				dnl Save the old sizeof (off_t) value  and then unset it to see if it
+				dnl changes when Large File Support is enabled.
+				pre_largefile_sizeof_off_t=$ac_cv_sizeof_off_t
+				unset ac_cv_sizeof_off_t
+
+				AC_SYS_LARGEFILE
+				AS_IF([test "x$ac_cv_sys_largefile_CFLAGS" = "xno"], [
+						ac_cv_sys_largefile_CFLAGS=""
+					])
+				AS_IF([test "x$ac_cv_sys_largefile_LDFLAGS" = "xno"], [
+						ac_cv_sys_largefile_LDFLAGS=""
+					])
+				AS_IF([test "x$ac_cv_sys_largefile_LIBS" = "xno"], [
+						ac_cv_sys_largefile_LIBS=""
+					])
+
+				AC_CHECK_SIZEOF(off_t,1)
+			])
+	])
+
+AC_TYPE_SSIZE_T
+
+dnl ====================================================================================
+dnl  Determine endian-ness of host processor.
+
+AC_C_BIGENDIAN([
+		dnl big-endian
+		ac_cv_c_big_endian=1
+		ac_cv_c_little_endian=0
+	], [
+		dnl little-endian
+		ac_cv_c_big_endian=0
+		ac_cv_c_little_endian=1
+	])
+
+AC_DEFINE_UNQUOTED([CPU_IS_BIG_ENDIAN], [${ac_cv_c_big_endian}], [Host processor is big endian.])
+AC_DEFINE_UNQUOTED([CPU_IS_LITTLE_ENDIAN], [${ac_cv_c_little_endian}], [Host processor is little endian.])
+
+dnl ====================================================================================
+dnl  Check for functions.
+
+AC_CHECK_FUNCS([malloc calloc realloc free])
+AC_CHECK_FUNCS([open read write lseek lseek64])
+AC_CHECK_FUNCS([fstat fstat64 ftruncate fsync])
+AC_CHECK_FUNCS([snprintf vsnprintf])
+AC_CHECK_FUNCS([gmtime gmtime_r localtime localtime_r gettimeofday])
+AC_CHECK_FUNCS([mmap getpagesize])
+AC_CHECK_FUNCS([setlocale])
+AC_CHECK_FUNCS([pipe waitpid])
+
+AC_SEARCH_LIBS([floor], [m], [], [
+		AC_MSG_ERROR([unable to find the floor() function!])
+	])
+AC_CHECK_FUNCS([floor ceil fmod lrint lrintf])
+
+dnl ====================================================================================
+dnl  Check for requirements for building plugins for other languages/enviroments.
+
+dnl Octave maths environment http://www.octave.org/
+AS_IF([test "x$cross_compiling" = "xno"], [
+		AS_IF([test "x$enable_octave" = "xno"], [
+				AM_CONDITIONAL(BUILD_OCTAVE_MOD, false)
+			], [
+				AC_OCTAVE_BUILD
+			])
+	], [
+		AM_CONDITIONAL(BUILD_OCTAVE_MOD, false)
+	])
+
+dnl ====================================================================================
+dnl  Check for Ogg, Vorbis and FLAC.
+
+HAVE_EXTERNAL_XIPH_LIBS=0
+EXTERNAL_XIPH_CFLAGS=""
+EXTERNAL_XIPH_LIBS=""
+EXTERNAL_XIPH_REQUIRE=""
+
+dnl Check for pkg-config outside the if statement.
+PKG_PROG_PKG_CONFIG
+AX_REQUIRE_DEFINED([PKG_INSTALLDIR])
+PKG_INSTALLDIR
+
+AS_IF([test -n "$PKG_CONFIG"], [
+		AS_IF([test "x$enable_external_libs" = "xno"], [
+				AC_MSG_WARN([[*** External libs (FLAC, Ogg, Vorbis) disabled. ***]])
+			], [
+				PKG_CHECK_MOD_VERSION(FLAC, flac >= 1.3.1, ac_cv_flac=yes, ac_cv_flac=no)
+
+				dnl Make sure the FLAC_CFLAGS value is sane.
+				FLAC_CFLAGS=`echo $FLAC_CFLAGS | $SED "s|include/FLAC|include|"`
+
+				PKG_CHECK_MOD_VERSION(OGG, ogg >= 1.3.0, ac_cv_ogg=yes, ac_cv_ogg=no)
+
+				AS_IF([test "x$enable_experimental" = "xyes"], [
+						PKG_CHECK_MOD_VERSION(SPEEX, speex >= 1.2, ac_cv_speex=yes, ac_cv_speex=no)
+					], [
+						SPEEX_CFLAGS=""
+						SPEEX_LIBS=""
+					])
+
+				dnl Vorbis versions earlier than 1.2.3 have bugs that cause the libsndfile
+				dnl test suite to fail on MIPS, PowerPC and others.
+				dnl See: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=549899
+				PKG_CHECK_MOD_VERSION(VORBIS, vorbis >= 1.2.3, ac_cv_vorbis=yes, ac_cv_vorbis=no)
+				PKG_CHECK_MOD_VERSION(VORBISENC, vorbisenc >= 1.2.3, ac_cv_vorbisenc=yes, ac_cv_vorbisenc=no)
+
+				PKG_CHECK_MOD_VERSION(OPUS, opus >= 1.1, ac_cv_opus=yes, ac_cv_opus=no)
+				enable_external_libs=yes
+			])
+
+			AS_IF([test "x$ac_cv_flac$ac_cv_ogg$ac_cv_vorbis$ac_cv_vorbisenc$ac_cv_opus" = "xyesyesyesyesyes"], [
+					HAVE_EXTERNAL_XIPH_LIBS=1
+					enable_external_libs=yes
+
+					EXTERNAL_XIPH_CFLAGS="$FLAC_CFLAGS $VORBIS_CFLAGS $VORBISENC_CFLAGS $SPEEX_CFLAGS $OPUS_CFLAGS $OGG_CFLAGS "
+					EXTERNAL_XIPH_LIBS="$FLAC_LIBS $VORBIS_LIBS $VORBISENC_LIBS $SPEEX_LIBS $OPUS_LIBS $OGG_LIBS "
+					EXTERNAL_XIPH_REQUIRE="flac ogg vorbis vorbisenc opus"
+
+					if test x$ac_cv_speex = "xyes" ; then
+						EXTERNAL_XIPH_REQUIRE="$EXTERNAL_XIPH_REQUIRE speex"
+					fi
+				], [
+					AS_ECHO([""])
+					AC_MSG_WARN([[*** One or more of the external libraries (ie libflac, libogg,]])
+					AC_MSG_WARN([[*** libvorbis and libopus) is either missing (possibly only the development]])
+					AC_MSG_WARN([[*** headers) or is of an unsupported version.]])
+					AC_MSG_WARN([[***]])
+					AC_MSG_WARN([[*** Unfortunately, for ease of maintenance, the external libs]])
+					AC_MSG_WARN([[*** are an all or nothing affair.]])
+					AS_ECHO([""])
+					enable_external_libs=no
+				])
+	])
+
+AC_DEFINE_UNQUOTED([HAVE_EXTERNAL_XIPH_LIBS], [$HAVE_EXTERNAL_XIPH_LIBS], [Will be set to 1 if flac, ogg, vorbis, and opus are available.])
+
+dnl ====================================================================================
+dnl  Check for MPEG libraris liblame
+
+ac_cv_lame="no"
+ac_cv_mpg123="no"
+HAVE_MPEG=0
+EXTERNAL_MPEG_REQUIRE=""
+EXTERNAL_MPEG_LIBS=""
+
+AS_IF([test -n "$PKG_CONFIG"], [
+		AS_IF([test "x$enable_mpeg" = "xno"], [
+				AC_MSG_WARN([[*** MPEG (Lame/MPG123) disabled. ***]])
+			], [
+				AC_CHECK_HEADER(lame/lame.h,
+								lame_header_found="yes",
+								lame_header_found="no")
+				AC_SEARCH_LIBS(lame_set_VBR_q, [lame mp3lame], [lame_lib_found="yes"], [lame_lib_found="no"])
+				AS_IF([test "x$lame_lib_found$lame_header_found" = "xyesyes"], [
+						ac_cv_lame="yes"
+					], [
+						AC_MSG_WARN([["MPEG support selected but external Lame library cannot be found.]])
+					])
+
+				PKG_CHECK_MOD_VERSION([MPG123], [libmpg123 >= 1.25.10], [
+						ac_cv_mpg123="yes"
+					], [
+						AC_MSG_WARN([["MPEG support selected but external MPG123 library cannot be found.]])
+					])
+
+				AS_IF([test "x$ac_cv_lame$ac_cv_mpg123" = "xyesyes"], [
+						enable_mpeg="yes"
+						HAVE_MPEG=1
+						EXTERNAL_MPEG_REQUIRE="libmpg123"
+						EXTERNAL_MPEG_LIBS="-lmp3lame"
+						MPEG_CFLAGS="$MPG123_CFLAGS"
+						MPEG_LIBS="$MPG123_LIBS"
+					], [
+						enable_mpeg="no"
+						AS_ECHO([""])
+						AC_MSG_WARN([[*** MPEG support disabled.]])
+						AS_ECHO([""])
+					])
+			])
+	])
+
+AC_DEFINE_UNQUOTED([HAVE_MPEG], [$HAVE_MPEG], [Will be set to 1 if lame, mpg123 mpeg support is available.])
+
+dnl ====================================================================================
+dnl  Check for libsqlite3 (only used in regtest).
+
+ac_cv_sqlite3=0
+AS_IF([test "x$enable_sqlite" != "xno"], [
+		PKG_CHECK_MOD_VERSION([SQLITE3], [sqlite3 >= 3.2], [ac_cv_sqlite3=1], [ac_cv_sqlite3=0])
+	])
+
+AC_DEFINE_UNQUOTED([HAVE_SQLITE3], [${ac_cv_sqlite3}], [Set to 1 if you have libsqlite3.])
+AM_CONDITIONAL([HAVE_SQLITE3], [test "x$ac_cv_sqlite3" = "x1"])
+
+dnl ====================================================================================
+dnl  Determine if the processor can do clipping on float to int conversions.
+
+AS_IF([test "x$enable_cpu_clip" != "xno"], [
+		MN_C_CLIP_MODE
+	], [
+		AS_ECHO(["checking processor clipping capabilities... disabled"])
+		ac_cv_c_clip_positive=0
+		ac_cv_c_clip_negative=0
+	])
+
+AC_DEFINE_UNQUOTED([CPU_CLIPS_POSITIVE], [${ac_cv_c_clip_positive}],
+	[Host processor clips on positive float to int conversion.])
+AC_DEFINE_UNQUOTED([CPU_CLIPS_NEGATIVE], [${ac_cv_c_clip_negative}],
+	[Host processor clips on negative float to int conversion.])
+
+dnl ====================================================================================
+dnl  Host OS specific stuff.
+
+OS_SPECIFIC_CFLAGS=""
+OS_SPECIFIC_LINKS=""
+os_is_win32=0
+os_is_openbsd=0
+use_windows_api=0
+AS_CASE([$host_os],
+	[darwin* | rhapsody*], [
+		AS_IF([test "x$HAVE_XCODE_SELECT" = "xyes"], [
+				developer_path=`xcode-select --print-path`
+			], [
+				developer_path="/Developer"
+			])
+		OS_SPECIFIC_LINKS="-framework CoreAudio -framework AudioToolbox -framework CoreFoundation"],
+	[mingw*], [
+		os_is_win32=1
+		use_windows_api=1
+		OS_SPECIFIC_LINKS="-lwinmm"],
+	[openbsd*], [
+		os_is_openbsd=1
+	])
+
+AC_DEFINE_UNQUOTED([OS_IS_WIN32], [${os_is_win32}], [Set to 1 if compiling for Win32])
+AC_DEFINE_UNQUOTED([OS_IS_OPENBSD], [${os_is_openbsd}], [Set to 1 if compiling for OpenBSD])
+AC_DEFINE_UNQUOTED([USE_WINDOWS_API], [${use_windows_api}], [Set to 1 to use the native windows API])
+AM_CONDITIONAL(USE_WIN_VERSION_FILE, test ${use_windows_api} -eq 1)
+
+dnl ====================================================================================
+dnl  Check for ALSA.
+
+AS_IF([test "x$enable_alsa" != "xno"], [
+		PKG_CHECK_MODULES([ALSA], [alsa], [
+				dnl actually test whether ALSA really works, in
+				dnl order to dodge wrong cross-compilation pickups
+				save_CFLAGS="${CFLAGS}"
+				save_LIBS="${LIBS}"
+				CFLAGS="${CFLAGS} ${ALSA_CFLAGS}"
+				LIBS="${LIBS} ${ALSA_LIBS}"
+				AC_CHECK_HEADERS([alsa/asoundlib.h])
+				AS_IF([test "x$ac_cv_header_alsa_asoundlib_h" = "xyes"], [
+						dnl ALSA definitely works
+						AC_DEFINE([HAVE_ALSA], [1], [Set to 1 if you have alsa])
+						alsa_works="yes"
+					], [
+						dnl picked up wrong ALSA
+						alsa_works="no"
+
+						dnl reset flags
+						ALSA_CFLAGS=""
+						ALSA_LIBS=""
+					])
+				CFLAGS="${save_CFLAGS}"
+				LIBS="${save_LIBS}"
+			], [
+				dnl could not find ALSA
+				alsa_works="no"
+			])
+
+		AS_IF([test "x$alsa_works" = "xno"], [
+				AS_IF([test "x$enable_alsa" = "xyes"], [
+						dnl explicitly passed --enable-alsa, hence error out loud and clearly
+						AC_MSG_ERROR([You explicitly requested alsa support, but alsa could not be found!])
+					], [
+						dnl did not explicitly pass --enable-alsa, relying on default automagic on
+						enable_alsa="no (auto)"
+					])
+			])
+	])
+
+dnl ====================================================================================
+dnl  Check for OpenBSD's sndio.
+
+SNDIO_LIBS=""
+HAVE_SNDIO_H=0
+AS_CASE([$host_os],
+	[openbsd*], [
+		AC_CHECK_HEADERS(sndio.h)
+		AS_IF([test "x$ac_cv_header_sndio_h" = "xyes"], [
+				SNDIO_LIBS="-lsndio"
+				HAVE_SNDIO_H=1
+			])
+	])
+
+AC_DEFINE_UNQUOTED([HAVE_SNDIO_H], [${HAVE_SNDIO_H}], [Set to 1 if <sndio.h> is available.])
+
+dnl ====================================================================================
+dnl  Test for sanity when cross-compiling.
+
+AS_IF([test "x$ac_cv_sizeof_short" != "x2"], [
+		AC_MSG_WARN([[******************************************************************]])
+		AC_MSG_WARN([[*** sizeof (short) != 2.                                          ]])
+		AC_MSG_WARN([[******************************************************************]])
+	])
+
+AS_IF([test "x$ac_cv_sizeof_int" != "x4"], [
+		AC_MSG_WARN([[******************************************************************]])
+		AC_MSG_WARN([[*** sizeof (int) != 4                                             ]])
+		AC_MSG_WARN([[******************************************************************]])
+	])
+
+AS_IF([test "x$ac_cv_sizeof_float" != "x4"], [
+		AC_MSG_WARN([[******************************************************************]])
+		AC_MSG_WARN([[*** sizeof (float) != 4.                                          ]])
+		AC_MSG_WARN([[******************************************************************]])
+	])
+
+AS_IF([test "x$ac_cv_sizeof_double" != "x8"], [
+		AC_MSG_WARN([[******************************************************************]])
+		AC_MSG_WARN([[*** sizeof (double) != 8.                                         ]])
+		AC_MSG_WARN([[******************************************************************]])
+	])
+
+AS_IF([test "x$ac_cv_prog_HAVE_AUTOGEN" = "xno"], [
+		AC_MSG_WARN([[Touching files in directory tests/.]])
+		touch tests/*.c tests/*.h
+	])
+
+dnl ====================================================================================
+dnl  Settings for the HTML documentation.
+
+AS_IF([test "x$enable_bow_docs" = "xyes"], [
+		HTML_BGCOLOUR="white"
+		HTML_FGCOLOUR="black"
+	], [
+		HTML_BGCOLOUR="black"
+		HTML_FGCOLOUR="white"
+	])
+
+dnl ====================================================================================
+dnl  Now use the information from the checking stage.
+
+win32_target_dll=0
+COMPILER_IS_GCC=0
+
+AS_IF([test "x$enable_flags_setting" = "xyes"], [
+		AX_APPEND_COMPILE_FLAGS([-O2 -pipe], [CFLAGS])
+
+		AC_LANG_PUSH([C++])
+		AX_APPEND_COMPILE_FLAGS([-O2 -pipe], [CXXFLAGS])
+		AC_LANG_POP([C++])
+
+		AS_CASE([${host_os}],
+			[darwin*], [
+				ldflags_test="-Wl,-dead_strip_dylibs"],
+			[linux*], [
+				ldflags_test="-Wl,-O1 -Wl,--as-needed -Wl,--no-undefined -Wl,--gc-sections"]
+			)
+		AX_APPEND_LINK_FLAGS([${ldflags_test}], [LDFLAGS])
+	])
+
+AS_IF([test "x$enable_werror" = "xyes"], [
+		AX_APPEND_COMPILE_FLAGS([-Werror], [CFLAGS])
+
+		AC_LANG_PUSH([C++])
+		AX_APPEND_COMPILE_FLAGS([-Werror], [CXXFLAGS])
+		AC_LANG_POP([C++])
+	])
+
+common_flags="-Wall -Wextra -Wpointer-arith -Wcast-align -Wcast-qual -Wshadow -Wwrite-strings -Wundef -Wuninitialized -Winit-self"
+common_cflags="${common_flags}"
+dnl Clang doesn't know about -Wno-format-truncation
+dnl and would spew tons of warnings otherwise.
+AS_IF([test "x$ax_cv_c_compiler_vendor" = "xgnu"], [
+		common_cflags+=" -Wno-format-truncation"
+	])
+common_cxxflags="${common_flags}"
+AS_IF([test "x$ax_cv_cxx_compiler_vendor" = "xgnu"], [
+		common_cxxflags+=" -Wno-format-truncation"
+	])
+
+AX_APPEND_COMPILE_FLAGS([${common_cflags} -Wvla -Wbad-function-cast -Wnested-externs -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Waggregate-return], [CFLAGS])
+
+AC_LANG_PUSH([C++])
+AX_APPEND_COMPILE_FLAGS([${common_cxxflags} -Wctor-dtor-privacy -Wnon-virtual-dtor -Woverloaded-virtual -Wreorder -Wsign-promo], [CXXFLAGS])
+AC_LANG_POP([C++])
+
+AS_IF([test "x$enable_test_coverage" = "xyes"], [
+		AX_APPEND_COMPILE_FLAGS([-coverage], [CFLAGS])
+	])
+
+dnl some distributions (such as Gentoo) have _FORTIFY_SOURCE always
+dnl enabled. We test for this situation in order to prevent polluting
+dnl the console with messages of macro redefinitions.
+AX_ADD_FORTIFY_SOURCE
+
+AS_IF([test "x$ax_cv_c_compiler_vendor" = "xgnu"], [
+		dnl OS specific tweaks.
+		AS_CASE([$host_os],
+			[darwin* | rhapsody*], [
+				dnl Disable -Wall, -pedantic and -Wshadow for Apple Darwin/Rhapsody.
+				dnl System headers on these systems are broken.
+				temp_CFLAGS=`echo $CFLAGS | $SED "s/-Wall -pedantic//" | $SED "s/-Wshadow//" | $SED "s/-Waggregate-return//"`
+				CFLAGS=$temp_CFLAGS
+				SHLIB_VERSION_ARG="-Wl,-exported_symbols_list -Wl,\$(top_srcdir)/src/Symbols.darwin"],
+			[mingw*], [
+				SHLIB_VERSION_ARG="-Wc,-static-libgcc -Wl,\$(top_srcdir)/src/libsndfile-1.def"
+				win32_target_dll=1
+				AS_IF([test "x$enable_shared" = "xno"], [
+						win32_target_dll=0
+					])
+				AX_APPEND_COMPILE_FLAGS([-mstackrealign], [CFLAGS])
+				],
+			[os2*], [
+				SHLIB_VERSION_ARG="-Wl,-export-symbols \$(top_srcdir)/src/Symbols.os2"
+			])
+
+		COMPILER_IS_GCC=1
+	])
+
+AS_CASE([$host_os], [linux*|kfreebsd*-gnu*|gnu*],  [SHLIB_VERSION_ARG="-Wl,--version-script=\$(top_srcdir)/src/Symbols.gnu-binutils"])
+
+AC_DEFINE_UNQUOTED([WIN32_TARGET_DLL], [${win32_target_dll}], [Set to 1 if windows DLL is being built.])
+AC_DEFINE_UNQUOTED([COMPILER_IS_GCC], [${COMPILER_IS_GCC}], [Set to 1 if the compile is GNU GCC.])
+
+CFLAGS="$CFLAGS $OS_SPECIFIC_CFLAGS"
+
+AS_IF([test "x$CFLAGS" = "x"], [
+		AC_MSG_ERROR(["Error in configure script. CFLAGS has been screwed up."])
+	])
+
+HOST_TRIPLET="${host_cpu}-${host_vendor}-${host_os}"
+
+AC_DEFINE_UNQUOTED([HOST_TRIPLET], [${HOST_TRIPLET}], [The host triplet of the compiled binary.])
+
+AS_IF([test "$HOST_TRIPLET" = "x86_64-w64-mingw32"], [
+		OS_SPECIFIC_LINKS=" -static-libgcc $OS_SPECIFIC_LINKS"
+	])
+
+WIN_RC_VERSION=`echo $PACKAGE_VERSION | $SED -e "s/pre.*//" -e "s/beta.*//" -e "s/\./,/g"`
+
+
+AS_IF([test "x$enable_static" = "xno"], [
+		SRC_BINDIR=src/.libs/
+		TEST_BINDIR=tests/.libs/
+	], [
+		SRC_BINDIR=src/
+		TEST_BINDIR=tests/
+	])
+
+dnl -------------------------------------------------------------------------------
+
+AC_SUBST(HOST_TRIPLET)
+
+AC_SUBST(HTML_BGCOLOUR)
+AC_SUBST(HTML_FGCOLOUR)
+
+AC_SUBST(SHLIB_VERSION_ARG)
+AC_SUBST([SHARED_VERSION_INFO], [lt_current:lt_revision:lt_age])
+AC_SUBST([ABI_VERSION], [abi_version_major.abi_version_minor.abi_version_patch])
+AC_SUBST(CLEAN_VERSION)
+AC_SUBST(VERSION_MAJOR)
+AC_SUBST(GEN_TOOL)
+
+AC_SUBST(WIN_RC_VERSION)
+
+AC_SUBST(HAVE_EXTERNAL_XIPH_LIBS)
+AC_SUBST(HAVE_MPEG)
+AC_SUBST(OS_SPECIFIC_CFLAGS)
+AC_SUBST(OS_SPECIFIC_LINKS)
+AC_SUBST(SNDIO_LIBS)
+
+AC_SUBST(EXTERNAL_XIPH_CFLAGS)
+AC_SUBST(EXTERNAL_XIPH_LIBS)
+AC_SUBST(EXTERNAL_XIPH_REQUIRE)
+AC_SUBST(EXTERNAL_MPEG_LIBS)
+AC_SUBST(EXTERNAL_MPEG_REQUIRE)
+AC_SUBST(MPG123_CFLAGS)
+AC_SUBST(MPG123_LIBS)
+AC_SUBST(MPEG_CFLAGS)
+AC_SUBST(MPEG_LIBS)
+AC_SUBST(SRC_BINDIR)
+AC_SUBST(TEST_BINDIR)
+
+AC_CONFIG_FILES([
+	Makefile Octave/Makefile
+	src/version-metadata.rc
+	tests/test_wrapper.sh tests/pedantic-header-test.sh
+	sndfile.pc
+	Scripts/build-test-tarball.mk
+	])
+AC_OUTPUT
+
+dnl ====================================================================================
+
+AS_IF([test -z "$PKG_CONFIG"], [
+		AS_ECHO(["    *****************************************************************"])
+		AS_ECHO(["    ***          The pkg-config program is missing.               ***"])
+		AS_ECHO(["    *** External FLAC/Ogg/Vorbis libs cannot be found without it. ***"])
+		AS_ECHO(["    ***       http://pkg-config.freedesktop.org/wiki/             ***"])
+		AS_ECHO(["    *****************************************************************"])
+	])
+
+AX_RECURSIVE_EVAL([$libdir], [full_absolute_libdir])
+AX_RECURSIVE_EVAL([$bindir], [full_absolute_bindir])
+AX_RECURSIVE_EVAL([$pkgconfigdir], [full_absolute_pkgconfigdir])
+AX_RECURSIVE_EVAL([$htmldir], [full_absolute_htmldir])
+AC_MSG_RESULT([
+-=-=-=-=-=-=-=-=-=-= Configuration Complete =-=-=-=-=-=-=-=-=-=-=-
+
+  Configuration summary :
+
+    libsndfile version : .................. ${VERSION}
+
+    Host CPU : ............................ ${host_cpu}
+    Host Vendor : ......................... ${host_vendor}
+    Host OS : ............................. ${host_os}
+
+    CFLAGS : .............................. ${CFLAGS}
+    CXXFLAGS : ............................ ${CXXFLAGS}
+    CPPFLAGS : ............................ ${CPPFLAGS}
+    LDFLAGS : ............................. ${LDFLAGS}
+
+    Experimental code : ................... ${enable_experimental:-no}
+    Using ALSA in example programs : ...... ${enable_alsa:-no}
+    External FLAC/Ogg/Vorbis/Opus : ....... ${enable_external_libs:-no}
+    External MPEG Lame/MPG123 : ........... ${enable_mpeg:-no}
+    Building Octave interface : ........... ${OCTAVE_BUILD}
+
+  Tools :
+
+    C Compiler Vendor is : ................ ${ax_cv_c_compiler_vendor} (${ax_cv_c_compiler_version})
+    CXX Compiler Vendor is : .............. ${ax_cv_cxx_compiler_vendor} (${ax_cv_cxx_compiler_version})
+
+    Sanitizer enabled : ................... ${enable_sanitizer:-no}
+
+  Installation directories :
+
+    Library directory : ................... ${full_absolute_libdir}
+    Program directory : ................... ${full_absolute_bindir}
+    Pkgconfig directory : ................. ${full_absolute_pkgconfigdir}
+    HTML docs directory : ................. ${full_absolute_htmldir}
+
+Compiling some other packages against libsndfile may require
+the addition of '$full_absolute_pkgconfigdir' to the
+PKG_CONFIG_PATH environment variable.
+])
+
+dnl Remove symlink created by Scripts/android-configure.sh.
+rm -f gdbclient
diff --git a/docs/FAQ.md b/docs/FAQ.md
new file mode 100644
index 0000000..e7c98e2
--- /dev/null
+++ b/docs/FAQ.md
@@ -0,0 +1,482 @@
+---
+layout: default
+title: libsndfile : Frequently Asked Questions.
+---
+
+# libsndfile : Frequently Asked Questions
+
+1. [Do you plan to support XYZ codec in libsndfile?](#Q001)
+2. [In version 0 the SF\_INFO struct had a pcmbitwidth field but version 1 does not. Why?](#Q002)
+3. [Compiling is really slow on MacOS X. Why?](#Q003)
+4. [When trying to compile libsndfile on Solaris I get a "bad substitution" error during linking. What can I do to fix this?](#Q004)
+5. [Why doesn't libsndfile do interleaving/de-interleaving?](#Q005)
+6. [What's the best format for storing temporary files?](#Q006)
+7. [On Linux/Unix/MacOS X, what's the best way of detecting the presence of libsndfile?](#Q007)
+8. [I have libsndfile installed and now I want to use it. I just want a simple Makefile\! What do I do?](#Q008)
+9. [How about adding the ability to write/read sound files to/from memory buffers?](#Q009)
+10. [Reading a 16 bit PCM file as normalised floats and then writing them back changes some sample values. Why?](#Q010)
+11. [I'm having problems with u-law encoded WAV files generated by libsndfile in Winamp. Why?](#Q011)
+12. [I'm looking at sf\_read\*. What are items? What are frames?](#Q012)
+13. [Why can't libsndfile open this Sound Designer II (SD2) file?](#Q013)
+14. [I'd like to statically link libsndfile to my closed source application. Can I buy a license so that this is possible?](#Q014)
+15. [My program is crashing during a call to a function in libsndfile. Is this a bug in libsndfile?](#Q015)
+16. [Will you accept a fix for compiling libsndfile with compiler X?](#Q016)
+17. [Can libsndfile read/write files from/to UNIX pipes?](#Q017)
+18. [Is it possible to build a Universal Binary on Mac OS X?](#Q018)
+19. [I have project files for Visual Studio / XCode / Whatever. Why don't you distribute them with libsndfile?](#Q019)
+20. [Why doesn't libsndfile support MP3?](#Q020)
+21. [How do I use libsndfile in a closed source or commercial program and comply with the license?](#Q021)
+22. [What versions of windows does libsndfile work on?](#Q022)
+23. [I'm cross compiling libsndfile for another platform. How can I run the test suite?](#Q023)
+
+-----
+
+## Q1 : Do you plan to support XYZ codec in libsndfile? {#Q001}
+
+If source code for XYZ codec is available under a suitable license (LGPL, BSD,
+MIT etc) then yes, I'd like to add it.
+
+If suitable documentation is available on how to decode and encode the format
+then maybe, depending on how much work is involved.
+
+If XYZ is some proprietary codec where no source code or documentation is
+available then no.
+
+So if you want support for XYZ codec, first find existing source code or
+documentation. If you can't find either then the answer is no.
+
+## Q2 : In version 0 the SF\_INFO struct had a pcmbitwidth field but version 1 does not. Why? {#Q002}
+
+This was dropped for a number of reasons:
+
+- pcmbitwidth makes little sense on compressed or floating point formats
+- with the new API you really don't need to know it
+
+As documented [here](api.md#note-1) there is now a well defined behaviour which
+ensures that no matter what the bit width of the source file, the scaling always
+does something sensible. This makes it safe to read 8, 16, 24 and 32 bit PCM
+files using `sf_read_short()` and always have the optimal behaviour.
+
+## Q3 : Compiling is really slow on MacOS X. Why? {#Q003}
+
+When you configure and compile libsndfile, it uses the /bin/sh shell for a
+number of tasks (ie configure script and libtool). Older versions of OS X
+(10.2?) shipped a really crappy Bourne shell as /bin/sh which resulted in
+**really** slow compiles. Newer version of OS X ship GNU Bash as /bin/sh and
+this answer doesn't apply in that case.
+
+To fix this I suggest that you install the GNU Bash shell, rename /bin/sh to
+/bin/sh.old and make a symlink from /bin/sh to the bash shell. Bash is designed
+to behave as a Bourne shell when it is called as /bin/sh.
+
+When I did this on my iBook running MacOS X, compile times dropped from 13
+minutes to 3 minutes.
+
+## Q4 : When trying to compile libsndfile on Solaris I get a "bad substitution" error on linking. Why? {#Q004}
+
+It seems that the Solaris Bourne shell disagrees with GNU libtool.
+
+To fix this I suggest that you install the GNU Bash shell, rename /bin/sh to
+/bin/sh.old and make a symlink from /bin/sh to the bash shell. Bash is designed
+to behave as a Bourne shell when it is called as /bin/sh.
+
+## Q5 : Why doesn't libsndfile do interleaving/de-interleaving? {#Q005}
+
+This problem is bigger than it may seem at first.
+
+For a stereo file, it is a pretty safe bet that a simple interleaving/
+de-interleaving could satisfy most users. However, for files with more than 2
+channels this is unlikely to be the case. If the user has a 4 channel file and
+want to play that file on a stereo output sound card they either want the first
+2 channels or they want some mixed combination of the 4 channels.
+
+When you add more channels, the combinations grow exponentially and it becomes
+increasingly difficult to cover even a sensible subset of the possible
+combinations. On top of that, coding any one style of interleaver/de-interleaver
+is trivial, while coding one that can cover all combinations is far from
+trivial. This means that this feature will not be added any time soon.
+
+## Q6 : What's the best format for storing temporary files? {#Q006}
+
+When you want to store temporary data there are a number of requirements:
+
+- A simple, easy to parse header.
+- The format must provide the fastest possible read and write rates (ie avoid
+  conversions and encoding/decoding).
+- The file format must be reasonably common and playable by most players.
+- Able to store data in either endian-ness.
+
+The format which best meets these requirements is AU, which allows data to be
+stored in any one of short, int, float and double (among others) formats.
+
+For instance, if an application uses float data internally, its temporary files
+should use a format of (SF_ENDIAN_CPU | SF_FORMAT_AU | SF_FORMAT_FLOAT) which
+will store big endian float data in big endian CPUs and little endian float data
+on little endian CPUs. Reading and writing this format will not require any
+conversions or byte swapping regardless of the host CPU.
+
+## Q7 : On Linux/Unix/MaxOS X, what's the best way of detecting the presence of libsndfile using autoconf? {#Q007}
+
+libsndfile uses the pkg-config (man pkg-config) method of registering itself
+with the host system. The best way of detecting its presence is using something
+like this in configure.ac (or configure.in):
+
+    PKG_CHECK_MODULES(SNDFILE, sndfile >= 1.0.2, ac_cv_sndfile=1, ac_cv_sndfile=0)
+
+    AC_DEFINE_UNQUOTED([HAVE_SNDFILE],${ac_cv_sndfile},
+        [Set to 1 if you have libsndfile.])
+
+    AC_SUBST(SNDFILE_CFLAGS)
+    AC_SUBST(SNDFILE_LIBS)
+
+This will automatically set the **SNDFILE_CFLAGS** and **SNDFILE_LIBS**
+variables which can be used in Makefile.am like this:
+
+    SNDFILE_CFLAGS = @SNDFILE_CFLAGS@
+    SNDFILE_LIBS = @SNDFILE_LIBS@
+
+If you install libsndfile from source, you will probably need to set the
+**PKG_CONFIG_PATH** environment variable as suggested at the end of the
+libsndfile configure process. For instance on my system I get this:
+
+    -=-=-=-=-=-=-=-=-=-= Configuration Complete =-=-=-=-=-=-=-=-=-=-
+
+        Configuration summary :
+
+        Version : ..................... 1.0.5
+        Experimental code : ........... no
+
+        Tools :
+
+        Compiler is GCC : ............. yes
+        GCC major version : ........... 3
+
+        Installation directories :
+
+        Library directory : ........... /usr/local/lib
+        Program directory : ........... /usr/local/bin
+        Pkgconfig directory : ......... /usr/local/lib/pkgconfig
+
+    Compiling some other packages against libsndfile may require
+    the addition of "/usr/local/lib/pkgconfig" to the
+    PKG_CONFIG_PATH environment variable.
+
+## Q8 : I have libsndfile installed and now I want to use it. I just want a simple Makefile\! What do I do? {#Q008}
+
+The **pkg-config** program makes finding the correct compiler flag values and
+library location far easier. During the installation of libsndfile, a file named
+**sndfile.pc** is installed in the directory **${libdir}/pkgconfig** (ie if
+libsndfile is installed in **/usr/local/lib**, **sndfile.pc** will be installed
+in **/usr/local/lib/pkgconfig/**).
+
+In order for pkg-config to find sndfile.pc it may be necessary to point the
+environment variable **PKG_CONFIG_PATH** in the right direction.
+
+    export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
+
+Then, to compile a C file into an object file, the command would be:
+
+    gcc `pkg-config --cflags sndfile` -c somefile.c
+
+and to link a number of objects into an executable that links against
+libsndfile, the command would be:
+
+    gcc `pkg-config --libs sndfile` obj1.o obj2.o -o program
+
+## Q9 : How about adding the ability to write/read sound files to/from memory buffers? {#Q009}
+
+This has been [added](api.md#open_virtual) for version 1.0.12.
+
+## Q10 : Reading a 16 bit PCM file as normalised floats and then writing them back changes some sample values. Why? {#Q010}
+
+This is caused by the fact that the conversion from 16 bit short to float is
+done by dividing by 32768 (0x8000 in hexadecimal) while the conversion from
+float to 16 bit short is done by multiplying by 32767 (0x7FFF in hex). So for
+instance, a value in a 16 bit PCM file of 20000 gets read as a floating point
+number of 0.6103515625 (20000.0 / 0x8000). Converting that back to a 16 bit
+short results in a value of 19999.3896484375 (0.6103515625 \* 0x7FFF) which then
+gets rounded down to 19999.
+
+You will notice that for this particular case, the error is 1 in 20000 or
+0.005%. Interestingly, for values of less than 16369, dividing by 0x8000
+followed by multiplying by 0x7FFF and then rounding the result, gives back the
+original value. It turns out that as long as the host operating system supplies
+the 1999 ISO C Standard functions **lrintf** and **lrint** (or a replacement has
+been supplied) then the maximum possible error is 1 in 16369 or about 0.006%.
+
+Regardless of the size of the error, the reason why this is done is rather
+subtle.
+
+In a file containing 16 bit PCM samples, the values are restricted to the range
+[-32768, 32767] while we want floating point values in the range [-1.0, 1.0].
+The only way to do this conversion is to do a floating point division by a value
+of 0x8000. Converting the other way, the only way to ensure that floating point
+values in the range [-1.0, 1.0] are within the valid range allowed by a 16 bit
+short is to multiply by 0x7FFF.
+
+Some people would say that this is a severe short-coming of libsndfile. I would
+counter that anybody who is constantly converting back and forth between 16 bit
+shorts and normalised floats is going to suffer other losses in audio quality
+that they should also be concerned about.
+
+Since this problem only occurs when converting between integer data on disk and
+normalized floats in the application, it can be avoided by using something other
+than normalized floats in the application. Alternatives to normalized floats are
+the **short** and **int** data types (ie using sf_read_short or sf_read_int) or
+using un-normalized floats (see
+[SFC_SET_NORM_FLOAT](command.html#sfc_set_norm_float)).
+
+Another way to deal with this problem is to consider 16 bit short data as a
+final destination format only, not as an intermediate storage format. All
+intermediate data (ie which is going to be processed further) should be stored
+in floating point format which is supported by all of the most common file
+formats. If floating point files are considered too large (2 times the size of a
+16 bit PCM file), it would also be possible to use 24 bit PCM as an intermediate
+storage format (and which is also supported by most common file types).
+
+## Q11 : I'm having problems with u-law encoded WAV files generated by libsndfile in Winamp. Why? {#Q011}
+
+This is actually a Winamp problem. The official Microsoft spec suggests that the
+'fmt ' chunk should be 18 bytes. Unfortunately at least one of Microsoft's own
+applications (Sound Recorder on Win98 I believe) did not accept 18 bytes 'fmt '
+chunks.
+
+Michael Lee did some experimenting and found that:
+
+> I have checked that Windows Media Player 9, QuickTime Player 6.4, RealOne
+> Player 2.0 and GoldWave 5.06 can all play u-law files with 16-byte or 18-byte
+> 'fmt ' chunk. Only Winamp (2.91) and foobar2000 are unable to play u-law files
+> with 16-byte 'fmt ' chunk.
+
+Even this is a very small sampling of all the players out there. For that reason
+it is probably not a good idea to change this now because there is the risk of
+breaking something that currently works.
+
+## Q12 : I'm looking at sf_read*. What are items? What are frames? {#Q012}
+
+An `item` is a single sample of the data type you are reading; ie a single
+`short` value for `sf_read_short` or a single `float` for `sf_read_float`.
+
+For a sound file with only one channel, a frame is the same as a item (ie a
+single sample) while for multi channel sound files, a single frame contains a
+single item for each channel.
+
+Here are two simple, correct examples, both of which are assumed to be working
+on a stereo file, first using items:
+
+```c
+#define CHANNELS 2
+short data [CHANNELS * 100] ;
+sf_count items_read = sf_read_short (file, data, 200) ;
+assert (items_read == 200) ;
+```
+
+and now reading the exact same amount of data using frames:
+
+```c
+#define CHANNELS 2
+short data [CHANNELS * 100] ;
+sf_count frames_read = sf_readf_short (file, data, 100) ;
+assert (frames_read == 100) ;
+```
+
+## Q13 : Why can't libsndfile open this Sound Designer II (SD2) file? {#Q013}
+
+This is somewhat complicated. First some background.
+
+SD2 files are native to the Apple Macintosh platform and use features of the Mac
+filesystem (file resource forks) to store the file's sample rate, number of
+channels, sample width and more. When you look at a file and its resource fork
+on Mac OS X it looks like this:
+
+    -rw-r--r--  1 erikd erikd   46512 Oct 18 22:57 file.sd2
+    -rw-r--r--  1 erikd erikd     538 Oct 18 22:57 file.sd2/rsrc
+
+Notice how the file itself looks like a directory containing a single file named
+**rsrc**. When libsndfile is compiled for MacOS X, it should open (for write and
+read) SD2 file with resource forks like this without any problems. It will also
+handle files with the resource fork in a separate file as described below.
+
+When SD2 files are moved to other platforms, the resource fork of the file can
+sometimes be dropped altogether. All that remains is the raw audio data and no
+information about the number of channels, sample rate or bit width which makes
+it a little difficult for libsndfile to open the file.
+
+However, it is possible to safely move an SD2 file to a Linux or Windows
+machine. For instance, when an SD2 file is copied from inside MacOS X to a
+windows shared directory or a Samba share (ie Linux), MacOS X is clever enough
+to store the resource fork of the file in a separate hidden file in the same
+directory like this:
+
+    -rw-r--r--  1 erikd erikd     538 Oct 18 22:57 ._file.sd2
+    -rw-r--r--  1 erikd erikd   46512 Oct 18 22:57 file.sd2
+
+Regardless of what platform it is running on, when libsndfile is asked to open a
+file named **"foo"** and it can't recognize the file type from the data in the
+file, it will attempt to open the resource fork and if that fails, it then tries
+to open a file named **"._foo"** to see if the file has a valid resource fork.
+This is the same regardless of whether the file is being opened for read or
+write.
+
+In short, libsndfile should open SD2 files with a valid resource fork on all of
+the platforms that libsndfile supports. If a file has lost its resource fork,
+the only option is the open the file using the SF_FORMAT_RAW option and guessing
+its sample rate, channel count and bit width.
+
+Occasionally, when SD2 files are moved to other systems, the file is
+[BinHexed](http://www.macdisk.com/binhexen.php3) which wraps the resource fork
+and the data fork together. For these files, it would be possible to write a
+BinHex parser but there is not a lot to gain considering how rare these BinHexed
+SD2 files are.
+
+## Q14 : I'd like to statically link libsndfile to my closed source application. Can I buy a license so that this is possible? {#Q014}
+
+Unfortunately no. libsndfile contains code written by other people who have
+agreed that their code be used under the GNU LGPL but no more. Even if they were
+to agree, there would be significant difficulties in dividing up the payments
+fairly.
+
+The **only** way you can legally use libsndfile as a statically linked library
+is if your application is released under the GNU GPL or LGPL.
+
+## Q15 : My program is crashing during a call to a function in libsndfile. Is this a bug in libsndfile? {#Q015}
+
+libsndfile is being used by large numbers of people all over the world without
+any problems like this. That means that it is much more likely that your code
+has a bug than libsndfile. However, it is still possible that there is a bug in
+libsndfile.
+
+To figure out whether it is your code or libsndfile you should do the following:
+
+- Make sure you are compiling your code with warnings switched on and that you
+  fix as many warnings as possible. With the GNU compiler (gcc) I would
+  recommend at least **-W -Wall -Werror** which will force you to fix all
+  warnings before you can run the code.
+- Try using a memory debugger. [Valgrind](http://valgrind.kde.org/) on x86 Linux
+  is excellent. [Purify](http://www.ibm.com/software/awdtools/purify/) also has
+  a good reputation.
+- If the code is clean after the above two steps and you still get a crash in
+  libsndfile, then send me a small snippet of code (no more than 30-40 lines)
+  which includes the call to sf_open() and also shows how all variables passed
+  to/returned from sf_open() are defined.
+
+## Q16 : Will you accept a fix for compiling libsndfile with compiler X? {#Q016}
+
+If compiler X is a C++ compiler then no. C and C++ are different enough to make
+writing code that compiles as valid C and valid C++ too difficult. I would
+rather spend my time fixing bugs and adding features.
+
+If compiler X is a C compiler then I will do what I can as long as that does not
+hamper the correctness, portability and maintainability of the existing code. It
+should be noted however that libsndfile uses features specified by the 1999 ISO
+C Standard. This can make compiling libsndfile with some older compilers
+difficult.
+
+## Q17 : Can libsndfile read/write files from/to UNIX pipes? {#Q017}
+
+Yes, libsndfile can read files from pipes. Unfortunately, the write case is much
+more complicated.
+
+File formats like AIFF and WAV have information at the start of the file (the
+file header) which states the length of the file, the number of sample frames
+etc. This information must be filled in correctly when the file header is
+written, but this information is not reliably known until the file is closed.
+This means that libsndfile cannot write AIFF, WAV and many other file types to a
+pipe.
+
+However, there is at least one file format (AU) which is specifically designed
+to be written to a pipe. Like AIFF and WAV, AU has a header with a sample frames
+field, but it is specifically allowable to set that frames field to 0x7FFFFFFF
+if the file length is not known when the header is written. The AU file format
+can also hold data in many of the standard formats (ie SF_FORMAT_PCM_16,
+SF_FORMAT_PCM_24, SF_FORMAT_FLOAT etc) as well as allowing data in both big and
+little endian format.
+
+See also [FAQ Q6](#Q006).
+
+## Q18 : Is it possible to build a Universal Binary on Mac OS X? {#Q018}
+
+Yes, but you must do two separate configure/build/test runs; one on PowerPC and
+one on Intel. It is then possible to merge the binaries into a single universal
+binary using one of the programs in the Apple tool chain.
+
+It is **not** possible to build a working universal binary via a single
+compile/build run on a single CPU.
+
+The problem is that the libsndfile build process detects features of the CPU its
+being built for during the configure process and when building a universal
+binary, configure is only run once and that data is then used for both CPUs.
+That configure data will be wrong for one of those CPUs. You will still be able
+to compile libsndfile, and the test suite will pass on the machine you compiled
+it on. However, if you take the universal binary test suite programs compiled on
+one CPU and run them on the other, the test suite will fail.
+
+Part of the problem is that the CPU endian-ness is detected at configure time.
+Yes, I know the Apple compiler defines one of the macros \_\_LITTLE\_ENDIAN\_\_
+and \_\_BIG\_ENDIAN\_\_, but those macros are not part of the 1999 ISO C
+Standard and they are not portable.
+
+Endian issues are not the only reason why the cross compiled binary will fail.
+The configure script also detects other CPU specific idiosyncrasies to provide
+more optimized code.
+
+Finally, the real show stopper problem with universal binaries is the problem
+with the test suite. libsndfile contains a huge, comprehensive test suite. When
+you compile a universal binary and run the test suite, you only test the native
+compile. The cross compiled binary (the one with the much higher chance of
+having problems) cannot be tested.
+
+Now, if you have read this far you're probably thinking there must be a way to
+fix this and there probably is. The problem is that its a hell of a lot of work
+and would require significant changes to the configure process, the internal
+code and the test suite. In addition, these changes must not break compilation
+on any of the platforms libsndfile is currently working on.
+
+## Q19 : I have project files for Visual Studio / XCode / Whatever. Why don't you distribute them with libsndfile? {#Q019}
+
+Use CMake project.
+
+## Q20 : Why doesn't libsndfile support MP3? {#Q020}
+
+~~In the past, MP3 was not supported because the technology behind MP3 was
+patented. Those patents have now expired and there is an
+[open ticket](https://github.com/libsndfile/libsndfile/issues/258) to implement
+MP3 support.~~
+
+**Update :** Starting from version 1.1.0 libsndfile supports MP3 format.
+
+## Q21 : How do I use libsndfile in a closed source or commercial program and comply with the license? {#Q021}
+
+Here is a checklist of things you need to do to make sure your use of libsndfile
+in a closed source or commercial project complies with the license libsndfile is
+released under, the GNU Lesser General Public License (LGPL):
+
+- Make sure you are linking to libsndfile as a shared library (Linux and Unix
+  systems), Dynamic Link Library (Microsoft Windows) or dynlib (Mac OS X). If
+  you are using some other operating system that doesn't allow dynamically
+  linked libraries, you will not be able to use libsndfile unless you release
+  the source code to your program.
+- In the licensing documentation for your program, add a statement that your
+  software depends on libsndfile and that libsndfile is released under the GNU
+  Lesser General Public License, either
+  [version 2.1](http://www.gnu.org/licenses/lgpl-2.1.txt) or optionally
+  [version 3](http://www.gnu.org/licenses/lgpl.txt).
+- Include the text for both versions of the license, possibly as separate files
+  named libsndfile_lgpl_v2_1.txt and libsndfile_lgpl_v3.txt.
+
+## Q22 : What versions of Windows does libsndfile work on? {#Q022}
+
+New versions of libsndfile binary releases require Wiindows Vista. If you need
+Windows XP support, you can build DLL from sources, we don't use specific WinXP
+features.
+
+## Q23 : I'm cross compiling libsndfile for another platform. How can I run the test suite? {#Q023}
+
+Since version 1.0.21 the top level Makefile has an extra make target,
+'test-tarball'. Building this target creates a tarball called called:
+
+    ` libsndfile-testsuite-${host_triplet}-${version}.tar.gz`
+
+in the top level directory. This tarball can then be copied to the target
+platform. Once untarred and test script `test_wrapper.sh` can be run from the
+top level of the extracted tarball.
diff --git a/docs/_config.yml b/docs/_config.yml
new file mode 100644
index 0000000..4c626e0
--- /dev/null
+++ b/docs/_config.yml
@@ -0,0 +1,8 @@
+---
+author: "The libsndfile team"
+version: "1.2.2"
+
+markdown: kramdown
+plugins:
+  - jekyll-default-layout
+---
diff --git a/docs/_includes/logo.html b/docs/_includes/logo.html
new file mode 100644
index 0000000..f17a034
--- /dev/null
+++ b/docs/_includes/logo.html
@@ -0,0 +1 @@
+<img src="libsndfile.jpg" class="image-logo" height=98 width=367 alt="Logo">
diff --git a/docs/_layouts/default.html b/docs/_layouts/default.html
new file mode 100644
index 0000000..76c2cb8
--- /dev/null
+++ b/docs/_layouts/default.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="utf-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>{{ page.title }}</title>
+  <link rel="stylesheet" href="libsndfile.css" type="text/css" media="all">
+  <link rel="stylesheet" href="print.css" type="text/css" media="print">
+</head>
+  <body>
+    {{ content }}
+    <footer>
+      <hr>
+      <p>The libsndfile home page is <a href="http://libsndfile.github.io/libsndfile/">here</a>.</p>
+      <p>The libsndfile version: {{ site.version }}.</p>
+    </footer>
+  </body>
+</html>
diff --git a/docs/_layouts/home.html b/docs/_layouts/home.html
new file mode 100644
index 0000000..6c8119d
--- /dev/null
+++ b/docs/_layouts/home.html
@@ -0,0 +1,34 @@
+---
+layout: default
+keywords: WAV AIFF AU SVX PAF NIST W64 libsndfile sound audio dsp Linux
+robots: nofollow
+title: libsndfile home page
+---
+
+<footer>
+  {% include logo.html %}
+
+  <nav>
+    <a href="#history">History</a> -+-
+    <a href="#features">Features</a> -+-
+    <a href="#similar-or-related-projects">Similar or related projects</a> -+-
+    <a href="https://github.com/libsndfile/libsndfile/blob/master/CHANGELOG.md">News</a>
+    <br>
+    <a href="development.html">Development</a> -+-
+    <a href="api.html">Programming interface</a> -+-
+    <a href="bugs.html">Bug reporting</a> -+-
+    <a href="#download">Download</a>
+    <br>
+    <a href="FAQ.html">FAQ</a> -+-
+    <a href="lists.html">Mailing lists</a> -+-
+    <a href="#licensing">Licensing information</a> -+-
+    <a href="#see-also">See also</a>
+  </nav>
+  <br><br>
+</footer>
+
+<main>
+  <article>
+    {{ content }}
+  </article>
+</main>
diff --git a/docs/_layouts/page.html b/docs/_layouts/page.html
new file mode 100644
index 0000000..21c7d9b
--- /dev/null
+++ b/docs/_layouts/page.html
@@ -0,0 +1,10 @@
+---
+layout: default
+---
+
+
+<main>
+  <article>
+    {{ content }}
+  </article>
+</main>
diff --git a/docs/api.md b/docs/api.md
new file mode 100644
index 0000000..e17f5ad
--- /dev/null
+++ b/docs/api.md
@@ -0,0 +1,744 @@
+---
+layout: page
+title: The libsndfile API
+---
+
+# libsndfile
+
+Libsndfile is a library designed to allow the reading and writing of many different sampled sound file formats (such as
+MS Windows WAV and the Apple/SGI AIFF format) through one standard library interface.
+
+During read and write operations, formats are seamlessly converted between the format the application program has
+requested or supplied and the file's data format. The application programmer can remain blissfully unaware of issues
+such as file endian-ness and data format. See [Note 1](#note-1) and [Note 2](#note-2).
+
+Every effort is made to keep these documents up-to-date, error free and unambiguous. However, since maintaining the
+documentation is the least fun part of working on libsndfile, these docs can and do fall behind the behaviour of the
+library. If any errors, omissions or ambiguities are found, please notify me (erikd) at mega-nerd dot com.
+
+To supplement this reference documentation, there are simple example programs included in the source code tarball. The
+test suite which is also part of the source code tarball is also a good place to look for the correct usage of the
+library functions.
+
+**Finally, if you think there is some feature missing from libsndfile, check that it isn't already implemented (and
+documented) [here](command.md).**
+
+## Synopsis
+
+```c
+#include <stdio.h>;
+#include <sndfile.h>;
+```
+
+| Name                                                                                                        | Description                                    |
+|:------------------------------------------------------------------------------------------------------------|:---------------------------------------        |
+| [sf_open, sf_wchar_open](#open)                                                                             | File open functions.                           |
+| [sf_open_fd](#open_fd)                                                                                      | Open sound file using file descriptor.         |
+| [sf_open_virtual](#open_virtual)                                                                            | Open sound file using virtual API.             |
+| [sf_format_check](#check)                                                                                   | Validate sound file info.                      |
+| [sf_seek](#seek)                                                                                            | Seek position in sound file.                   |
+| [sf_command](command.md)                                                                                    | Command interface.                             |
+| [sf_error, sf_strerror, sf_error_number, sf_perror, sf_error_str](#error)                                   | Error functions.                               |
+| [sf_close](#close)                                                                                          | File close function.                           |
+| [sf_write_sync](#write_sync)                                                                                | Write sync function.                           |
+| [sf_read_short, sf_read_int, sf_read_float, sf_read_double](#read)                                          | File items read functions.                     |
+| [sf_readf_short, sf_readf_int, sf_readf_float, sf_readf_double](#readf)                                     | File frames read functions.                    |
+| [sf_write_short, sf_write_int, sf_write_float, sf_write_double](#write)                                     | File items write functions.                    |
+| [sf_writef_short, sf_writef_int, sf_writef_float, sf_writef_double](#writef)                                | File frames write functions.                   |
+| [sf_read_raw, sf_write_raw](#raw)                                                                           | Raw read/write functions.                      |
+| [sf_get_string, sf_set_string](#string)                                                                     | Functions for reading and writing string data. |
+| [sf_version_string](#version_string)                                                                        | Retrieve library version string.                |
+| [sf_current_byterate](#current_byterate)                                                                    | Retrieve current byterate.                     |
+| [sf_set_chunk, sf_get_chunk_iterator, sf_next_chunk_iterator, sf_get_chunk_size, sf_get_chunk_data](#chunk) | RIFF chunks API.                               |
+
+SNDFILE* is an anonymous pointer to data which is private to the library.
+
+## File Open Function {#open}
+
+```c
+SNDFILE*  sf_open    (const char *path, int mode, SF_INFO *sfinfo) ;
+```
+
+The sf_open() function opens the sound file at the specified path. The filename is byte encoded, but may be utf-8 on
+Linux, while on Mac OS X it will use the filesystem character set. On Windows, there is also a Windows specific
+sf_wchar_open() that takes a UTF16_BE encoded filename.
+
+```c
+SNDFILE*  sf_wchar_open (LPCWSTR wpath, int mode, SF_INFO *sfinfo) ;
+```
+
+The SF_INFO structure is for passing data between the calling function and the library when opening a file for reading
+or writing. It is defined in sndfile.h as follows:
+
+```c
+typedef struct
+{    sf_count_t  frames ;     /* Used to be called samples. */
+        int         samplerate ;
+        int         channels ;
+        int         format ;
+        int         sections ;
+        int         seekable ;
+    } SF_INFO ;
+```
+
+The mode parameter for this function can be any one of the following three values:
+
+SFM_READ
+: read only mode
+
+SFM_WRITE
+: write only mode
+
+SFM_RDWR
+: read/write mode
+
+When opening a file for read, the **format** field should be set to zero before
+calling **sf_open**(). The only exception to this is the case of RAW files where
+the caller has to set the **samplerate**, **channels** and **format** fields to
+valid values. All other fields of the structure are filled in by the library.
+
+**Note:** The libsndfile library will reject values ​​for field **channels** that
+are greater than `1024`. These value ​​represent the maximum theoretical limit
+and may be less for specific formats.
+
+When opening a file for write, the caller must fill in structure members
+**samplerate**, **channels**, and **format**.
+
+The **format** field in the above **SF_INFO** structure is made up of the
+bit-wise OR of a major format type (values between 0x10000 and 0x08000000), a
+minor format type (with values less than 0x10000) and an optional endian-ness
+value. The currently understood formats are listed in *sndfile.h* as follows and
+also include bitmasks for separating major and minor file types. Not all
+combinations of endian-ness and major and minor file types are valid.
+
+| Name                     | Value      | Description                                |
+|:-------------------------|:-----------|:-------------------------------------------|
+| **Major formats.**                                                                 |
+| SF_FORMAT_WAV            | 0x010000   | Microsoft WAV format (little endian).      |
+| SF_FORMAT_AIFF           | 0x020000   | Apple/SGI AIFF format (big endian).        |
+| SF_FORMAT_AU             | 0x030000   | Sun/NeXT AU format (big endian).           |
+| SF_FORMAT_RAW            | 0x040000   | RAW PCM data.                              |
+| SF_FORMAT_PAF            | 0x050000   | Ensoniq PARIS file format.                 |
+| SF_FORMAT_SVX            | 0x060000   | Amiga IFF / SVX8 / SV16 format.            |
+| SF_FORMAT_NIST           | 0x070000   | Sphere NIST format.                        |
+| SF_FORMAT_VOC            | 0x080000   | VOC files.                                 |
+| SF_FORMAT_IRCAM          | 0x0A0000   | Berkeley/IRCAM/CARL                        |
+| SF_FORMAT_W64            | 0x0B0000   | Sonic Foundry's 64 bit RIFF/WAV            |
+| SF_FORMAT_MAT4           | 0x0C0000   | Matlab (tm) V4.2 / GNU Octave 2.0          |
+| SF_FORMAT_MAT5           | 0x0D0000   | Matlab (tm) V5.0 / GNU Octave 2.1          |
+| SF_FORMAT_PVF            | 0x0E0000   | Portable Voice Format                      |
+| SF_FORMAT_XI             | 0x0F0000   | Fasttracker 2 Extended Instrument          |
+| SF_FORMAT_HTK            | 0x100000   | HMM Tool Kit format                        |
+| SF_FORMAT_SDS            | 0x110000   | Midi Sample Dump Standard                  |
+| SF_FORMAT_AVR            | 0x120000   | Audio Visual Research                      |
+| SF_FORMAT_WAVEX          | 0x130000   | MS WAVE with WAVEFORMATEX                  |
+| SF_FORMAT_SD2            | 0x160000   | Sound Designer 2                           |
+| SF_FORMAT_FLAC           | 0x170000   | FLAC lossless file format                  |
+| SF_FORMAT_CAF            | 0x180000   | Core Audio File format                     |
+| SF_FORMAT_WVE            | 0x190000   | Psion WVE format                           |
+| SF_FORMAT_OGG            | 0x200000   | Xiph OGG container                         |
+| SF_FORMAT_MPC2K          | 0x210000   | Akai MPC 2000 sampler                      |
+| SF_FORMAT_RF64           | 0x220000   | RF64 WAV file                              |
+| SF_FORMAT_MPEG           | 0x230000   | MPEG-1/2 audio stream                      |
+| **Subtypes.**                                                                      |
+| SF_FORMAT_PCM_S8         | 0x0001     | Signed 8 bit data                          |
+| SF_FORMAT_PCM_16         | 0x0002     | Signed 16 bit data                         |
+| SF_FORMAT_PCM_24         | 0x0003     | Signed 24 bit data                         |
+| SF_FORMAT_PCM_32         | 0x0004     | Signed 32 bit data                         |
+| SF_FORMAT_PCM_U8         | 0x0005     | Unsigned 8 bit data (WAV and RAW only)     |
+| SF_FORMAT_FLOAT          | 0x0006     | 32 bit float data                          |
+| SF_FORMAT_DOUBLE         | 0x0007     | 64 bit float data                          |
+| SF_FORMAT_ULAW           | 0x0010     | U-Law encoded.                             |
+| SF_FORMAT_ALAW           | 0x0011     | A-Law encoded.                             |
+| SF_FORMAT_IMA_ADPCM      | 0x0012     | IMA ADPCM.                                 |
+| SF_FORMAT_MS_ADPCM       | 0x0013     | Microsoft ADPCM.                           |
+| SF_FORMAT_GSM610         | 0x0020     | GSM 6.10 encoding.                         |
+| SF_FORMAT_VOX_ADPCM      | 0x0021     | OKI / Dialogix ADPCM                       |
+| SF_FORMAT_NMS_ADPCM_16   | 0x0022     | 16kbs NMS G721-variant encoding.           |
+| SF_FORMAT_NMS_ADPCM_24   | 0x0023     | 24kbs NMS G721-variant encoding.           |
+| SF_FORMAT_NMS_ADPCM_32   | 0x0024     | 32kbs NMS G721-variant encoding.           |
+| SF_FORMAT_G721_32        | 0x0030     | 32kbs G721 ADPCM encoding.                 |
+| SF_FORMAT_G723_24        | 0x0031     | 24kbs G723 ADPCM encoding.                 |
+| SF_FORMAT_G723_40        | 0x0032     | 40kbs G723 ADPCM encoding.                 |
+| SF_FORMAT_DWVW_12        | 0x0040     | 12 bit Delta Width Variable Word encoding. |
+| SF_FORMAT_DWVW_16        | 0x0041     | 16 bit Delta Width Variable Word encoding. |
+| SF_FORMAT_DWVW_24        | 0x0042     | 24 bit Delta Width Variable Word encoding. |
+| SF_FORMAT_DWVW_N         | 0x0043     | N bit Delta Width Variable Word encoding.  |
+| SF_FORMAT_DPCM_8         | 0x0050     | 8 bit differential PCM (XI only)           |
+| SF_FORMAT_DPCM_16        | 0x0051     | 16 bit differential PCM (XI only)          |
+| SF_FORMAT_VORBIS         | 0x0060     | Xiph Vorbis encoding.                      |
+| SF_FORMAT_OPUS           | 0x0064     | Xiph/Skype Opus encoding.                  |
+| SF_FORMAT_ALAC_16        | 0x0070     | Apple Lossless Audio Codec (16 bit).       |
+| SF_FORMAT_ALAC_20        | 0x0071     | Apple Lossless Audio Codec (20 bit).       |
+| SF_FORMAT_ALAC_24        | 0x0072     | Apple Lossless Audio Codec (24 bit).       |
+| SF_FORMAT_ALAC_32        | 0x0073     | Apple Lossless Audio Codec (32 bit).       |
+| SF_FORMAT_MPEG_LAYER_I   | 0x0080     | MPEG-1 Audio Layer I.                      |
+| SF_FORMAT_MPEG_LAYER_II  | 0x0081     | MPEG-1 Audio Layer II.                     |
+| SF_FORMAT_MPEG_LAYER_III | 0x0082     | MPEG-2 Audio Layer III.                    |
+| **Endian-ness options.**                                                           |
+| SF_ENDIAN_FILE           | 0x00000000 | Default file endian-ness.                  |
+| SF_ENDIAN_LITTLE         | 0x10000000 | Force little endian-ness.                  |
+| SF_ENDIAN_BIG            | 0x20000000 | Force big endian-ness.                     |
+| SF_ENDIAN_CPU            | 0x30000000 | Force CPU endian-ness.                     |
+| SF_FORMAT_SUBMASK        | 0x0000FFFF |                                            |
+| SF_FORMAT_TYPEMASK       | 0x0FFF0000 |                                            |
+| SF_FORMAT_ENDMASK        | 0x30000000 |                                            |
+
+Every call to **sf_open**() should be matched with a call to
+[**sf_close**()](#close) to free up memory allocated during the call to **sf_open**().
+
+On success, the sf_open function returns a non-NULL pointer which should be passed as the first parameter to all
+subsequent libsndfile calls dealing with that audio file. On fail, the sf_open function returns a NULL pointer. An
+explanation of the error can obtained by passing NULL to [**sf_strerror**()](#error).
+
+### File Descriptor Open {#open_fd}
+
+```c
+SNDFILE*  sf_open_fd (int fd, int mode, SF_INFO *sfinfo, int close_desc) ;
+```
+
+**Note:** On Microsoft Windows, this function does not work if the application
+and the libsndfile DLL are linked to different versions of the Microsoft C
+runtime DLL.
+
+The second open function takes a file descriptor of a file that has already been
+opened. Care should be taken to ensure that the mode of the file represented by
+the descriptor matches the mode argument. This function is useful in the
+following circumstances:
+
+* Opening temporary files securely (ie use the **tmpfile**() to return a FILE*
+  pointer and then using fileno() to retrieve the file descriptor which is then
+  passed to libsndfile).
+* Opening files with file names using OS specific character encodings and then
+  passing the file descriptor to **sf_open_fd**().
+* Opening sound files embedded within larger files. [More info](embedded_files.md).
+
+Every call to `sf_open_fd`() should be matched with a call to sf_close() to free
+up memory allocated during the call to sf_open_fd().
+
+When sf_close() is called, the file descriptor is only closed if the
+**close_desc** parameter was TRUE when the sf_open_fd() function was called.
+
+On success, the sf_open_fd() function returns a non-NULL pointer which should be
+passed as the first parameter to all subsequent libsndfile calls dealing with
+that audio file. On fail, the sf_open_fd() function returns a NULL pointer.
+
+### Virtual File Open Function {#open_virtual}
+
+```c
+SNDFILE*    sf_open_virtual (SF_VIRTUAL_IO *sfvirtual, int mode, SF_INFO *sfinfo, void *user_data) ;
+```
+
+Opens a soundfile from a virtual file I/O context which is provided by the
+caller. This is usually used to interface libsndfile to write/read from memory
+with a stream or buffer based system. Apart from the sfvirtual and the user_data
+parameters this function behaves like [sf_open()](#open).
+
+```c
+    typedef struct
+    {    sf_vio_get_filelen  get_filelen ;
+          sf_vio_seek         seek ;
+          sf_vio_read         read ;
+          sf_vio_write        write ;
+          sf_vio_tell         tell ;
+    } SF_VIRTUAL_IO ;
+```
+
+Libsndfile calls the callbacks provided by the SF_VIRTUAL_IO structure when
+opening, reading and writing to the virtual file context. The user_data pointer
+is a user defined context which will be available in the callbacks.
+
+```c
+typedef sf_count_t  (*sf_vio_get_filelen) (void *user_data) ;
+typedef sf_count_t  (*sf_vio_seek)        (sf_count_t offset, int whence, void *user_data) ;
+typedef sf_count_t  (*sf_vio_read)        (void *ptr, sf_count_t count, void *user_data) ;
+typedef sf_count_t  (*sf_vio_write)       (const void *ptr, sf_count_t count, void *user_data) ;
+typedef sf_count_t  (*sf_vio_tell)        (void *user_data) ;
+```
+
+#### sf_vio_get_filelen
+
+```c
+typedef sf_count_t  (*sf_vio_get_filelen) (void *user_data) ;
+```
+
+The virtual file context must return the length of the virtual file in bytes.
+
+#### sf_vio_seek
+
+```c
+typedef sf_count_t  (*sf_vio_seek)        (sf_count_t offset, int whence, void *user_data) ;
+```
+
+The virtual file context must seek to offset using the seek mode provided by
+whence which is one of SEEK_CUR, SEEK_SET, SEEK_END.
+
+The return value must contain the new offset in the file.
+
+#### sf_vio_read
+
+```c
+typedef sf_count_t  (*sf_vio_read)        (void *ptr, sf_count_t count, void *user_data) ;
+```
+
+The virtual file context must copy ("read") "count" bytes into the buffer
+provided by ptr and return the count of actually copied bytes.
+
+#### sf_vio_write
+
+```c
+typedef sf_count_t  (*sf_vio_write)       (const void *ptr, sf_count_t count, void *user_data) ;
+```
+
+The virtual file context must process "count" bytes stored in the buffer passed
+with ptr and return the count of actually processed bytes.
+
+#### sf_vio_tell
+
+```c
+typedef sf_count_t  (*sf_vio_tell)        (void *user_data) ;
+```
+
+Return the current position of the virtual file context.
+
+## Format Check Function {#chek}
+
+```c
+int  sf_format_check (const SF_INFO *info) ;
+```
+
+This function allows the caller to check if a set of parameters in the SF_INFO
+struct is valid before calling [sf_open](#open) (SFM_WRITE).
+
+sf_format_check() returns TRUE if the parameters are valid and FALSE otherwise.
+
+## File Seek Functions
+
+```c
+sf_count_t  sf_seek  (SNDFILE *sndfile, sf_count_t frames, int whence) ;
+```
+
+The file seek functions work much like lseek in unistd.h with the exception that
+the non-audio data is ignored and the seek only moves within the audio data
+section of the file. In addition, seeks are defined in number of (multichannel)
+frames. Therefore, a seek in a stereo file from the current position forward
+with an offset of 1 would skip forward by one sample of both channels.
+
+like lseek(), the whence parameter can be any one of the following three values:
+
+SEEK_SET
+: The offset is set to the start of the audio data plus offset (multichannel)
+frames.
+
+SEEK_CUR
+: The offset is set to its current location plus offset (multichannel) frames.
+
+SEEK_END
+: The offset is set to the end of the data plus offset (multichannel) frames.
+
+Internally, libsndfile keeps track of the read and write locations using
+separate read and write pointers. If a file has been opened with a mode of
+SFM_RDWR, bitwise OR-ing the standard whence values above with either SFM_READ
+or SFM_WRITE allows the read and write pointers to be modified separately.
+If the SEEK_* values are used on their own, the read and write pointers are
+both modified.
+
+Note that the frames offset can be negative and in fact should be when SEEK_END
+is used for the whence parameter.
+
+sf_seek will return the offset in (multichannel) frames from the start of the
+audio data or -1 if an error occurred (ie an attempt is made to seek beyond the
+start or end of the file).
+
+## Error Reporting Functions {#error}
+
+```c
+int sf_error (SNDFILE *sndfile) ;
+```
+
+This function returns the current error number for the given SNDFILE.
+
+The error number may be one of the following:
+
+| Name                        | Value |
+|:----------------------------|:------|
+| SF_ERR_NO_ERROR             | 0     |
+| SF_ERR_UNRECOGNISED_FORMAT  | 1     |
+| SF_ERR_SYSTEM               | 2     |
+| SF_ERR_MALFORMED_FILE       | 3     |
+| SF_ERR_UNSUPPORTED_ENCODING | 4     |
+
+or any one of many other internal error values.
+Applications should only test the return value against error values defined in
+\<sndfile.h\>; as the internal error values are subject to change at any time.
+For errors not in the above list, the function sf_error_number() can be used to
+convert it to an error string.
+
+```c
+const char* sf_strerror     (SNDFILE *sndfile) ;
+const char* sf_error_number (int errnum) ;
+```
+
+The error functions sf_strerror () and sf_error_number () convert the library's
+internal error enumerations into text strings.
+
+```c
+int sf_perror    (SNDFILE *sndfile) ;
+int sf_error_str (SNDFILE *sndfile, char* str, size_t len) ;
+```
+
+The functions sf_perror() and sf_error_str() are deprecated and will be dropped
+from the library at some later date.
+
+## File Close Function {#close}
+
+```c
+int sf_close (SNDFILE *sndfile) ;
+```
+
+The close function closes the file, deallocates its internal buffers and returns
+0 on success or an error value otherwise.
+
+## Write Sync Function {#write_sync}
+
+```c
+void  sf_write_sync  (SNDFILE *sndfile) ;
+```
+
+If the file is opened SFM_WRITE or SFM_RDWR, call the operating system's
+function to force the writing of all file cache buffers to disk. If the file is
+opened SFM_READ no action is taken.
+
+## File Read Functions {#read}
+
+```c
+sf_count_t sf_read_short  (SNDFILE *sndfile, short *ptr, sf_count_t items) ;
+sf_count_t sf_read_int    (SNDFILE *sndfile, int *ptr, sf_count_t items) ;
+sf_count_t sf_read_float  (SNDFILE *sndfile, float *ptr, sf_count_t items) ;
+sf_count_t sf_read_double (SNDFILE *sndfile, double *ptr, sf_count_t items) ;
+```
+
+{: #readf}
+```c
+sf_count_t sf_readf_short  (SNDFILE *sndfile, short *ptr, sf_count_t frames) ;
+sf_count_t sf_readf_int    (SNDFILE *sndfile, int *ptr, sf_count_t frames) ;
+sf_count_t sf_readf_float  (SNDFILE *sndfile, float *ptr, sf_count_t frames) ;
+sf_count_t sf_readf_double (SNDFILE *sndfile, double *ptr, sf_count_t frames) ;
+```
+
+The file read functions fill the array pointed to by ptr with the requested
+number of items or frames.
+
+For the frames-count functions, the frames parameter specifies the number of
+frames. A frame is just a block of samples, one for each channel.
+
+**Care must be taken to ensure that there is enough space in the array pointed
+to by ptr, to take (frames \* channels) number of items (shorts, ints, floats or
+doubles).**
+
+For the items-count functions, the items parameter must be an integer product
+of the number of channels or an error will occur. Here, an item is just a
+sample.
+
+Note: The only difference between the "items" and "frames" versions of each read
+function is the units in which the object count is specified - calling
+sf_readf_short() with a count argument of N, on a SNDFILE with C channels, is
+the same as calling sf_read_short with a count argument of N\*C. The buffer
+pointed to by "ptr" should be the same number of bytes in each case.
+
+Note: The data type used by the calling program and the data format of the file
+do not need to be the same. For instance, it is possible to open a 16 bit PCM
+encoded WAV file and read the data using sf_read_float(). The library seamlessly
+converts between the two formats on-the-fly. See [Note 1](#note-1).
+
+The sf_read_XXXX and sf_readf_XXXX functions return the number of items or
+frames read, respectively. Unless the end of the file was reached during the
+read, the return value should equal the number of objects requested. Attempts to
+read beyond the end of the file will not result in an error but will cause the
+read functions to return less than the number of objects requested or 0 if
+already at the end of the file. When the buffer is not is not completely filled,
+unused buffer space is filled by zeroes.
+
+## File Write Functions {#write}
+
+```c
+sf_count_t sf_write_short  (SNDFILE *sndfile, short *ptr, sf_count_t items) ;
+sf_count_t sf_write_int    (SNDFILE *sndfile, int *ptr, sf_count_t items) ;
+sf_count_t sf_write_float  (SNDFILE *sndfile, float *ptr, sf_count_t items) ;
+sf_count_t sf_write_double (SNDFILE *sndfile, double *ptr, sf_count_t items) ;
+```
+
+{: #writef}
+```c
+sf_count_t sf_writef_short  (SNDFILE *sndfile, short *ptr, sf_count_t frames) ;
+sf_count_t sf_writef_int    (SNDFILE *sndfile, int *ptr, sf_count_t frames) ;
+sf_count_t sf_writef_float  (SNDFILE *sndfile, float *ptr, sf_count_t frames) ;
+sf_count_t sf_writef_double (SNDFILE *sndfile, double *ptr, sf_count_t frames) ;
+```
+
+The file write functions write the data in the array pointed to by ptr to the
+file.
+
+For items-count functions, the items parameter specifies the size of the array
+and must be an integer product of the number of channels or an error will occur.
+
+For the frames-count functions, the array is expected to be large enough to hold
+a number of items equal to the product of frames and the number of channels.
+
+As with the read functions [above](#read), the only difference in the items and
+frames version of each write function is the units in which the buffer size is
+specified. Again, the data type used by the calling program and the data format
+of the file do not need to be the same ([Note 1](#note-1)).
+
+The sf_write_XXXX and sf_writef_XXXX functions respectively return the number of
+items or frames written (which should be the same as the items or frames
+parameter).
+
+## Raw File Read and Write Functions {#raw}
+
+```c
+sf_count_t sf_read_raw  (SNDFILE *sndfile, void *ptr, sf_count_t bytes) ;
+sf_count_t sf_write_raw (SNDFILE *sndfile, void *ptr, sf_count_t bytes) ;
+```
+
+**Note:** Unless you are writing an external decoder/encode that uses libsndfile
+to handle the file headers, you should not be using these functions.
+
+The raw read and write functions read raw audio data from the audio file (not to
+be confused with reading RAW header-less PCM files). The number of bytes read or
+written must always be an integer multiple of the number of channels multiplied
+by the number of bytes required to represent one sample from one channel.
+
+The raw read and write functions return the number of bytes read or written
+(which should be the same as the bytes parameter).
+
+**Note : The result of using of both regular reads/writes and raw reads/writes
+on compressed file formats other than SF_FORMAT_ALAW and SF_FORMAT_ULAW is
+undefined.**
+
+See also : [SFC_RAW_NEEDS_ENDSWAP](command.md#sfc_raw_needs_endswap).
+
+## Functions for Reading and Writing String Data {#string}
+
+```c
+const char* sf_get_string (SNDFILE *sndfile, int str_type) ;
+int         sf_set_string (SNDFILE *sndfile, int str_type, const char* str) ;
+```
+
+These functions allow strings to be set on files opened for write and to be
+retrieved from files opened for read where supported by the given file type. The
+**str_type** parameter can be any one of the following string types:
+
+| Name               | Value | Description   |
+|:-------------------|:------|:--------------|
+| SF_STR_TITLE       | 0x01  | Title.        |
+| SF_STR_COPYRIGHT   | 0x02  | Copyright.    |
+| SF_STR_SOFTWARE    | 0x03  | Software.     |
+| SF_STR_ARTIST      | 0x04  | Artist.       |
+| SF_STR_COMMENT     | 0x05  | Comment.      |
+| SF_STR_DATE        | 0x06  | Date.         |
+| SF_STR_ALBUM       | 0x07  | Album.        |
+| SF_STR_LICENSE     | 0x08  | License.      |
+| SF_STR_TRACKNUMBER | 0x09  | Track number. |
+| SF_STR_GENRE       | 0x10  | Genre.        |
+
+The sf_get_string() function returns the specified string if it exists and a
+NULL pointer otherwise. In addition to the string ids above, SF_STR_FIRST (==
+SF_STR_TITLE) and SF_STR_LAST (always the same as the highest numbers string id)
+are also available to allow iteration over all the available string ids.
+
+The sf_set_string() function sets the string data. It returns zero on success
+and non-zero on error.The error code can be converted to a string using
+sf_error_number().
+
+Strings passed to and retrieved from these two functions are assumed to be
+utf-8. However, while formats like Ogg/Vorbis and FLAC fully support utf-8,
+others like WAV and AIFF officially only support ASCII. Writing utf-8 strings to
+WAV and AIF files with libsndfile will work when read back with libsndfile, but
+may not work with other programs.
+
+The suggested method of dealing with tags retrieved using sf_get_string() is to
+assume they are utf-8. Similarly if you have a string in some exotic format like
+utf-16, it should be encoded to utf-8 before being written using libsndfile.
+
+## Function for retrieving library version {#version_string}
+
+```c
+const char *sf_version_string (void) ;
+```
+
+Return the library version string.
+
+## Function for retrieving current byterate {#current_byterate}
+
+```c
+int sf_current_byterate (SNDFILE *sndfile) ;
+```
+
+Return the current byterate at this point in the file. The byte rate in this
+case is the number of bytes per second of audio data. For instance, for a
+stereo, 18 bit PCM encoded file with an 16kHz sample rate, the byte rate
+would be 2 (stereo) \* 2 (two bytes per sample) * 16000 => 64000 bytes/sec.
+
+For some file formats the returned value will be accurate and exact, for some
+it will be a close approximation, for some it will be the average bitrate for
+the whole file and for some it will be a time varying value that was accurate
+when the file was most recently read or written.
+
+To get the bitrate, multiple this value by 8.
+
+`sf_current_byterate` returns byte per second or -1 if byterate is
+unknown.
+
+## Functions to get and set chunks from within a sound file
+
+These functions allow the getting and setting of chunks within a sound file (for
+those formats which allow it).
+
+These functions fail safely. Specifically, they will not allow you to overwrite
+existing chunks or add extra versions of format specific reserved chunks but
+should allow you to retrieve any and all chunks (may not be implemented for all
+chunks or all file formats).
+
+### sf_set_chunk
+
+```c
+int sf_set_chunk (SNDFILE *sndfile, const SF_CHUNK_INFO *chunk_info) ;
+```
+
+Set the specified chunk info (must be done before any audio data is written to
+the file). This will fail for format specific reserved chunks. The
+`chunk_info->data` pointer must be valid until the file is closed.
+
+The `SF_CHUNK_INFO` struct is documented as follows:
+
+```c
+struct SF_CHUNK_INFO
+{     char        id [64] ;   /* The chunk identifier. */
+    unsigned      id_size ;   /* The size of the chunk identifier. */
+    unsigned      datalen ;   /* The size of that data. */
+    void          *data ;     /* Pointer to the data. */
+} ;
+    typedef struct SF_CHUNK_INFO SF_CHUNK_INFO ;
+```
+
+`sf_set_chunk` returns `SF_ERR_NO_ERROR` on success or non-zero on failure.
+
+### sf_get_chunk_iterator
+
+```c
+SF_CHUNK_ITERATOR *
+sf_get_chunk_iterator (SNDFILE *sndfile, const SF_CHUNK_INFO *chunk_info) ;
+```
+
+Get an iterator for all chunks matching `chunk_info`.
+
+`SF_CHUNK_ITERATOR` is an opaque structure to an iterator over the all chunks of
+a given id and defined as follows:
+
+```c
+typedef	struct SF_CHUNK_ITERATOR SF_CHUNK_ITERATOR ;
+```
+
+The iterator will point to the first chunk matching `chunk_info`. Chunks are
+matching, if (`chunk_info->id`) matches the first (`chunk_info->id_size`) bytes
+of a chunk found in the `SNDFILE*` handle. If `chunk_info` is `NULL`, an
+iterator to all chunks in the `SNDFILE*` handle is returned. The values of
+`chunk_info->datalen` and `chunk_info->data` are ignored. If no matching chunks
+are found in the sndfile, `NULL` is returned.
+
+The returned iterator will stay valid until one of the following occurs:
+
+* The sndfile is closed.
+* A new chunk is added using [`sf_set_chunk()`](#sf_set_chunk).
+* Another chunk iterator function is called on the same `SNDFILE*`
+  handle that causes the iterator to be modified.
+
+The memory for the iterator belongs to the SNDFILE* handle and is freed when
+[sf_close](#close) is called.
+
+### sf_next_chunk_iterator
+
+```c
+sf_next_chunk_iterator (SF_CHUNK_ITERATOR * iterator) ;
+```
+
+Iterate through chunks by incrementing the iterator.
+
+Increments the iterator and returns a handle to the new one. After this call,
+iterator will no longer be valid, and you must use the newly returned handle
+from now on. The returned handle can be used to access the next chunk matching
+the criteria as defined in [sf_get_chunk_iterator](#sf_get_chunk_iterator).
+If iterator points to the last chunk, this will free all resources associated
+with iterator and return `NULL`. The returned iterator will stay valid until
+`sf_get_next_chunk_iterator` is called again, the sndfile is closed or a new
+chunk us added.
+
+### sf_get_chunk_size
+
+```c
+int
+sf_get_chunk_size (const SF_CHUNK_ITERATOR * it, SF_CHUNK_INFO * chunk_info) ;
+```
+
+Get the size of the specified chunk.
+
+If the specified chunk exists, the size will be returned in the `datalen` field
+of the `SF_CHUNK_INFO` struct. Additionally, the id of the chunk will be copied
+to the `id` field of the `SF_CHUNK_INFO` struct and it's `id_size` field will be
+updated accordingly.
+
+If the chunk doesn't exist `chunk_info->datalen` will be zero, and the `id` and
+`id_size` fields will be undefined.
+
+The function will return `SF_ERR_NO_ERROR` on success or non-zero on failure.
+
+### sf_get_chunk_data
+
+```c
+int
+sf_get_chunk_data (const SF_CHUNK_ITERATOR *it, SF_CHUNK_INFO *chunk_info) ;
+```
+
+Get the specified chunk data.
+
+If the specified chunk exists, up to `chunk_info->datalen` bytes of the chunk
+data will be copied into the `chunk_info->data` buffer (allocated by the caller)
+and the `chunk_info->datalen` field updated to reflect the size of the data. The
+`id` and `id_size` field will be updated according to the retrieved chunk. If
+the chunk doesn't exist `chunk_info->datalen` will be zero, and the `id` and
+`id_size` fields will be undefined.
+
+The function will return `SF_ERR_NO_ERROR` on success or non-zero on failure.
+
+## Note 1
+
+When converting between integer PCM formats of differing size (e.g. using
+sf_read_int() to read a 16 bit PCM encoded WAV file) libsndfile obeys one simple
+rule:
+
+Whenever integer data is moved from one sized container to another sized
+container, the most significant bit in the source container will become the most
+significant bit in the destination container.
+
+When converting between integer data and floating point data, different rules
+apply. The default behaviour when reading floating point data (sf_read_float()
+or sf_read_double ()) from a file with integer data is normalisation. Regardless
+of whether data in the file is 8, 16, 24 or 32 bit wide, the data will be read
+as floating point data in the range [-1.0, 1.0]. Similarly, data in the range
+[-1.0, 1.0] will be written to an integer PCM file so that a data value of 1.0
+will be the largest allowable integer for the given bit width. This
+normalisation can be turned on or off using the [sf_command](command.md)
+interface.
+
+## Note 2
+
+Reading a file containing floating point data (allowable with WAV, AIFF, AU and
+other file formats) using integer read methods (sf_read_short() or
+sf_read_int()) can produce unexpected results. For instance the data in the file
+may have a maximum absolute value &lt; 1.0 which would mean that all sample
+values read from the file will be zero. In order to read these files correctly
+using integer read methods, it is recommended that you use the
+[sf_command](command.md) interface, a command of
+[SFC_SET_SCALE_FLOAT_INT_READ](command.md#sfc_set_scale_float_int_read) and a
+parameter of SF_TRUE to force correct scaling.
diff --git a/docs/bugs.md b/docs/bugs.md
new file mode 100644
index 0000000..9f2f00c
--- /dev/null
+++ b/docs/bugs.md
@@ -0,0 +1,47 @@
+---
+layout: page
+---
+
+# Reporting Bugs in libsndfile
+
+Before even attempting to report a bug in libsndfile please make sure you have
+read the [Frequently Asked Questions](FAQ.md). If you are having a problem
+writing code using libsndfile make sure you read the
+[Application Programming Interface](api.md) documentation.
+
+That said, I am interested in finding and fixing all genuine bugs in libsndfile.
+Bugs I want to fix include any of the following problems (and probably others):
+
+- Compilation problems on new platforms.
+- Errors being detected during the `make check` process.
+- Segmentation faults occurring inside libsndfile.
+- libsndfile hanging when opening a file.
+- Supported sound file types being incorrectly read or written.
+- Omissions, errors or spelling mistakes in the documentation.
+
+When submitting a bug report you must include:
+
+- Your system (CPU and memory size should be enough).
+- The operating system you are using.
+- Whether you are using a package provided by your distribution or you compiled
+  it yourself.
+- If you compiled it yourself, the compiler you are using. (Also make sure to
+  run `make check`.)
+- A description of the problem.
+- Information generated by the sndfile-info program (see next paragraph).
+- If you are having problems with sndfile-play and ALSA on Linux, I will need
+  information about your kernel, ALSA version, compiler version, whether you
+  compiled the kernel/ALSA your self or installed from a package etc.
+
+If libsndfile compiles and installs correctly but has difficulty reading a
+particular file or type of file you should run the **sndfile-info** program
+(from the examples directory of the libsndfile distribution) on the file. See
+[here](sndfile_info.md) for an example of the use of the **sndfile-info**
+program.
+
+Please do not send me a sound file which fails to open under libsndfile unless
+I specifically ask you to. The above information should usually suffice for most
+problems.
+
+Once you have the above information you should submit a ticket on the libsndfile
+[GitHub issue tracker](https://github.com/libsndfile/libsndfile/issues).
diff --git a/docs/command.md b/docs/command.md
new file mode 100644
index 0000000..53e0192
--- /dev/null
+++ b/docs/command.md
@@ -0,0 +1,2016 @@
+---
+layout: page
+title: libsndfile : the sf_command function.
+---
+
+# sf_command
+
+```c
+int sf_command (SNDFILE *sndfile, int cmd, void *data, int datasize) ;
+```
+
+This function allows the caller to retrieve information from or change aspects
+of the library behaviour. Examples include retrieving a string containing the
+library version or changing the scaling applied to floating point sample data
+during read and write. Most of these operations are performed on a per-file
+basis.
+
+The cmd parameter is an integer identifier which is defined in *sndfile.h*. All
+of the valid command identifiers have names beginning with "SFC_". Data is
+passed to and returned from the library by use of a void pointer. The library
+will not read or write more than datasize bytes from the void pointer. For some
+calls no data is required in which case data should be NULL and datasize may be
+used for some other purpose.
+
+The available commands are as follows:
+
+| Name                                                              | Description                                             |
+|:------------------------------------------------------------------|:--------------------------------------------------------|
+| [SFC_GET_LIB_VERSION](#sfc_get_lib_version)                       | Retrieve the version of the library as a string.        |
+| [SFC_GET_LOG_INFO](#sfc_get_log_info)                             | Retrieve the internal per-file operation log.           |
+| [SFC_GET_CURRENT_SF_INFO](#sfc_get_current_sf_info)               | Retrieve `SF_INFO` struct of opened file.               |
+| [SFC_CALC_SIGNAL_MAX](#sfc_calc_signal_max)                       | Calculate the measured maximum signal value.            |
+| [SFC_CALC_NORM_SIGNAL_MAX](#sfc_calc_norm_signal_max)             | Calculate the measured normalised maximum signal value. |
+| [SFC_CALC_MAX_ALL_CHANNELS](#sfc_calc_max_all_channels)           | Calculate the peak value for each channel.              |
+| [SFC_CALC_NORM_MAX_ALL_CHANNELS](#sfc_calc_norm_max_all_channels) | Calculate the normalised peak for each channel.         |
+| [SFC_GET_SIGNAL_MAX](#sfc_get_signal_max)                         | Retrieve the peak value for the file.                   |
+| [SFC_GET_MAX_ALL_CHANNELS](#sfc_get_max_all_channels)             | Retrieve the peak value for each channel.               |
+| [SFC_SET_NORM_FLOAT](#sfc_set_norm_float)                         | Set float normalisation behaviour.                      |
+| [SFC_SET_NORM_DOUBLE](#sfc_set_norm_double)                       | Set double normalisation behaviour.                     |
+| [SFC_GET_NORM_FLOAT](#sfc_get_norm_float)                         | Get float normalisation behaviour.                      |
+| [SFC_GET_NORM_DOUBLE](#sfc_get_norm_double)                       | Get double normalisation behaviour.                     |
+| [SFC_SET_SCALE_FLOAT_INT_READ](#sfc_set_scale_float_int_read)     | Control scale factor on read.                           |
+| [SFC_SET_SCALE_INT_FLOAT_WRITE](#sfc_set_scale_int_float_write)   | Control scale factor on write.                          |
+| [SFC_GET_SIMPLE_FORMAT_COUNT](#sfc_get_simple_format_count)       | Get simple formats count.                               |
+| [SFC_GET_SIMPLE_FORMAT](#sfc_get_simple_format)                   | Get information about a simple format.                  |
+| [SFC_GET_FORMAT_INFO](#sfc_get_format_info)                       | Get information about a major or subtype format.        |
+| [SFC_GET_FORMAT_MAJOR_COUNT](#sfc_get_format_major_count)         | Get the number of major formats.                        |
+| [SFC_GET_FORMAT_MAJOR](#sfc_get_format_major)                     | Get information about a major format type.              |
+| [SFC_GET_FORMAT_SUBTYPE_COUNT](#sfc_get_format_subtype_count)     | Get the number of subformats.                           |
+| [SFC_GET_FORMAT_SUBTYPE](#sfc_get_format_subtype)                 | Get information about a subformat.                      |
+| [SFC_SET_ADD_PEAK_CHUNK](#sfc_set_add_peak_chunk)                 | Control PEAK chunk write to WAV and AIFF.               |
+| [SFC_UPDATE_HEADER_NOW](#sfc_update_header_now)                   | Update the file header in write mode on demand.         |
+| [SFC_SET_UPDATE_HEADER_AUTO](#sfc_set_update_header_auto)         | Update the file header on each write.                   |
+| [SFC_FILE_TRUNCATE](#sfc_file_truncate)                           | Truncate a file open for write or for read/write.       |
+| [SFC_SET_RAW_START_OFFSET](#sfc_set_raw_start_offset)             | Change the data start offset for raw files.             |
+| SFC_SET_DITHER_ON_WRITE                                           | Not implemented.                                        |
+| SFC_SET_DITHER_ON_READ                                            | Not implemented.                                        |
+| SFC_GET_DITHER_INFO_COUNT                                         | Not implemented.                                        |
+| SFC_GET_DITHER_INFO                                               | Not implemented.                                        |
+| [SFC_SET_CLIPPING](#sfc_set_clipping)                             | Control automatic clipping behaviour.                   |
+| [SFC_GET_CLIPPING](#sfc_get_clipping)                             | Get current clipping setting.                           |
+| [SFC_GET_EMBED_FILE_INFO](#sfc_get_embed_file_info)               | Get information about embedded audio files.             |
+| [SFC_WAVEX_GET_AMBISONIC](#sfc_wavex_get_ambisonic)               | Test a WAVEX file for Ambisonic format.                 |
+| [SFC_WAVEX_SET_AMBISONIC](#sfc_wavex_set_ambisonic)               | Modify a WAVEX header for Ambisonic format.             |
+| [SFC_SET_VBR_ENCODING_QUALITY](#sfc_set_vbr_encoding_quality)     | Set the Variable Bit Rate encoding quality.             |
+| [SFC_SET_OGG_PAGE_LATENCY_MS](#sfc_set_ogg_page_latency_ms)       | Set Ogg page latency for Opus file.                     |
+| [SFC_GET_OGG_STREAM_SERIALNO](#sfc_get_ogg_stream_serialno)       | Get Ogg stream serial number.                           |
+| [SFC_SET_COMPRESSION_LEVEL](#sfc_set_compression_level)           | Set the compression level.                              |
+| [SFC_RAW_DATA_NEEDS_ENDSWAP](#sfc_raw_data_needs_endswap)         | Determine if raw data needs endswapping.                |
+| [SFC_GET_BROADCAST_INFO](#sfc_get_broadcast_info)                 | Get the Broadcast Chunk info.                           |
+| [SFC_SET_BROADCAST_INFO](#sfc_set_broadcast_info)                 | Set the Broadcast Chunk info.                           |
+| [SFC_GET_CHANNEL_MAP_INFO](#sfc_get_channel_map_info)             | Get the channel map info.                               |
+| [SFC_SET_CHANNEL_MAP_INFO](#sfc_set_channel_map_info)             | Set the channel map info.                               |
+| [SFC_SET_CART_INFO](#sfc_set_cart_info)                           | Set the Cart Chunk info.                                |
+| [SFC_GET_CART_INFO](#sfc_get_cart_info)                           | Get the Cart Chunk info.                                |
+| [SFC_GET_LOOP_INFO](#sfc_get_loop_info)                           | Get loop info.                                          |
+| [SFC_GET_INSTRUMENT](#sfc_get_instrument)                         | Get instrument info.                                    |
+| [SFC_SET_INSTRUMENT](#sfc_set_instrument)                         | Set instrument info.                                    |
+| [SFC_GET_CUE_COUNT](#sfc_get_cue_count)                           | Get the cue marker count.                               |
+| [SFC_GET_CUE](#sfc_get_cue)                                       | Get cue marker info.                                    |
+| [SFC_SET_CUE](#sfc_set_cue)                                       | Set cue marker info.                                    |
+| [SFC_RF64_AUTO_DOWNGRADE](#sfc_rf64_auto_downgrade)               | Set auto downgrade from RF64 to WAV.                    |
+| [SFC_GET_ORIGINAL_SAMPLERATE](#sfc_get_original_samplerate)       | Get original samplerate metadata.                       |
+| [SFC_SET_ORIGINAL_SAMPLERATE](#sfc_set_original_samplerate)       | Set original samplerate metadata.                       |
+| [SFC_GET_BITRATE_MODE](#sfc_get_bitrate_mode)                     | Get bitrate mode.                                       |
+| [SFC_SET_BITRATE_MODE](#sfc_set_bitrate_mode)                     | Set bitrate mode.                                       |
+
+---
+
+## SFC_GET_LIB_VERSION
+
+Retrieve the version of the library as a string.
+
+### Parameters
+
+sndfile
+: Not used
+
+cmd
+: SFC_GET_LIB_VERSION
+
+data
+: A pointer to a char buffer
+
+datasize
+: The size of the buffer
+
+### Examples
+
+```c
+char  buffer [128] ;
+sf_command (NULL, SFC_GET_LIB_VERSION, buffer, sizeof (buffer)) ;
+```
+
+### Return value
+
+This call will return the length of the retrieved version string.
+
+### Notes
+
+The string returned in the buffer passed to this function will not overflow the
+buffer and will always be null terminated .
+
+## SFC_GET_LOG_INFO
+
+Retrieve the internal per-file operation log.
+
+This log buffer can often contain a good reason for why libsndfile failed to
+open a particular file.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_GET_LOG_INFO
+
+data
+: A pointer to a char buffer
+
+datasize
+: The size of the buffer
+
+Example:
+
+```c
+char  buffer [2048] ;
+sf_command (sndfile, SFC_GET_LOG_INFO, buffer, sizeof (buffer)) ;
+```
+
+### Return value
+
+This call will return the length of the retrieved version string.
+
+### Notes
+
+The string returned in the buffer passed to this function will not overflow the
+buffer and will always be null terminated.
+
+## SFC_GET_CURRENT_SF_INFO
+
+Retrieve `SF_INFO` struct of opened file.
+
+`SFC_GET_CURRENT_SF_INFO` command copies `SF_INFO` struct of `sndfile` object to
+provided buffer.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_GET_CURRENT_SF_INFO
+
+data
+: A pointer to a valid SF_INFO* pointer
+
+datasize
+: sizeof (SF_INFO)
+
+### Examples
+
+```c
+SF_INFO  sfinfo ;
+sf_command (sndfile, SFC_GET_CURRENT_SF_INFO, sfinfo, sizeof (SF_INFO)) ;
+```
+
+### Return value
+
+Zero on success, non-zero otherwise.
+
+## SFC_CALC_SIGNAL_MAX
+
+Retrieve the measured maximum signal value. This involves reading through the
+whole file which can be slow on large files.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_CALC_SIGNAL_MAX
+
+data
+: A pointer to a double
+
+datasize
+: sizeof (double)
+
+### Examples
+
+```c
+double   max_val ;
+sf_command (sndfile, SFC_CALC_SIGNAL_MAX, &max_val, sizeof (max_val)) ;
+```
+
+### Return value
+
+Zero on success, non-zero otherwise.
+
+## SFC_CALC_NORM_SIGNAL_MAX
+
+Retrieve the measured normalised maximum signal value. This involves reading
+through the whole file which can be slow on large files.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_CALC_NORM_SIGNAL_MAX
+
+data
+: A pointer to a double
+
+datasize
+: sizeof (double)
+
+### Examples
+
+```c
+double   max_val ;
+sf_command (sndfile, SFC_CALC_NORM_SIGNAL_MAX, &max_val, sizeof (max_val)) ;
+```
+
+### Return value
+
+Zero on success, non-zero otherwise.
+
+## SFC_CALC_MAX_ALL_CHANNELS
+
+Calculate the peak value (ie a single number) for each channel. This involves
+reading through the whole file which can be slow on large files.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_CALC_MAX_ALL_CHANNELS
+
+data
+: A pointer to a double
+
+datasize
+: sizeof (double) * number_of_channels
+
+### Examples
+
+```c
+double   peaks [number_of_channels] ;
+sf_command (sndfile, SFC_CALC_MAX_ALL_CHANNELS, peaks, sizeof (peaks)) ;
+```
+
+### Return value
+
+Zero if peaks have been calculated successfully and non-zero otherwise.
+
+## SFC_CALC_NORM_MAX_ALL_CHANNELS
+
+Calculate the normalised peak for each channel. This involves reading through
+the whole file which can be slow on large files.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_CALC_NORM_MAX_ALL_CHANNELS
+
+data
+: A pointer to a double
+
+datasize
+: sizeof (double) * number_of_channels
+
+### Examples
+
+```c
+double   peaks [number_of_channels] ;
+sf_command (sndfile, SFC_CALC_NORM_MAX_ALL_CHANNELS, peaks, sizeof (peaks)) ;
+```
+
+### Return value
+
+Zero if peaks have been calculated successfully and non-zero otherwise.
+
+## SFC_GET_SIGNAL_MAX
+
+Retrieve the peak value for the file as stored in the file header.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_GET_SIGNAL_MAX
+
+data
+: A pointer to a double
+
+datasize
+: sizeof (double)
+
+### Examples
+
+```c
+double   max_peak ;
+sf_command (sndfile, SFC_GET_SIGNAL_MAX, &max_peak, sizeof (max_peak)) ;
+```
+
+### Return value
+
+SF_TRUE if the file header contained the peak value. SF_FALSE
+otherwise.
+
+## SFC_GET_MAX_ALL_CHANNELS
+
+Retrieve the peak value for the file as stored in the file header.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_GET_SIGNAL_MAX
+
+data
+: A pointer to an array of doubles
+
+datasize
+: sizeof (double) * number_of_channels
+
+### Example
+
+```c
+double   peaks [number_of_channels] ;
+sf_command (sndfile, SFC_GET_MAX_ALL_CHANNELS, peaks, sizeof (peaks)) ;
+```
+
+### Return value
+
+SF_TRUE if the file header contains per channel peak values for the file,
+SF_FALSE otherwise.
+
+## SFC_SET_NORM_FLOAT
+
+This command only affects data read from or written to using the
+floating point
+functions:
+
+```c
+size_t    sf_read_float    (SNDFILE *sndfile, float *ptr, size_t items) ;
+size_t    sf_readf_float   (SNDFILE *sndfile, float *ptr, size_t frames) ;
+
+size_t    sf_write_float   (SNDFILE *sndfile, float *ptr, size_t items) ;
+size_t    sf_writef_float  (SNDFILE *sndfile, float *ptr, size_t frames) ;
+```
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_SET_NORM_FLOAT
+
+data
+: NULL
+
+datasize
+: SF_TRUE or SF_FALSE
+
+For read operations setting normalisation to SF_TRUE means that the data from
+all subsequent reads will be be normalised to the range [-1.0, 1.0].
+
+For write operations, setting normalisation to SF_TRUE means than all data
+supplied to the float write functions should be in the range [-1.0, 1.0] and
+will be scaled for the file format as necessary.
+
+For both cases, setting normalisation to SF_FALSE means that no scaling will
+take place.
+
+### Examples
+
+```c
+sf_command (sndfile, SFC_SET_NORM_FLOAT, NULL, SF_TRUE) ;
+
+sf_command (sndfile, SFC_SET_NORM_FLOAT, NULL, SF_FALSE) ;
+```
+
+### Return value
+
+Returns the previous float normalisation mode.
+
+## SFC_SET_NORM_DOUBLE
+
+This command only affects data read from or written to using the double
+precision floating point
+functions:
+
+```c
+size_t sf_read_double  (SNDFILE *sndfile, double *ptr, size_t items) ;
+size_t sf_readf_double (SNDFILE *sndfile, double *ptr, size_t frames) ;
+
+size_t sf_write_double  (SNDFILE *sndfile, double *ptr, size_t items) ;
+size_t sf_writef_double (SNDFILE *sndfile, double *ptr, size_t frames) ;
+```
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_SET_NORM_DOUBLE
+
+data
+: NULL
+
+datasize
+: SF_TRUE or SF_FALSE
+
+For read operations setting normalisation to SF_TRUE means that the data from
+all subsequent reads will be be normalised to the range [-1.0, 1.0].
+
+For write operations, setting normalisation to SF_TRUE means than all data
+supplied to the double write functions should be in the range [-1.0, 1.0] and
+will be scaled for the file format as necessary.
+
+For both cases, setting normalisation to SF_FALSE means that no scaling will
+take place.
+
+### Examples
+
+```c
+sf_command (sndfile, SFC_SET_NORM_DOUBLE, NULL, SF_TRUE) ;
+
+sf_command (sndfile, SFC_SET_NORM_DOUBLE, NULL, SF_FALSE) ;
+```
+
+### Return value
+
+Returns the previous double normalisation mode.
+
+## SFC_GET_NORM_FLOAT
+
+Retrieve the current float normalisation mode.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_GET_NORM_FLOAT
+
+data
+: NULL
+
+datasize
+: anything
+
+### Examples
+
+```c
+normalisation = sf_command (sndfile, SFC_GET_NORM_FLOAT, NULL, 0) ;
+```
+
+### Return value
+
+Returns TRUE if normalisation is on and FALSE otherwise.
+
+## SFC_GET_NORM_DOUBLE
+
+Retrieve the current float normalisation mode.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_GET_NORM_DOUBLE
+
+data
+: NULL
+
+datasize
+: anything
+
+Example:
+
+```c
+normalisation = sf_command (sndfile, SFC_GET_NORM_DOUBLE, NULL, 0) ;
+```
+
+### Return value
+
+Returns TRUE if normalisation is on and FALSE otherwise.
+
+## SFC_SET_SCALE_FLOAT_INT_READ
+
+Set/clear the scale factor when integer (short/int) data is read from a file
+containing floating point data.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd:
+SFC_SET_SCALE_FLOAT_INT_READ
+
+data
+: NULL
+
+datasize
+: TRUE or FALSE
+
+Example:
+
+```c
+sf_command (sndfile, SFC_SET_SCALE_FLOAT_INT_READ, NULL, SF_TRUE) ;
+```
+
+### Return value
+
+Returns the previous `SFC_SET_SCALE_FLOAT_INT_READ` setting for this file.
+
+## SFC_SET_SCALE_INT_FLOAT_WRITE
+
+Set/clear the scale factor when integer (short/int) data is written to a file as
+floating point data.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_SET_SCALE_INT_FLOAT_WRITE
+
+data
+: NULL
+
+datasize
+: TRUE or FALSE
+
+### Examples
+
+```c
+sf_command (sndfile, SFC_SET_SCALE_INT_FLOAT_WRITE, NULL, SF_TRUE) ;
+```
+
+### Return value
+
+Returns the previous `SFC_SET_SCALE_INT_FLOAT_WRITE` setting for this file.
+
+## SFC_GET_SIMPLE_FORMAT_COUNT
+
+Retrieve the number of simple formats supported by libsndfile.
+
+### Parameters
+
+sndfile
+: Not used.
+
+cmd
+: SFC_GET_SIMPLE_FORMAT_COUNT
+
+data
+: a pointer to an int
+
+datasize
+: sizeof (int)
+
+### Examples
+
+```c
+int  count ;
+sf_command (sndfile, SFC_GET_SIMPLE_FORMAT_COUNT, &count, sizeof (int)) ;
+```
+
+### Return value
+
+`0`.
+
+## SFC_GET_SIMPLE_FORMAT
+
+Retrieve information about a simple format.
+
+### Parameters
+
+sndfile
+: Not used.
+
+cmd
+: SFC_GET_SIMPLE_FORMAT
+
+data
+: a pointer to an  SF_FORMAT_INFO struct
+
+datasize
+: sizeof (SF_FORMAT_INFO)
+
+The SF_FORMAT_INFO struct is defined in *sndfile.h* as:
+
+```c
+typedef struct
+{   int         format ;
+    const char  *name ;
+    const char  *extension ;
+} SF_FORMAT_INFO ;
+```
+
+When `sf_command()` is called with `SF_GET_SIMPLE_FORMAT`, the value of the
+format field should be the format number (ie 0 \<= format \<= count value
+obtained using `SF_GET_SIMPLE_FORMAT_COUNT).
+
+### Examples
+
+```c
+SF_FORMAT_INFO  format_info ;
+int             k, count ;
+
+sf_command (sndfile, SFC_GET_SIMPLE_FORMAT_COUNT, &count, sizeof (int)) ;
+
+for (k = 0 ; k < count ; k++)
+{   format_info.format = k ;
+    sf_command (sndfile, SFC_GET_SIMPLE_FORMAT, &format_info, sizeof (format_info)) ;
+    printf ("%08x  %s %s\n", format_info.format, format_info.name, format_info.extension) ;
+    } ;
+```
+
+### Return value
+
+0 on success and non-zero otherwise.
+
+The value of the format field of the `SF_FORMAT_INFO` struct will be a value
+which can be placed in the format field of an `SF_INFO` struct when a file is to
+be opened for write. The name field will contain a char\* pointer to the name of
+the string, eg. "WAV (Microsoft 16 bit PCM)". The extension field will contain
+the most commonly used file extension for that file type.
+
+## SFC_GET_FORMAT_INFO
+
+Retrieve information about a major or subtype format.
+
+### Parameters
+
+sndfile
+: Not used.
+
+cmd
+: SFC_GET_FORMAT_INFO
+
+data
+: a pointer to an SF_FORMAT_INFO struct
+
+datasize
+: sizeof (SF_FORMAT_INFO)
+
+The `SF_FORMAT_INFO` struct is defined in \<sndfile.h\> as:
+
+```c
+typedef struct
+{   int         format ;
+    const char  *name ;
+    const char  *extension ;
+} SF_FORMAT_INFO ;
+```
+
+When `sf_command()` is called with `SF_GET_FORMAT_INFO`, the format field is
+examined and if (format & `SF_FORMAT_TYPEMASK`) is a valid format then the
+struct is filled in with information about the given major type. If (format &
+`SF_FORMAT_TYPEMASK`) is FALSE and (format & `SF_FORMAT_SUBMASK`) is a valid
+subtype format then the struct is filled in with information about the given
+subtype.
+
+### Examples
+
+```c
+SF_FORMAT_INFO  format_info ;
+
+format_info.format = SF_FORMAT_WAV ;
+sf_command (sndfile, SFC_GET_FORMAT_INFO, &format_info, sizeof (format_info)) ;
+printf ("%08x  %s %s\n", format_info.format, format_info.name, format_info.extension) ;
+
+format_info.format = SF_FORMAT_ULAW ;
+sf_command (sndfile, SFC_GET_FORMAT_INFO, &format_info, sizeof (format_info)) ;
+printf ("%08x  %s\n", format_info.format, format_info.name) ;
+```
+
+### Return value
+
+0 on success and non-zero otherwise.
+
+## SFC_GET_FORMAT_MAJOR_COUNT
+
+Retrieve the number of major formats.
+
+### Parameters
+
+sndfile
+: Not used.
+
+cmd
+: SFC_GET_FORMAT_MAJOR_COUNT
+
+data
+: a pointer to an int
+
+datasize
+: sizeof (int)
+
+### Examples
+
+```c
+int  count ;
+sf_command (sndfile, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof (int)) ;
+```
+
+### Return value
+
+0.
+
+## SFC_GET_FORMAT_MAJOR
+
+Retrieve information about a major format type.
+
+### Parameters
+
+sndfile
+: Not used.
+
+cmd
+: SFC_GET_FORMAT_MAJOR
+
+data
+: a pointer to an  SF_FORMAT_INFO struct
+
+datasize
+: sizeof (SF_FORMAT_INFO)
+
+### Examples
+
+```c
+SF_FORMAT_INFO  format_info ;
+int             k, count ;
+
+sf_command (sndfile, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof (int)) ;
+
+for (k = 0 ; k < count ; k++)
+{   format_info.format = k ;
+    sf_command (sndfile, SFC_GET_FORMAT_MAJOR, &format_info, sizeof (format_info)) ;
+    printf ("%08x  %s %s\n", format_info.format, format_info.name, format_info.extension) ;
+    } ;
+```
+
+For a more comprehensive example, see the program `list_formats.c` in the
+`examples/` directory of the libsndfile source code distribution.
+
+### Return value
+
+0 on success and non-zero otherwise.
+
+The value of the format field will be one of the major format identifiers such
+as `SF_FORMAT_WAV` or `SF_FORMAT`_AIFF. The name field will contain a char\*
+pointer to the name of the string, eg. "WAV (Microsoft)". The extension field
+will contain the most commonly used file extension for that file type.
+
+## SFC_GET_FORMAT_SUBTYPE_COUNT
+
+Retrieve the number of subformats.
+
+### Parameters
+
+sndfile
+: Not used.
+
+cmd
+: SFC_GET_FORMAT_SUBTYPE_COUNT
+
+data
+: a pointer to an int
+
+datasize
+: sizeof (int)
+
+### Examples
+
+```c
+int   count ;
+sf_command (sndfile, SFC_GET_FORMAT_SUBTYPE_COUNT, &count, sizeof (int)) ;
+```
+
+### Return value
+
+Returns zero.
+
+## SFC_GET_FORMAT_SUBTYPE
+
+Enumerate the subtypes (this function does not translate a subtype into a string
+describing that subtype). A typical use case might be retrieving a string
+description of all subtypes so that a dialog box can be filled in.
+
+### Parameters
+
+sndfile
+: Not used.
+
+cmd
+: SFC_GET_FORMAT_SUBTYPE
+
+data
+: a pointer to an SF_FORMAT_INFO struct
+
+datasize
+: sizeof (SF_FORMAT_INFO)
+
+### Examples
+
+Example 1: Retrieve all sybtypes supported by the WAV format.
+
+```c
+SF_FORMAT_INFO  format_info ;
+int             k, count ;
+
+sf_command (sndfile, SFC_GET_FORMAT_SUBTYPE_COUNT, &count, sizeof (int)) ;
+
+for (k = 0 ; k < count ; k++)
+{   format_info.format = k ;
+    sf_command (sndfile, SFC_GET_FORMAT_SUBTYPE, &format_info, sizeof (format_info)) ;
+    if (! sf_format_check (format_info.format | SF_FORMAT_WAV))
+        continue ;
+    printf ("%08x  %s\n", format_info.format, format_info.name) ;
+    } ;
+```
+
+Example 2: Print a string describing the `SF_FORMAT_PCM_16` subtype.
+
+```c
+SF_FORMAT_INFO  format_info ;
+int             k, count ;
+
+sf_command (sndfile, SFC_GET_FORMAT_SUBTYPE_COUNT, &count, sizeof (int)) ;
+
+for (k = 0 ; k < count ; k++)
+{   format_info.format = k ;
+    sf_command (sndfile, SFC_GET_FORMAT_SUBTYPE, &format_info, sizeof (format_info)) ;
+    if (format_info.format == SF_FORMAT_PCM_16)
+    {   printf ("%08x  %s\n", format_info.format, format_info.name) ;
+        break ;
+        } ;
+    } ;
+```
+
+For a more comprehensive example, see the program `list_formats.c` in the
+`examples/` directory of the libsndfile source code distribution.
+
+### Return value
+
+0 on success and non-zero otherwise.
+
+The value of the format field will be one of the major format identifiers such
+as `SF_FORMAT_WAV` or `SF_FORMAT_AIFF`. The name field will contain a char\*
+pointer to the name of the string; for instance "WAV (Microsoft)" or "AIFF
+(Apple/SGI)". The extension field will be a NULL pointer.
+
+## SFC_SET_ADD_PEAK_CHUNK
+
+By default, WAV and AIFF files which contain floating point data (subtype
+`SF_FORMAT_FLOAT` or `SF_FORMAT_DOUBLE`) have a PEAK chunk. By using this
+command, the addition of a PEAK chunk can be turned on or off.
+
+**Note**: This call must be made before any data is written to the file.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_SET_ADD_PEAK_CHUNK
+
+data
+: Not used (should be NULL)
+
+datasize
+: TRUE or FALSE.
+
+### Examples
+
+```c
+/* Turn on the PEAK chunk. */
+sf_command (sndfile, SFC_SET_ADD_PEAK_CHUNK, NULL, SF_TRUE) ;
+
+/* Turn off the PEAK chunk. */
+sf_command (sndfile, SFC_SET_ADD_PEAK_CHUNK, NULL, SF_FALSE) ;
+```
+
+### Return value
+
+Returns SF_TRUE if the peak chunk will be written after this call. Returns
+SF_FALSE if the peak chunk will not be written after this call.
+
+## SFC_UPDATE_HEADER_NOW
+
+The header of an audio file is normally written by libsndfile when the file is
+closed using [**sf_close()**](api.md#file-close-function).
+
+There are however situations where large files are being generated and it would
+be nice to have valid data in the header before the file is complete. Using this
+command will update the file header to reflect the amount of data written to the
+file so far. Other programs opening the file for read (before any more data is
+written) will then read a valid sound file header.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_UPDATE_HEADER_NOW
+
+data
+: Not used (should be NULL)
+
+datasize
+: Not used.
+
+### Examples
+
+```c
+/* Update the header now. */
+sf_command (sndfile, SFC_UPDATE_HEADER_NOW, NULL, 0) ;
+```
+
+### Return value
+
+Returns zero.
+
+## SFC_SET_UPDATE_HEADER_AUTO
+
+Similar to `SFC_UPDATE_HEADER_NOW` but updates the header at the end of every
+call to the [sf_write\*](api.md#write) functions.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_SET_UPDATE_HEADER_AUTO
+
+data
+: Not used (should be NULL)
+
+datasize
+: `SF_TRUE` or `SF_FALSE`
+
+### Examples
+
+```c
+/* Turn on auto header update. */
+sf_command (sndfile, SFC_SET_UPDATE_HEADER_AUTO, NULL, SF_TRUE) ;
+
+/* Turn off auto header update. */
+sf_command (sndfile, SFC_SET_UPDATE_HEADER_AUTO, NULL, SF_FALSE) ;
+```
+
+### Return value
+
+TRUE if auto update header is now on; FALSE otherwise.
+
+## SFC_FILE_TRUNCATE
+
+Truncate a file that was opened for write or read/write.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_FILE_TRUNCATE
+
+data
+: A pointer to an sf_count_t.
+
+datasize
+: sizeof (sf_count_t)
+
+Truncate the file to the number of frames specified by the sf_count_t pointed to
+by data. After this command, both the read and the write pointer will be at the
+new end of the file. This command will fail (returning non-zero) if the
+requested truncate position is beyond the end of the file.
+
+### Examples
+
+```c
+/* Truncate the file to a length of 20 frames. */
+sf_count_t  frames = 20 ;
+sf_command (sndfile, SFC_FILE_TRUNCATE, &frames, sizeof (frames)) ;
+```
+
+### Return value
+
+Zero on success, non-zero otherwise.
+
+## SFC_SET_RAW_START_OFFSET
+
+Change the data start offset for files opened up as `SF_FORMAT_RAW`.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_SET_RAW_START_OFFSET
+
+data
+: A pointer to an sf_count_t.
+
+datasize
+: sizeof (sf_count_t)
+
+For a file opened as format `SF_FORMAT_RAW`, set the data offset to the value
+given by `data`.
+
+### Examples
+
+```c
+/* Reset the data offset to 5 bytes from the start of the file. */
+sf_count_t  offset = 5 ;
+sf_command (sndfile, SFC_SET_RAW_START_OFFSET, &offset, sizeof (offset)) ;
+```
+
+### Return value
+
+Zero on success, non-zero otherwise.
+
+## SFC_SET_CLIPPING
+
+Turn on/off automatic clipping when doing floating point to integer conversion.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_SET_CLIPPING
+
+data
+: NULL
+
+datasize
+: SF_TRUE or SF_FALSE.
+
+Turn on (datasize == SF_TRUE) or off (datasize == SF_FALSE) clipping.
+
+### Examples
+
+```c
+sf_command (sndfile, SFC_SET_CLIPPING, NULL, SF_TRUE) ;
+```
+
+### Return value
+
+Clipping mode (SF_TRUE or SF_FALSE).
+
+## SFC_GET_CLIPPING
+
+Turn on/off automatic clipping when doing floating point to integer conversion.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_GET_CLIPPING
+
+data
+: NULL
+
+datasize
+: 0
+
+Retrieve the current cliiping setting.
+
+### Examples
+
+```c
+sf_command (sndfile, SFC_GET_CLIPPING, NULL, 0) ;
+```
+
+### Return value
+
+Clipping mode (SF_TRUE or SF_FALSE).
+
+## SFC_GET_EMBED_FILE_INFO
+
+Get the file offset and file length of a file enbedded within another larger
+file.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_GET_EMBED_FILE_INFO
+
+data
+: a pointer to an  SF_EMBED_FILE_INFO struct
+
+datasize
+: sizeof (SF_EMBED_FILE_INFO)
+
+The `SF_EMBED_FILE_INFO` struct is defined in *sndfile.h* as:
+
+```c
+typedef struct
+{   sf_count_t  offset ;
+    sf_count_t  length ;
+} SF_EMBED_FILE_INFO ;
+```
+
+### Return value
+
+0 on success and non-zero otherwise.
+
+The value of the offset field of the `SF_EMBED_FILE_INFO` struct will be the
+offsets in bytes from the start of the outer file to the start of the audio
+file. The value of the offset field of the `SF_EMBED_FILE_INFO` struct will be
+the length in bytes of the embedded file.
+
+## SFC_WAVEX_GET_AMBISONIC
+
+Test if the current file has the GUID of a WAVEX file for any of the Ambisonic
+formats.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_WAVEX_GET_AMBISONIC
+
+data
+: NULL
+
+datasize
+: 0
+
+The Ambisonic WAVEX formats are defined here:
+<http://dream.cs.bath.ac.uk/researchdev/wave-ex/bformat.html>.
+
+### Return value
+
+`SF_AMBISONIC_NONE(0x40)` or `SF_AMBISONIC_B_FORMAT(0x41)` or zero if the file
+format does not support ambisonic formats.
+
+## SFC_WAVEX_SET_AMBISONIC
+
+Set the GUID of a new WAVEX file to indicate an Ambisonics format.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_WAVEX_SET_AMBISONIC
+
+data
+: NULL
+
+datasize
+: SF_AMBISONIC_NONE or SF_AMBISONIC_B_FORMAT
+
+Turn on (`SF_AMBISONIC_B_FORMAT(0x41)`) or off (`SF_AMBISONIC_NONE(0x40)`)
+encoding. This command is currently only supported for files with
+`SF_FORMAT_WAVEX` format.
+
+The Ambisonic WAVEX formats are defined here:
+
+<http://dream.cs.bath.ac.uk/researchdev/wave-ex/bformat.html>.
+
+### Return value
+
+Return the ambisonic value that has just been set or zero if the
+file format does not support ambisonic encoding.
+
+## SFC_SET_VBR_ENCODING_QUALITY
+
+Set the Variable Bit Rate encoding quality. The encoding quality value
+should be between 0.0 (lowest quality) and 1.0 (highest quality).
+Currently this command is only implemented for FLAC and Ogg/Vorbis files.
+It has no effect on un-compressed file formats.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_SET_VBR_ENCODING_QUALITY
+
+data
+: A pointer to a double value
+
+datasize
+: sizeof (double)
+
+The command must be sent before any audio data is written to the file.
+
+### Return value
+
+SF_TRUE if VBR encoding quality was set. SF_FALSE otherwise.
+
+## SFC_SET_OGG_PAGE_LATENCY_MS
+
+Set page latency for Ogg Opus file in milliseconds. The value should be between
+50.0 and 1600.0. This command is only implemented for Ogg Opus files.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_SET_OGG_PAGE_LATENCY_MS
+
+data
+: A pointer to a double value
+
+datasize
+: sizeof (double)
+
+### Return value
+
+0 on success and non-zero otherwise.
+
+## SFC_GET_OGG_STREAM_SERIALNO
+
+Get the Ogg stream serial number for files with the Ogg major format. Ogg
+stream serail numbers are a randomly chosen 32-bit value, used for
+differentiating logical Ogg streams.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_SET_OGG_STREAM_SERIALNO
+
+data
+: A pointer to a 32-bit int value
+
+datasize
+: sizeof (int32_t) = 4
+
+### Return value
+
+0 on success and non-zero otherwise.
+
+## SFC_SET_COMPRESSION_LEVEL
+
+Set the compression level. The compression level should be between 0.0 (minimum
+compression level) and 1.0 (highest compression level). Currently this command is
+only implemented for FLAC and Ogg/Vorbis files. It has no effect on
+uncompressed file formats.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_SET_COMPRESSION_LEVEL
+
+data
+: A pointer to a double value
+
+datasize
+: sizeof (double)
+
+The command must be sent before any audio data is written to the file.
+
+### Return value
+
+SF_TRUE if compression level was set. SF_FALSE otherwise.
+
+## SFC_RAW_DATA_NEEDS_ENDSWAP
+
+Determine if raw data read using [sf_read_raw()](api.md#raw) needs to be end
+swapped on the host CPU.
+
+For instance, will return SF_TRUE on when reading WAV containing
+`SF_FORMAT_PCM_16` data on a big endian machine and `SF_FALSE` on a
+little endian machine.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_RAW_DATA_NEEDS_ENDSWAP
+
+data
+: NULL
+
+datasize
+: 0
+
+### Return value
+
+`SF_TRUE` or `SF_FALSE`.
+
+## SFC_GET_BROADCAST_INFO
+
+Retrieve the Broadcast Extension Chunk from WAV (and related) files.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_GET_BROADCAST_INFO
+
+data
+: a pointer to an SF_BROADCAST_INFO struct
+
+datasize
+: sizeof (SF_BROADCAST_INFO)
+
+The SF_BROADCAST_INFO struct is defined in *sndfile.h* as:
+
+```c
+typedef struct
+{   char            description [256] ;
+    char            originator [32] ;
+    char            originator_reference [32] ;
+    char            origination_date [10] ;
+    char            origination_time [8] ;
+    unsigned int    time_reference_low ;
+    unsigned int    time_reference_high ;
+    short           version ;
+    char            umid [64] ;
+    char            reserved [190] ;
+    unsigned int    coding_history_size ;
+    char            coding_history [256] ;
+} SF_BROADCAST_INFO ;
+```
+
+### Return value
+
+`SF_TRUE` if the file contained a Broadcast Extension chunk or `SF_FALSE`
+otherwise.
+
+## SFC_SET_BROADCAST_INFO
+
+Set the Broadcast Extension Chunk for WAV (and related) files.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_SET_BROADCAST_INFO
+
+data
+: a pointer to an SF_BROADCAST_INFO struct
+
+datasize
+: sizeof (SF_BROADCAST_INFO)
+
+### Return value
+
+`SF_TRUE` if setting the Broadcast Extension chunk was successful and `SF_FALSE`
+otherwise.
+
+## SFC_GET_CHANNEL_MAP_INFO
+
+Retrieve the channel map contained in an AIFF or CAF Channel Layout chunk.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_GET_CHANNEL_MAP_INFO
+
+data
+: a pointer to an array of int, the same size as the number of channels in the
+file
+
+datasize
+: number of channels * sizeof (int)
+
+Channel map positions are defined in an enum in *sndfile.h*:
+
+| Name                                 | Value | Description                                                       |
+|:-------------------------------------|:------|:------------------------------------------------------------------|
+| SF_CHANNEL_MAP_INVALID               | 0     |                                                                   |
+| SF_CHANNEL_MAP_MONO                  | 1     |                                                                   |
+| SF_CHANNEL_MAP_LEFT                  | 2     | Apple calls this 'Left'                                           |
+| SF_CHANNEL_MAP_RIGHT                 | 3     | Apple calls this 'Right'                                          |
+| SF_CHANNEL_MAP_CENTER                | 4     | Apple calls this 'Center'                                         |
+| SF_CHANNEL_MAP_FRONT_LEFT            | 5     |                                                                   |
+| SF_CHANNEL_MAP_FRONT_RIGHT           | 6     |                                                                   |
+| SF_CHANNEL_MAP_FRONT_CENTER          | 7     |                                                                   |
+| SF_CHANNEL_MAP_REAR_CENTER           | 8     | Apple calls this 'Center Surround', Msft calls this 'Back Center' |
+| SF_CHANNEL_MAP_REAR_LEFT             | 9     | Apple calls this 'Left Surround', Msft calls this 'Back Left'     |
+| SF_CHANNEL_MAP_REAR_RIGHT            | 10    | Apple calls this 'Right Surround', Msft calls this 'Back Right'   |
+| SF_CHANNEL_MAP_LFE                   | 11    | Apple calls this 'LFEScreen', Msft calls this 'Low Frequency'     |
+| SF_CHANNEL_MAP_FRONT_LEFT_OF_CENTER  | 12    | Apple calls this 'Left Center'                                    |
+| SF_CHANNEL_MAP_FRONT_RIGHT_OF_CENTER | 13    | Apple calls this 'Right Center'                                   |
+| SF_CHANNEL_MAP_SIDE_LEFT             | 14    | Apple calls this 'Left Surround Direct'                           |
+| SF_CHANNEL_MAP_SIDE_RIGHT            | 15    | Apple calls this 'Right Surround Direct'                          |
+| SF_CHANNEL_MAP_TOP_CENTER            | 16    | Apple calls this 'Top Center Surround'                            |
+| SF_CHANNEL_MAP_TOP_FRONT_LEFT        | 17    | Apple calls this 'Vertical Height Left'                           |
+| SF_CHANNEL_MAP_TOP_FRONT_RIGHT       | 18    | Apple calls this 'Vertical Height Right'                          |
+| SF_CHANNEL_MAP_TOP_FRONT_CENTER      | 19    | Apple calls this 'Vertical Height Center'                         |
+| SF_CHANNEL_MAP_TOP_REAR_LEFT         | 20    | Apple and MS call this 'Top Back Left'                            |
+| SF_CHANNEL_MAP_TOP_REAR_RIGHT        | 21    | Apple and MS call this 'Top Back Right'                           |
+| SF_CHANNEL_MAP_TOP_REAR_CENTER       | 22    | Apple and MS call this 'Top Back Center'                          |
+| SF_CHANNEL_MAP_AMBISONIC_B_W         | 23    |                                                                   |
+| SF_CHANNEL_MAP_AMBISONIC_B_X         | 24    |                                                                   | 
+| SF_CHANNEL_MAP_AMBISONIC_B_Y         | 25    |                                                                   |
+| SF_CHANNEL_MAP_AMBISONIC_B_Z         | 26    |                                                                   |
+| SF_CHANNEL_MAP_MAX                   | 27    |                                                                   |
+
+### Return value
+
+`SF_TRUE` if the file contained a Channel Layout chunk or `SF_FALSE` otherwise.
+
+## SFC_SET_CHANNEL_MAP_INFO
+
+Set the channel map contained in an AIFF or CAF Channel Layout chunk.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_SET_CHANNEL_MAP_INFO
+
+data
+: a pointer to an array of int, the same size as the number of channels in the
+file
+
+datasize
+: number of channels * sizeof (int)
+
+### Return value
+
+`SF_TRUE` if setting the Channel Layout chunk was successful and `SF_FALSE`
+otherwise.
+
+## SFC_GET_CART_INFO
+
+Retrieve the Cart Chunk from WAV (and related) files. Based on AES46 standard
+for CartChunk (see [CartChunk.org](http://www.cartchunk.org/) for more
+information.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_GET_CART_INFO
+
+data
+: a pointer to an SF_CART_INFO struct
+
+datasize
+: sizeof (SF_CART_INFO)
+
+The SF_CART_INFO struct is defined in *sndfile.h* as:
+
+```c
+#define SF_CART_INFO_VAR(p_tag_text_size) \
+    struct
+    {       char            version [4] ;
+            char            title [64] ;
+            char            artist [64] ;
+            char            cut_id [64] ;
+            char            client_id [64] ;
+            char            category [64] ;
+            char            classification [64] ;
+            char            out_cue [64] ;
+            char            start_date [10] ;
+            char            start_time [8] ;
+            char            end_date [10] ;
+            char            end_time [8] ;
+            char            producer_app_id [64] ;
+            char            producer_app_version [64] ;
+            char            user_def [64] ;
+            long    level_reference ;
+            SF_CART_TIMER   post_timers [8] ;
+            char            reserved [276] ;
+            char            url [1024] ;
+            unsigned int    tag_text_size ;
+            char            tag_text[p_tag_text_size] ;
+    }
+```
+
+### Return value
+
+`SF_TRUE` if the file contained a Cart chunk or `SF_FALSE` otherwise.
+
+## SFC_SET_CART_INFO
+
+Set the Cart Chunk for WAV (and related) files.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_SET_CART_INFO
+
+data
+: a pointer to an SF_CART_INFO struct
+
+datasize
+: sizeof (SF_CART_INFO)
+
+### Return value
+
+SF_TRUE if setting the Cart chunk was successful and SF_FALSE otherwise.
+
+## SFC_GET_LOOP_INFO
+
+Retrieve loop information for file including time signature, length in beats and
+original MIDI base note
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_GET_LOOP_INFO
+
+data
+: a pointer to an SF_LOOP_INFO struct
+
+datasize
+: sizeof (SF_LOOP_INFO)
+
+The SF_LOOP_INFO struct is defined in *sndfile.h* as:
+
+```c
+typedef struct
+{   short    time_sig_num ;   /* any positive integer    > 0  */
+    short    time_sig_den ;   /* any positive power of 2 > 0  */
+    int        loop_mode ;    /* see SF_LOOP enum             */
+
+    int        num_beats ;    /* this is NOT the amount of quarter notes !!!*/
+                              /* a full bar of 4/4 is 4 beats */
+                              /* a full bar of 7/8 is 7 beats */
+
+    float    bpm ;            /* suggestion, as it can be calculated using other fields:*/
+                              /* file's length, file's sampleRate and our time_sig_den*/
+                              /* -> bpms are always the amount of _quarter notes_ per minute */
+
+    int    root_key ;         /* MIDI note, or -1 for None */
+    int future [6] ;
+} SF_LOOP_INFO ;
+```
+
+### Examples
+
+```c
+SF_LOOP_INFO loop;
+sf_command (sndfile, SFC_GET_LOOP_INFO, &loop, sizeof (loop)) ;
+```
+
+### Return value
+
+`SF_TRUE` if the file header contains loop information for the file, `SF_FALSE`
+otherwise.
+
+## SFC_GET_INSTRUMENT
+
+Retrieve instrument information from file including MIDI base note, keyboard
+mapping and looping information (start/stop and mode).
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_GET_INSTRUMENT
+
+data
+: a pointer to an SF_INSTRUMENT struct
+
+datasize
+: sizeof (SF_INSTRUMENT)
+
+The `SF_INSTRUMENT` struct is defined in *sndfile.h* as:
+
+```c
+typedef struct
+{   int gain ;
+    char basenote, detune ;
+    char velocity_lo, velocity_hi ;
+    char key_lo, key_hi ;
+    int loop_count ;
+
+    struct
+    {   int mode ;
+        unsigned int start ;
+        unsigned int end ;
+        unsigned int count ;
+    } loops [16] ; /* make variable in a sensible way */
+} SF_INSTRUMENT ;
+```
+
+`mode` values are defined as:
+
+| Name                | Value | Description |
+|:--------------------|:------|:------------|
+| SF_LOOP_NONE        | 800   |             |
+| SF_LOOP_FORWARD     | 801   |             |
+| SF_LOOP_BACKWARD    | 802   |             |
+| SF_LOOP_ALTERNATING | 803   |             |
+
+### Examples
+
+```c
+SF_INSTRUMENT inst ;
+sf_command (sndfile, SFC_GET_INSTRUMENT, &inst, sizeof (inst)) ;
+```
+
+### Return value
+
+`SF_TRUE` if the file header contains instrument information for the file,
+`SF_FALSE` otherwise.
+
+## SFC_SET_INSTRUMENT
+
+Set the instrument information for the file.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_SET_INSTRUMENT
+
+data
+: a pointer to an SF_INSTRUMENT struct
+
+datasize
+: sizeof (SF_INSTRUMENT)
+
+### Examples
+
+```c
+SF_INSTRUMENT inst ;
+sf_command (sndfile, SFC_SET_INSTRUMENT, &inst, sizeof (inst)) ;
+```
+
+### Return value
+
+`SF_TRUE` if the file header contains instrument information for the file,
+`SF_FALSE` otherwise.
+
+## SFC_GET_CUE_COUNT
+
+Retrieve the number of cue markers available for retrieval using the
+[SFC_GET_CUE](#sfc_get_cue) command.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_GET_CUE_COUNT
+
+data
+: a pointer to a uint32_t
+
+datasize
+: sizeof (uint32_t)
+
+### Examples
+
+```c
+uint32_t cue_count ;
+sf_command (sndfile, SFC_GET_CUE_COUNT, &cue_count, sizeof (cue_count)) ;
+```
+
+### Return value
+
+`SF_TRUE` if the file header contains cue marker information for the file,
+`SF_FALSE` otherwise.
+
+## SFC_GET_CUE
+
+Retrieve cue marker information from file.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_GET_CUE
+
+data
+: a pointer to an SF_CUES struct
+
+datasize
+: sizeof (SF_CUES)
+
+The SF_CUES struct is defined in *sndfile.h* as:
+
+```c
+typedef struct
+{   int cue_count ;
+
+    struct
+    {   int32_t   indx ;
+        uint32_t  position ;
+        int32_t   fcc_chunk ;
+        int32_t   chunk_start ;
+        int32_t   block_start ;
+        uint32_t  sample_offset ;
+        char name [256] ;
+    } cue_points [100] ;
+} SF_CUES ;
+```
+
+There is also an SF_CUES_VAR \#define that allows reading/writing more than 100
+cue markers.
+
+### Examples
+
+```c
+SF_CUES cues ;
+sf_command (sndfile, SFC_GET_CUE, &cues, sizeof (cues)) ;
+```
+
+### Return value
+
+`SF_TRUE` if the file header contains cue marker information for the file,
+`SF_FALSE` otherwise.
+
+## SFC_SET_CUE
+
+Set the cue marker information for the file.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_SET_CUE
+
+data
+: a pointer to an SF_CUES struct
+
+datasize
+: sizeof (SF_CUES)
+
+### Examples
+
+```c
+SF_CUES cues ;
+sf_command (sndfile, SFC_SET_CUE, &cues, sizeof (cues)) ;
+```
+
+### Return value
+
+`SF_TRUE` if the file header contains cue marker information for the file,
+`SF_FALSE` otherwise.
+
+## SFC_RF64_AUTO_DOWNGRADE
+
+Enable auto downgrade from RF64 to WAV.
+
+The EBU recommendation is that when writing RF64 files and the resulting file is
+less than 4Gig in size, it should be downgraded to a WAV file (WAV files have a
+maximum size of 4Gig). libsndfile doesn't follow the EBU recommendations
+exactly, mainly because the test suite needs to be able test reading/writing
+RF64 files without having to generate files larger than 4 gigabytes.
+
+Note: This command should be issued before the first bit of audio data has been
+written to the file. Calling this command after audio data has been written will
+return the current value of this setting, but will not allow it to be changed.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_RF64_AUTO_DOWNGRADE
+
+data
+: NULL
+
+datasize
+: SF_TRUE or SF_FALSE
+
+### Examples
+
+```c
+/* Enable auto downgrade on file close. */
+sf_command (sndfile, SFC_RF64_AUTO_DOWNGRADE, NULL, SF_TRUE) ;
+```
+
+### Return value
+
+Returns `SF_TRUE` if `SFC_RF64_AUTO_DOWNGRADE` is set and `SF_FALSE` otherwise.
+
+## SFC_GET_ORIGINAL_SAMPLERATE
+
+Get original samplerate metadata.
+
+The Opus audio codec stores audio data independent of samplerate, but only
+supports encoding or decoding at 8000Hz, 12000Hz, 16000Hz, 24000Hz or 48000Hz.
+Opus includes a header field to record the original source input samplerate, and
+a samplerate converter may be used if needed.
+
+This command gets the original samplerate header field. It does not enable any
+(non-existent) samplerate conversion, nor change the current decoder samplerate.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_GET_ORIGINAL_SAMPLERATE
+
+data
+: pointer to an integer
+
+datasize
+: sizeof (int)
+
+### Examples
+
+```c
+/* Get the original sample rate */
+int original_samplerate ;
+sf_command (sndfile, SFC_GET_ORIGINAL_SAMPLERATE, &original_samplerate, sizeof (original_samplerate)) ;
+```
+
+### Return value
+
+Returns `SF_TRUE` on success, `SF_FALSE` otherwise.
+
+The passed integer is set to the value of the original samplerate.
+
+## SFC_SET_ORIGINAL_SAMPLERATE
+
+Set original samplerate metadata.
+
+The Opus audio codec stores audio data independent of samplerate, but only
+supports encoding or decoding at 8000Hz, 12000Hz, 16000Hz, 24000Hz or 48000Hz.
+Opus includes a header field to record the original source input samplerate, and
+a samplerate converter may be used if needed.
+
+When writing an Opus file this command sets the original samplerate header field
+to the provided value, which is then stored in the file. This has no effect on
+the current encoder samplerate.
+
+When reading an Opus file this command overrides the original samplerate value
+as read from the file. libsndfile uses this value to choose what samplerate to
+decode at, rounding up to the nearest valid Opus samplerate. After a successful
+call, the file samplerate and frames count may have changed.
+
+Note: This command should be issued before the first bit of audio data has been
+read from or written to the file.
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_SET_ORIGINAL_SAMPLERATE
+
+data
+: pointer to an integer
+
+datasize
+: sizeof (int)
+
+### Examples
+
+```c
+/* Store the original sample rate as 44100 */
+int original_samplerate 44100;
+sf_command (sndfile, SFC_SET_ORIGINAL_SAMPLERATE, &original_samplerate, sizeof (input_samplerate)) ;
+```
+
+### Return value
+
+Returns SF_TRUE on success, SF_FALSE otherwise.
+
+On write, can only succeed if no data has been written. On read, if successful,
+[SFC_GET_CURRENT_SF_INFO](#sfc_get_current_sf_info) should be called to
+determine the new frames count and samplerate
+
+## SFC_GET_BITRATE_MODE
+
+Get bitrate mode.
+
+The bitrate mode is one of:
+
+| Name                     | Value | Description       |
+|:-------------------------|:------|:------------------|
+| SF_BITRATE_MODE_CONSTANT | 800   | Constant bitrate. |
+| SF_BITRATE_MODE_AVERAGE  | 801   | Average bitrate.  |
+| SF_BITRATE_MODE_VARIABLE | 802   | Variable bitrate. |
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_GET_BITRATE_MODE
+
+data
+: NULL
+
+datasize
+: anything
+
+### Return value
+
+Returns one of `SF_BITRATE_MODE_XXX` on success, `-1` otherwise.
+
+## SFC_SET_BITRATE_MODE
+
+Set bitrate mode.
+
+The bitrate mode is one of:
+
+| Name                     | Value | Description       |
+|:-------------------------|:------|:------------------|
+| SF_BITRATE_MODE_CONSTANT | 800   | Constant bitrate. |
+| SF_BITRATE_MODE_AVERAGE  | 801   | Average bitrate.  |
+| SF_BITRATE_MODE_VARIABLE | 802   | Variable bitrate. |
+
+### Parameters
+
+sndfile
+: A valid SNDFILE* pointer
+
+cmd
+: SFC_SET_BITRATE_MODE
+
+data
+: pointer to an integer
+
+datasize
+: sizeof (int)
+
+### Return value
+
+Returns `SF_TRUE` on success, `SF_FALSE` otherwise.
diff --git a/docs/development.md b/docs/development.md
new file mode 100644
index 0000000..7d417ea
--- /dev/null
+++ b/docs/development.md
@@ -0,0 +1,18 @@
+---
+layout: page
+---
+
+# libsndfile Development
+
+libsndfile is being developed by a small community of users and hackers. People
+interested in helping can raise an issue on Github.
+
+The main repository can be found on Github:
+
+<https://github.com/libsndfile/libsndfile/>
+
+and includes [instructions](https://github.com/libsndfile/libsndfile/blob/master/README.md)
+on how to build libsndfile from the Git repo.
+
+If you are interested in how to add a new format to a libsndfile, you may find
+this [FAQ](new_file_type_howto.md) helpful.
diff --git a/docs/donate.md b/docs/donate.md
new file mode 100644
index 0000000..dd10769
--- /dev/null
+++ b/docs/donate.md
@@ -0,0 +1,60 @@
+---
+layout: page
+title: libsndfile : donate.
+---
+
+{% include logo.html %}
+
+Dear libsndfile user,
+
+This library was developed on Linux for Linux. I am not a Windows user and
+maintaining this library for Windows costs me significant amounts of time above
+and beyond the time taken to make it work on Linux and Unix-like systems.
+
+I therefore ask Windows users of libsndfile to donate to ensure that
+libsndfile's support for Windows continues. As long as donations continue to
+flow in at a decentrate, I will continue to release precompiled Windows binaries
+in sync with the Linux/Unix version. If donations are poor, support for windows
+will fall behind.
+
+You are free to donate any amount you chose. As a guideline:
+
+* If you are simply a user of libsndfile that would like to ensure that the
+  development of libsndfile continues, a donation of $10US would be more than
+  adequate.
+* If you are shareware author that distributes libsndfile with your app and
+  makes more than $1000 a year from your shareware, a one off donation of $50
+  would be appropriate.
+* If your company is a commercial software house that distributes one or more
+  products that ship with libsndfile, a donation of $100 every second or third
+  year would be appropriate.
+
+Donations can be made in Bitcoin to the Bitcoin address
+**15hVWemFiH6DLJ22SBYPk9b4fgWtxBEvfQ** which can be verified by checking the
+following GPG signature.
+
+    -----BEGIN PGP SIGNED MESSAGE-----
+    Hash: SHA256
+
+    libsndfile Bitcoin address : 15hVWemFiH6DLJ22SBYPk9b4fgWtxBEvfQ
+    -----BEGIN PGP SIGNATURE-----
+    Version: GnuPG v1.4.12 (GNU/Linux)
+
+    iQIcBAEBCAAGBQJSK7MUAAoJEEXYQ7zIiotIgXEP/R8hy65tlV7TiPw9bY9BklXS
+    /Vl8FU2RhDkBt61ZmxbfDTybyQ5Vce/3wWph15L4RvpoX1OyeintQFmwwuPjOGiq
+    eIz0nT9vDorG37Xdo5NZNBu9Tp1Od9MNtxFaAsRWFrDfvKEKljBHrcfM972cYrAp
+    DaFd0Ik+bHKom9iQXFB7TFd0w2V4uszVMQDUGqb/vRNeRURZS7ypeMNwc8tZyTKR
+    waEGMTa5sxxRjs7MqGRxSovnFT7JV3TNfdkBInUliIR/XvrudFR9J4Fiv+8Dk9P8
+    WNjm6uFxvgIqiu1G9bjrwwr+DsBju93ljGNcZoayAKw5vwbX6KTcCbc31k9dP8Hf
+    p6YdmPlZVKZmva+P3nLSJBTlxNu24Jm+ha+ZM/svDXTaPFWC8l5FP17kK0Bj8wCq
+    N7pDz6RchEn10u+HdhfT1XiUjxj0zNXrr0GGj9apjl0RlT0O49eBttV0oXIdBRLi
+    nTEaOWITpCgu7ggw1kWXHIWEncuiaSuJy/iH8PgNepWVj/6PxQRMrTqG4ux2Snk8
+    Ua4vO8YHLMZX/XvSUS7eMtgfM7AO6YjJ/ac9bQif9bh6LsYEVVklysMUin6ZRS7Z
+    Cms23FnqeQKtJOzdvqSJiV06lK6fP+tYdM4WSYn+AfL4IfYl2v48xXVU8XOOK9BH
+    bJPKMDcz1ZvfYtX5mSW1
+    =WXGB
+    -----END PGP SIGNATURE-----
+
+Thanks and regards,  
+Erik de Castro Lopo  
+Main libsndfile author and maintainer
diff --git a/docs/embedded_files.md b/docs/embedded_files.md
new file mode 100644
index 0000000..e55f2ed
--- /dev/null
+++ b/docs/embedded_files.md
@@ -0,0 +1,21 @@
+---
+layout: page
+---
+
+# Embedded Sound Files
+
+By using the open SNDFILE with a file descriptor function:
+
+    SNDFILE*  sf_open_fd (int fd, int mode, SF_INFO *sfinfo, int close_desc) ;
+
+it is possible to open sound files embedded within larger files. There are
+however a couple of caveats:
+
+* Read/Write mode (SFM_RDWR) is not supported.
+* Writing of embedded files is only supported at the end of the file.
+* Reading of embedded files is only supported at file offsets greater than zero.
+* Not all file formats are supported (currently only WAV, AIFF and AU).
+
+The test program **multi_file_test.c** in the **tests/** directory of the source
+code tarball shows how this functionality is used to read and write embedded
+files.
diff --git a/docs/formats.md b/docs/formats.md
new file mode 100644
index 0000000..1d96737
--- /dev/null
+++ b/docs/formats.md
@@ -0,0 +1,53 @@
+---
+layout: page
+title: libsndfile: Supported formats
+---
+
+# libsndfile : Supported formats
+
+The following table lists the file formats and encodings that libsndfile can
+read and write. The file formats are arranged across the top and encodings along
+the left edge.
+
+{:.formats-table}
+|                    | Microsoft WAV | SGI / Apple AIFF / AIFC |Sun / DEC / NeXT AU / SND | Headerless RAW | Paris Audio File PAF | Commodore Amiga IFF / SVX | Sphere Nist WAV | IRCAM SF | Creative VOC | Soundforge W64 | GNU Octave 2.0 MAT4 | GNU Octave 2.1 MAT5 | Portable Voice Format PVF | Fasttracker 2 XI | HMM Tool Kit HTK | Apple CAF | Sound Designer II SD2 | Free Lossless Audio Codec FLAC |
+|:-------------------|:-------------:|:-----------------------:|:------------------------:|:--------------:|:--------------------:|:-------------------------:|:---------------:|:--------:|:------------:|:--------------:|:-------------------:|:-------------------:|:-------------------------:|:----------------:|:----------------:|:---------:|:---------------------:|:------------------------------:|
+| Unsigned 8 bit PCM | R/W           | R/W                     |                          | R/W            |                      |                           |                 |          | R/W          | R/W            |                     | R/W                 |                           |                  |                  |           |                       |                                |
+| Signed 8 bit PCM   |               | R/W                     | R/W                      | R/W            | R/W                  | R/W                       | R/W             |          |              |                |                     |                     | R/W                       |                  |                  | R/W       | R/W                   | R/W                            |
+| Signed 16 bit PCM  | R/W           | R/W                     | R/W                      | R/W            | R/W                  | R/W                       | R/W             | R/W      | R/W          | R/W            | R/W                 | R/W                 | R/W                       |                  | R/W              | R/W       | R/W                   | R/W                            |
+| Signed 24 bit PCM  | R/W           | R/W                     | R/W                      | R/W            | R/W                  |                           | R/W             |          |              | R/W            |                     |                     |                           |                  |                  | R/W       | R/W                   | R/W                            |
+| Signed 32 bit PCM  | R/W           | R/W                     | R/W                      | R/W            |                      |                           | R/W             | R/W      |              | R/W            | R/W                 | R/W                 | R/W                       |                  |                  | R/W       |                       |                                |
+| 32 bit float       | R/W           | R/W                     | R/W                      | R/W            |                      |                           |                 | R/W      |              | R/W            | R/W                 | R/W                 |                           |                  |                  | R/W       |                       |                                |
+| 64 bit double      | R/W           | R/W                     | R/W                      | R/W            |                      |                           |                 |          |              | R/W            | R/W                 | R/W                 |                           |                  |                  | R/W       |                       |                                |
+| u-law encoding     | R/W           | R/W                     | R/W                      | R/W            |                      |                           | R/W             | R/W      | R/W          | R/W            |                     |                     |                           |                  |                  | R/W       |                       |                                |
+| A-law encoding     | R/W           | R/W                     | R/W                      | R/W            |                      |                           | R/W             | R/W      | R/W          | R/W            |                     |                     |                           |                  |                  | R/W       |                       |                                |
+| IMA ADPCM          | R/W           |                         |                          |                |                      |                           |                 |          |              | R/W            |                     |                     |                           |                  |                  |           |                       |                                |
+| MS ADPCM           | R/W           |                         |                          |                |                      |                           |                 |          |              | R/W            |                     |                     |                           |                  |                  |           |                       |                                |
+| GSM 6.10           | R/W           | R/W                     |                          | R/W            |                      |                           |                 |          |              | R/W            |                     |                     |                           |                  |                  |           |                       |                                |
+| G721 ADPCM 32kbps  | R/W           |                         | R/W                      |                |                      |                           |                 |          |              |                |                     |                     |                           |                  |                  |           |                       |                                |
+| G723 ADPCM 24kbps  |               |                         | R/W                      |                |                      |                           |                 |          |              |                |                     |                     |                           |                  |                  |           |                       |                                |
+| G723 ADPCM 40kbps  |               |                         | R/W                      |                |                      |                           |                 |          |              |                |                     |                     |                           |                  |                  |           |                       |                                |
+| 12 bit DWVW        |               | R/W                     |                          | R/W            |                      |                           |                 |          |              |                |                     |                     |                           |                  |                  |           |                       |                                |
+| 16 bit DWVW        |               | R/W                     |                          | R/W            |                      |                           |                 |          |              |                |                     |                     |                           |                  |                  |           |                       |                                |
+| 24 bit DWVW        |               | R/W                     |                          | R/W            |                      |                           |                 |          |              |                |                     |                     |                           |                  |                  |           |                       |                                |
+| Ok Dialogic ADPCM  |               |                         |                          | R/W            |                      |                           |                 |          |              |                |                     |                     |                           |                  |                  |           |                       |                                |
+| 8 bit DPCM         |               |                         |                          |                |                      |                           |                 |          |              |                |                     |                     |                           | R/W              |                  |           |                       |                                |
+| 16 bit DPCM        |               |                         |                          |                |                      |                           |                 |          |              |                |                     |                     |                           | R/W              |                  |           |                       |                                |
+
+From version 1.0.18, libsndfile also reads and writes
+[FLAC](https://xiph.org/flac/) and [Ogg/Vorbis](https://xiph.org/vorbis/).
+
+From version 1.0.29, libsndfile can read and write
+[Ogg/Opus](https://opus-codec.org/).
+
+From version 1.1.0, libsndfile can read and write MP3.
+
+Some of the file formats I am also interested in adding are:
+
+- Kurzweil K2000 sampler files.
+- Ogg Speex.
+
+Other file formats may also be added on request.
+
+If you are interested in how to add a new format to a libsndfile, you may find
+this [FAQ](new_file_type_howto.md) helpful.
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000..bc5890d
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,322 @@
+---
+layout: home
+title: The libsndfile Home Page
+---
+
+Libsndfile is a C library for reading and writing files containing sampled sound
+(such as MS Windows WAV and the Apple/SGI AIFF format) through one standard
+library interface. It is released in source code format under the
+[Gnu Lesser General Public License](http://www.gnu.org/copyleft/lesser.html).
+
+The library was written to compile and run on a Linux system but should compile
+and run on just about any Unix (including MacOS X).
+There are also pre-compiled binaries available for 32 and 64 bit windows.
+
+It was designed to handle both little-endian (such as WAV) and big-endian (such
+as AIFF) data, and to compile and run correctly on little-endian (such as Intel
+and DEC/Compaq Alpha) processor systems as well as big-endian processor systems
+such as Motorola 68k, Power PC, MIPS and Sparc. Hopefully the design of the
+library will also make it easy to extend for reading and writing new sound file
+formats.
+
+It has been compiled and tested (at one time or another) on the following
+systems:
+
+* Every platform supported by Debian GNU/Linux including x86_64-linux-gnu,
+  i486-linux-gnu, powerpc-linux-gnu, sparc-linux-gnu, alpha-linux-gnu,
+  mips-linux-gnu and armel-linux-gnu.
+* powerpc-apple-darwin7.0 (Mac OS X 10.3)
+* sparc-sun-solaris2.8 (using gcc)
+* mips-sgi-irix5.3 (using gcc)
+* QNX 6.0
+* i386-unknown-openbsd2.9
+* Microsoft Windows
+
+At the moment, each new release is being tested on i386 Linux, x86_64 Linux,
+PowerPC Linux, Win32 and Win64.
+
+## Features
+
+libsndfile has the following main features :
+
+* Ability to read and write a large number of [file formats](formats.html).
+* A simple, elegant and easy to use Applications Programming Interface.
+* Usable on Unix, Win32, MacOS and others.
+* On the fly format conversion, including endian-ness swapping, type conversion
+  and bitwidth scaling.
+* Optional normalisation when reading floating point data from files containing
+  integer data.
+* Ability to open files in read/write mode.
+* The ability to write the file header without closing the file (only on files
+  open for write or read/write).
+* Ability to query the library about all supported formats and retrieve text
+  strings describing each format.
+
+libsndfile has a comprehensive test suite so that each release is as bug free
+as possible.
+When new bugs are found, new tests are added to the test suite to ensure that
+these bugs don't creep back into the code.
+When new features are added, tests are added to the test suite to make sure that
+these features continue to work correctly even when they are old features.
+
+## History
+
+My first attempt at reading and writing WAV files was in 1990 or so under
+Windows 3.1. I started using Linux in early 1995 and contributed some code to
+the [wavplay](http://www.vaxxine.com/ve3wwg/gnuwave.html) program. That
+contributed code would eventually mutate into this library. As one of my
+interests is Digital Signal Processing (DSP) I decided that as well as reading
+data from an audio file in the native format (typically 16 bit short integers)
+it would also be useful to be able to have the library do the conversion to
+floating point numbers for DSP applications. It then dawned on me that whatever
+file format (anything from 8 bit unsigned chars, to 32 bit floating point
+numbers) the library should be able to convert the data to whatever format the
+library user wishes to use it in. For example, in a sound playback program, the
+library caller typically wants the sound data in 16 bit short integers to dump
+into a sound card even though the data in the file may be 32 bit floating point
+numbers (ie Microsoft's WAVE_FORMAT_IEEE_FLOAT format). Another example would be
+someone doing speech recognition research who has recorded some speech as a 16
+bit WAV file but wants to process it as double precision floating point numbers.
+
+Here is the release history for libsndfile:
+
+* Version 0.0.8 (Feb 15 1999) First official release.
+* Version 0.0.28 (Apr 26 2002) Final release of version 0 of libsndfile.
+* Version 1.0.0rc1 (Jun 24 2002) Release candidate 1 of version 1 of libsndfile.
+* Version 1.0.0rc6 (Aug 14 2002) MacOS 9 fixes.
+* Version 1.0.0 (Aug 16 2002) First 1.0.X release.
+* Version 1.0.1 (Sep 14 2002) Added MAT4 and MAT5 file formats.
+* Version 1.0.2 (Nov 24 2002) Added VOX ADPCM format.
+* Version 1.0.3 (Dec 09 2002) Fixes for Linux on ia64 CPUs.
+* Version 1.0.4 (Feb 02 2003) New file formats and functionality.
+* Version 1.0.5 (May 03 2003) One new file format and new functionality.
+* Version 1.0.6 (Feb 08 2004) Large file fix for Linux/Solaris, new
+  functionality   and Win32 improvements.
+* Version 1.0.7 (Feb 24 2004) Fix build problems on MacOS X and fix ia64/MIPS
+  etc clip mode detection.
+* Version 1.0.8 (Mar 14 2004) Minor bug fixes.
+* Version 1.0.9 (Mar 30 2004) Add AVR format. Improve handling of some WAV
+  files.
+* Version 1.0.10 (Jun 15 2004) Minor bug fixes. Fix support for Win32 MinGW
+  compiler.
+* Version 1.0.11 (Nov 15 2004) Add SD2 file support, reading of loop data in WAV
+  and AIFF. Minor bug fixes.
+* Version 1.0.12 (Sep 30 2005) Add FLAC and CAF file support, virtual I/O
+  interface. Minor bug fixes and cleanups.
+* Version 1.0.13 (Jan 21 2006) Add read/write of instrument chunks. Minor bug
+  fixes.
+* Version 1.0.14 (Feb 19 2006) Minor bug fixes. Start shipping windows
+  binary/source ZIP.
+* Version 1.0.15 (Mar 16 2006) Minor bug fixes.
+* Version 1.0.16 (Apr 30 2006) Add support for RIFX. Other minor feature
+  enhancements and bug fixes.
+* Version 1.0.17 (Aug 31 2006) Add C++ wrapper sndfile.hh. Minor bug fixes and
+  cleanups.
+* Version 1.0.18 (Feb 07 2009) Add Ogg/Vorbis support, remove captive
+  libraries, many new features and bug fixes. Generate Win32 and Win64
+  pre-compiled binaries.
+* Version 1.0.19 (Mar 02 2009) Fix for CVE-2009-0186. Huge number of minor fixes
+  as a result of static analysis.
+* Version 1.0.20 (May 14 2009) Fix for potential heap overflow.
+* Version 1.0.21 (December 13 2009) Bunch of minor bug fixes.
+* Version 1.0.22 (October 04 2010) Bunch of minor bug fixes.
+* Version 1.0.23 (October 10 2010) Minor bug fixes.
+* Version 1.0.24 (March 23 2011) Minor bug fixes.
+* Version 1.0.25 (July 13 2011) Fix for Secunia Advisory SA45125. Minor bug
+  fixes and improvements.
+* Version 1.0.26 (November 22 2015) Fix for CVE-2014-9496, CVE-2014-9756 and
+  CVE-2015-7805. Add ALAC/CAF support. Minor bug fixes and improvements.
+* Version 1.0.27 (June 19 2016) Fix a seek regression in 1.0.26. Add metadata
+  read/write for CAF and RF64. FIx PAF endian-ness issue.
+* Version 1.0.28 (April 2 2017) Fix buffer overruns in FLAC and ID3 handling
+  code. Reduce default header memory requirements. Fix detection of Large File
+  Support for 32 bit systems.
+* Version 1.0.29 (August 15 2020) Opus support, build system improvements and
+  bug fixes.
+* Version 1.0.30 (September 19 2020) Bugfix release. Fix file descriptor leaks
+  in sf_open_fd () function. Fix critical CMake bug leading to broken ABI on
+  Linux platforms. Other numerous fixes to CMake build system, consider it
+  stable now. Fix some memory leaks. Fix handling of some SD2 files. Update
+  documentation. Integrate GitHub Actions for faster test builds and Oss-Fuzz
+  for fuzzing tests. Move sndfile.h.in from src/ to include/ directory. To avoid
+  problems, delete old generated sndfile.h from $(top_builddir)/src.
+* Version 1.0.31 (January 24 2021) Bugfix release. Fix multiple memory leaks
+  reported by OSS-Fuzz. More SSE2-optimized functions for x86 and amd64.
+* Version 1.1.0 (March 27 2022) Minor release, backward compatible with previous
+  releases. Added long-awaited MP3 support. Numerous improvements and bugfixes.
+* Version 1.2.0 (December 25 2022) Various bugfixes,
+  removed artificial samplerate limit
+* Version 1.2.1 (August 12 2023) Patch release, various bugfixes.
+* Version 1.2.2 (August 13 2023) Patch release, various bugfixes.
+
+## Similar or Related Projects
+
+* [SoX](http://sox.sourceforge.net/) is a program for converting between sound
+  file formats.
+* [Wavplay](http://www.hitsquad.com/smm/programs/WavPlay/) started out as a
+  minimal WAV file player under Linux and has mutated into Gnuwave, a
+  client/server application for more general multimedia and games sound
+  playback.
+* [Audiofile](http://www.68k.org/~michael/audiofile/) (libaudiofile) is a
+  library similar to libsndfile but with a different programming interface. The
+  author Michael Pruett has set out to clone (and fix some bugs in) the
+  libaudiofile library which ships with SGI's IRIX OS.
+* [sndlib.tar.gz](ftp://ccrma-ftp.stanford.edu/pub/Lisp/sndlib.tar.gz) is
+  another library written by Bill Schottstaedt of CCRMA.
+
+## Licensing
+
+libsndfile is released under the terms of the GNU Lesser General Public License,
+of which there are two versions;
+[version 2.1](http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html)
+and
+[version 3](http://www.gnu.org/copyleft/lesser.html).
+To maximise the compatibility of libsndfile, the user may choose to use
+libsndfile under either of the above two licenses.
+You can also read a simple explanation of the ideas behind the GPL and the LGPL
+[here](http://www.gnu.org/copyleft/lesser.html).
+
+You can use libsndfile with
+[Free Software](http://www.gnu.org/),
+[Open Source](http://www.opensource.org/),
+proprietary, shareware or other closed source applications as long as libsndfile
+is used as a dynamically loaded library and you abide by a small number of other
+conditions (read the LGPL for more info).
+With applications released under the GNU GPL you can also use libsndfile
+statically linked to your application.
+
+I would like to see libsndfile used as widely as possible but I would prefer it
+if you released software that uses libsndfile as
+[Free Software](http://www.gnu.org/)
+or
+[Open Source](http://www.opensource.org/).
+However, if you put in a great deal of effort building a significant application
+which simply uses libsndfile for file I/O, then I have no problem with you
+releasing that as closed source and charging as much money as you want for it as
+long as you abide by [the license](http://www.gnu.org/copyleft/lesser.html).
+
+## Download
+
+Check latest version on
+[GitHub Releases page](https://github.com/libsndfile/libsndfile/releases/).
+
+Binaries and source packages are signed by current releaser David Seifert aka
+@SoapGentoo. You can verify signatures with his public GPG key:
+
+```
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2
+
+mQINBFppABgBEAC42ZiNvV7BTIgR6TQy0YnF54fx3mVRP1u8Mq00UZa7reAsNKh7
+1H60j0W4s6+4pVVIKGfpVGxLwUdJe+KVCYw1Cd3YW6uMf5zZrC/ZWqnJiH/n6S6o
+1l4INII2o6YbGBnzIWBPRo7PlOL+mvgKTLpBSJPnhD8XDGN5wRiV8rL2+6Dptg0F
+nJt7oxECGF3OD3gk6HMel0o82CVkIqMtNaX1L/bhcdF7K0Rp2MXPZMmpn1izW5sI
+asN1G9+w+Zwj7kMJzq1Aw3ac+rsX4SEYdvXjS2QhDHQUIr6LXri3D2WbcEqIZj2R
+JVoVwblsrG11dYXFDBbgrq4NhgTBsxHYDlkr/qF2W+kbPC/nhSqTVZeCYvTBZbOQ
++RqyN/I0izukglnWmV1jGijFA8snyP8efx732hw/24zRYmtXOtnEITUpw8WOeZCq
+6uiHaQ+eopnY2ojBg9BI7WZm0AFn58xxT9soMsyFOUFgXTqaWFZWlJ3fhZE8/0v8
+JEu/kPGE5aJReT3b34B+Bojkj74XR+h2u7iJJBHMTE8RwGoUOZHer/XsL9xlcdks
+I+7TCjiq++ShaSSt2XsJmw2BhREohrjW/2KkwmvT3b44RMpKPB4WTH+++aqJQNeM
+IqmswOMoZvzEZezInj7WVY/r0WEei1Y6wt1tBrJ/cFf1oQBM1UmphxcrfQARAQAB
+tB9EYXZpZCBTZWlmZXJ0IDxzb2FwQGdlbnRvby5vcmc+iQJUBBMBCgA+BQsJCAcD
+BRUKCQgLBRYCAwEAAh4BAheAAhsBFiEEMdlcq22A0mIkShdQpHYg6AHkfpUFAl/V
+CvoFCQkuceIACgkQpHYg6AHkfpXYxA//aiJW1NwunpmzEc62id8lRMnoLHWVjISZ
+b+xSlm+hk4LYq+ZbthJDzKcT86/3DJOSE1zQw9wLuCao9IW2UfFJQBtR+TAfbagG
+0Yyk/kMcLoFJxnG1ywdJWypCAauuIhia52Z7PmmjsBbFwr6LygDwSQmZAyACMAs7
+TLQe+yERc2RNDsIEsquLSxxRF0Spk9gagWtKgrPc2XBjuNtQDwW7JgsOUoEeHyxC
+29fRUjC3o/pG2I6iAZp17OROZI5yl4TSORrSBDGIi2sayxyxP0x+IPKtrCUcBGNx
+wGp+56bP/V0hA6sgCPh/iwvqLoeibso6l/Kd4ltVAEQnHTd6fr8g+wLEUXfbJVTR
+7aeFUoaFmWjSPlQrNr6HlxSLV/kRx9kVJp1Pn16vkfVBF7fG7iDLiqphwEeQg5ND
+nmGeKAbRRNxFHyBHf0XRsaYiFZQckguO+71XSRtVx8/YP5nyNbtl9y1h/4JlT6Gy
+t7hb5twYFQyQrKss83E/Bo1sRdHpj0ibtqb4ZbYANbh482E6yFhAkuo8YjVTJipI
+1Ve8EBKnX3R+pDt147uyysNvtPVXML+sWpGSMVSm4NA8uT3F5nqxVwj+SeXy3Wq/
+CHQ2VBKGBC655G+wFD5C6O7cTx2MwH+2H8tzhWm+gFlI3MFKEXa/PC+YUC/diYcb
+BrApavriTRa5Ag0EWmkAZgEQAPXMD3mZI+ChvBysXZWksC88/uSEwFeb3XkcRm7v
+04GN7hcz+bfrmnUTB3tuE/ZQgv+u7ZjetvH1aEKieznn/GjnWoOBoJusOYvfAQeF
+0mQVi118QiOZRCnEZpkz+RY9TiXVgrZJg+AGqHZ3Ol4GkInEV2NWgH37Xal+HkFl
+rwI2U7mL0kZRG+LAVCQHKzqU0R0HE1XyJ4qf0awtG5Qi/TZvgXBdZPDXgr8i9Vlf
+UUu10c2XnXM0Av/YAlZmBFjVYrSOUCFenqSVqL+s9sTCVdWlJrGjrr3Ja4uT3kl2
+rLva0AR4oSQoxt8adKohmFz0vzOkQtCoRzhrCwoo3JvNjKdSNoOP1nSsxlO5ji8r
+ih5d+ajPgi580XyHLnrvG7vobR48qqscv1hizKuCgTacOTe6Db2Gqc8xF6v8HhJa
+KwWJtmFllIfN/tIvZ6BbbgHQn0IGf4CYnWf0SksPZqpBmTRpD2jfBxcj2UEg+AR3
+LARjuyUVpFJScyu6ExQG+6O+ByLL31iWP5MgUrza1rIpriPa3NT3rZ3DG2pvQrS3
+ySsrPzH7VRX8L1ThSMSzjwF96aMsd14s7XzR4EzNuWwZDukfs0yavZk6l4o1M0mb
+tbJi7hE4cz13KRHYvIkKMdZGYUnzRzZUDlsj2imakk3BR6GXnxZ1ST6062g+QxiL
+AJFLABEBAAGJBHIEGAEKACYCGwIWIQQx2VyrbYDSYiRKF1CkdiDoAeR+lQUCX9UL
+DQUJCS5xpwJAwXQgBBkBCgAdFiEEuNUxXaAAcsCoYIifzjbhFyAuOEIFAlppAGYA
+CgkQzjbhFyAuOELmrQ/9H9wrWsWa21STZdxUmyU2sh9VXAWEHl1Ey0fVTznDM0Fl
+zx5YSR/TmmnE36rpaz31Ttkx8SP914oV+mMgseecdya9Bf6uZL9Cv7V3KEsJBRL/
+ncrOWQBHP/Xy1X+mLD6A19xq7H4RihSLj0LeK2YVjrJzJ7wMf4mKXuBayQeAHImU
+WRCRTbmK3umh2nB5V0iPd/XZEIiYtiTPe+7E/va6+0bBvOumF3a+Z0iui7eU4hFC
+7Jk71D0dcg09SlIaNoMOrw7cMC3j2pMdKtsj8+0I6WBv14PhhqPAsnjdf7I/4NfK
+L7Jav8T/gDS01uA2Jxm72d+wr+eSjOBXa6x8CEbTqfkjAGxsWENThCp6zDkaXSDd
+JsV0va47vjzG8+wTDAvPy5IxIM/KZZdl4uWM+mF5K+q+eSTOHe7aLF2OdcussoBA
+A18zm994dAkG1COX/qpxanxx2bv/2IvCGPg+x6JtAN8ji2kncWu3dWGQdE5XbVjc
+fDwgsUPpp04G27Mr/x+HpEbgZ5SdA0dAqJktlNvCcHALhlblCWrsh/1QNjT/2iG8
+wsjcpEy/s4tWAuV4PTa4xvZ1JPS7Z7Eo5aBy9ZGOWG9SrHEiHnhkUsiswbHBOEjd
+pBSkmNElDcv9fRUahVCTPfvWBATFDrQyMjJBSm+cV8c/iFQM7isVSu8W7E0eetsJ
+EKR2IOgB5H6Vv9sP/1dxTvH0N0UoEoxIG/hnirEkbRpljdvqy4/uikYBKyQgSbo8
+VITTjea7gIhDztil9WZYt35jbOmoaGM2Z6TP2LEDOWgljYUNq9pl9Sc2GS8cNtEO
+WxExzGOc1Flo730dX3A85Ks3+0WPXZjLDcRRcPVkFd5WLQQDV1YVYopWkuQBC+Br
+4q3uv+sk+bw6gDa9+zFBbDuegdsYuTXrFHoxHz2GRv9Yb7ULCMgpFeNKDgtQq91u
+RqewoTwQp9tlp91LH/hh7R0Q4DRgeFDkLnVRXwSKjVvCrT5cBgImGwtFTGS4egoy
+MDKd/KKjZllp1ahRCln1XfmFQyQVMVvuF/JTtt31n6KwXwK2yxIlXB01xvRH+Ees
+AWeRYWKWXydaAY/9Ve0/PLFlgsr/XUGvt0GoEKe7odD3nZgg6015+/8JTroKw19L
+NZkhdfFMl11Zi0j5k3UbyzjYVpFSd8K2o0VoOG1LFsPp8tlRxNoVzpId0CX1au/p
+y1H7Wy/39mzriRG3rw+mJAQbBjN09putCltXFXpOEWk08n/N3vufCVQUoSu/2Bqw
+2HYj8VtToQp+O5dG3XxvDHINtInP1yr2Wcw2plna0KoXLwv/lZgDm3LN+eCWpG6d
+N/xk25DTSqTHArUQIEkhcHYK6GnyxUcvoKtG88hXtqEPYXiK08FZYAUPTnDYuQIN
+BFppAIkBEADDjvQZUs1NoqJpxkD2QDBudU1DBCaeI1D6CancMtb5FebPUxgFlDMd
+CBGOun48dY5i87gDhT/qS3gP/Mv9rjKJmcG9JHfhpXdW73owxrcsQ96nxxVJNEVl
+UHJw00z8C9eGWqr0SzSoE33K/PkzSkgtsaotF6+3uCerWulweulmGa5dpVfV0mbS
+aVw8VmrhZ5NmCeodyy/lR85rPik5pb32NT6v7xBkgkfS0VYtPB2E5gW1pXX/jEOi
+Mfq9idOEP9lxrNXV9j49Lr0JQCwAcrYbQ2+VPe6eacJEjzJ/6HiUqhPrYdnvydmb
+hU+xmv2NjGp2UnDZDEhzQfwm6fMx+8Nx2uPzCnXQGoyRBwiC/KcdW0F1ZPKdSXqH
+NKoOF62pLvIMSmfI3ZVOrTohArfr1kFEYVDv9Nl7oY+qg2rZEc2srOF74a9Z46bR
+TDPsEQzE2UMCvu3+rofhSD7aRotlKeDCvbe2s0yE4Man457Xc3LXh8Gva8CzCOLE
+2eMhNTsHIZk68WgXp3/uvE4Xy42myrk1AV8XXDdlWgx0Kc/I6tE59O5NVPSfuGvH
+1a15KKx0F6euEnYDKKpQ5PDR6dSn61po0tfbt96m044G/xQFjrfhHei4jji9Ogd9
+vlXVAi2vn3+NCSHFP5l3igLByBHy9iLIdmz7yQuus/1nwRmxOHOf2QARAQABiQI8
+BBgBCgAmAhsMFiEEMdlcq22A0mIkShdQpHYg6AHkfpUFAl/VCxkFCQkucZAACgkQ
+pHYg6AHkfpVPSRAAmheYkYJmtDbkzPBBnj5mbCIQN1/G5PI9eixc/TXWFOXtcjU1
+mJlJpSidHJyLRrx7r0c+N+s8vnY/JuUBsNoMJMER+Mv/CFW4iFi59V534SyAb2S0
+7NINJnFNkXBY62CDz9KsMuv/MdSv2yLhPH2Tfrm/eDRQesj1PanE4U1cgjWyJRc/
+IOlaRHvTasWDLgwbQi8ykt+4xUWzL/YKHzB+KyyzBK7vPBXqySX8ka4BOw7SDwG5
+lX2gtmhk4AGBwVChLXKflqVx1WXj4DPOt0kmOKVnKFyvUijK58M0A2FMgFMXDTIS
+DRtoZPdx/rkODXxgS+W+27NcYAnxJiM0cQqizEnQh7PQ1KzgdChPejYXMKe9lwdn
+ssMUxrBpbuAuagEf+pebNjD2eaNR4p8kfaDdGn53q55ysDvoyxKvnVQGSk1FAR9Q
+s4N5a4f02U7dzlyEhEfIcuUlRCfnlpn4n725YIhHheDig5zKWoEZCkNIfiRcGzDl
+8Drj+tlZiUR+gDkIoWSBaCkKbIQlc8qCYy6Hm7oZBaol6xKlUnTMK2rjK8fR4i8r
+bVDWBAaWj3jcDHJ0Jg3fS/qBpeya/JXMp89TR8NK5Ys7PZpWbor+puXBYyXDAVx3
+rXQ7JBA5klHPxrgjso1S/LqwscKLENtrVjdjhryLBmPifrmofJRnrpiHIEa5Ag0E
+WmkAswEQAL0hKwsRybQzkNGpJP+ElLSwFHd7XQhr+qIwLllpumWtnIK/DHmv8SpW
+FqAYajmRTXipFcBHH25x2jIIliZidn0a9826l+sMzrFadMC6/W4pitP71TeqZzwn
+pAuHs14YL7Wiy0aJQnfbCpRzPq3kYyOXmhmY7lPWO0WdUpR6W8wUbleK5XOVDDRx
+aIC/M3hhDOxZOMzQ+pdn4BaOFQQ0ygsRkqOudbuc0R1giYRt1i6gMeT8gfzL9jlw
+HcJ+aVnxdUQQ4uC47oKo/+lg7qh7LsiW79pQC1Bcdm8lhRmqtxe6ub60ecjax3XU
+1ILIEfIFCv6M7LRUAwz0bqk35spgkJqrGGKkdeWEKAFHg2QWR2F0zy+HdlPLfKxO
+uhaccpwc9EJtf744GS0SXa2AXr32j56n7CFcEjFcIQPBC6OJn6eA3hOVUYGZ7SrT
+4fsmZiFAdGEkvLKFuNhju1Hj2EJQUY1pm4GSBco7BR8x+QqoYrt5clU3WxRMNfTR
+0Rtuzsh4xskXNVMMgvKOahAtxENv2M2Cx6zJPVL5dmaysP7d6QRVeOQA5PwkcZ5Q
+qK6JtDZj2jpaKQH4Za715kiIcdqMDSkwxa6avc0kARHvfFcBR4hwDm1GAlaKG7eH
+8TOGGQIk8x2F3s4l8mTJVLWTP/uJYnkYBdqANYo5t1NIQLvwLFV3ABEBAAGJAjwE
+GAEKACYCGyAWIQQx2VyrbYDSYiRKF1CkdiDoAeR+lQUCX9ULIwUJCS5xcAAKCRCk
+diDoAeR+leekD/sF7aHH0W35ckWrXZlfSp0qHPWrBUaLBI9OAUHenRhgs4SbK0D4
+wqEiu0C5iDQojpXAeALQ8g/1pUsZ1yuFqYbGYWrHkA0Pm+P3tAGB4LMZ41YfvROP
+uaiW/+IMJbWllgRtaDt8/NtCgs30WI9I+az5M29HcGfvEwEUykrBx3dE9T+1ui3O
+capdd+GMvdAAsX5PyVkjWgZ7GrZeH8mG7UysYfT4qthxEtQfZ/u8ceSduKA46ugh
+C2eafIDNvluqn7BU4oKxME61u6C8BN2yHLI6LV0Tr4z5H8joVbM4BSFMwLVGlsXf
+HhB8kLiErN6bXolxsjARlmYiD9S9H2AcYidr6RYXf2EVFSpBG59xn1WTDN+DsHQf
+7btNPEPl/OPxa3OQjG+xn8USddiP0N0B4xsyzMNCCKDgvXXcIhX55KG9eh3Tc98S
+fEyhxu8ybZBIGmTJysPKxijfvSgQF+RPNTsz9lvXqkoK7RTgeYMschpjJEznCLbt
+M6eTDb5z0G5uLXh6+dYxtDOlPogI5OHd+G51LwCjvrQ+AtIUCgafuemwA9mpFT2b
+svb/qcxSVUb44bVaNHn1JHebX2YbokGtBOm1x2PI5fT8n6YIIYz3jKYOZAYdUT7x
+6qURyNjOfG4aPJIATwuh4GSNuxUG40+yuT+XfQF24mu1esS1J3wzRloJ7w==
+=K3x+
+-----END PGP PUBLIC KEY BLOCK-----
+```
+
+## See Also
+
+* [sndfile-tools](https://github.com/libsndfile/sndfile-tools): a small
+collection of programs which use libsndfile.
diff --git a/docs/libsndfile.css b/docs/libsndfile.css
new file mode 100644
index 0000000..3386bc3
--- /dev/null
+++ b/docs/libsndfile.css
@@ -0,0 +1,101 @@
+body {
+	background : black ;
+	color : white ;
+	font-family : arial, helvetica, sans-serif ;
+	line-height: 1.5 ;
+	margin-left: 6%;
+	margin-right: 6%;
+}
+td {
+	font-family : arial, helvetica, sans-serif ;
+	background : black ;
+	color : white ;
+}
+center {
+	font-family : arial, helvetica, sans-serif ;
+}
+p {
+	font-family : arial, helvetica, sans-serif ;
+	text-align : left ;
+}
+.indent_block {
+	font-family : arial, helvetica, sans-serif ;
+	text-align : left ;
+}
+br {
+	font-family : arial, helvetica, sans-serif ;
+}
+form {
+	font-family : arial, helvetica, sans-serif ;
+}
+ul {
+	font-family : arial, helvetica, sans-serif ;
+	text-align : left ;
+}
+ol {
+	font-family : arial, helvetica, sans-serif ;
+	text-align : left ;
+}
+dl {
+	font-family : arial, helvetica, sans-serif ;
+	text-align : left ;
+}
+h1 {
+	font-size : xx-large ;
+	background : black ;
+	color : #5050FF ;
+	text-align : left ;
+}
+h2 {
+	font-size : x-large ;
+	background : black ;
+	color : #5050FF ;
+	text-align : left ;
+}
+h3 {
+	font-size : large ;
+	background : black ;
+	color : #5050FF ;
+	text-align : left ;
+}
+h4 {
+	font-size : medium ;
+	background : black ;
+	color : #5050FF ;
+	text-align : left ;
+}
+pre {
+	font-family : courier, monospace ;
+	font-size : medium ;
+}
+code,
+kbd,
+samp,
+var {
+	font-family: "Courier New", monospace, serif;
+	font-size: 1em;
+}
+a:link { color : #9090FF ; }
+a:visited { color : #5050FF ; }
+a:active { color : #FF00FF ; }
+a:hover { background-color : #202080 ; }
+
+table.formats-table {
+	text-align: center;
+	font-size: small;
+	border-collapse: collapse;
+}
+
+table.formats-table, table.formats-table th, table.formats-table td {
+	border: 1px solid white;
+}
+
+.image-logo {
+	display: block;
+	margin-left: auto;
+	margin-right: auto;
+}
+
+nav {
+	text-align: center;
+}
diff --git a/docs/libsndfile.jpg b/docs/libsndfile.jpg
new file mode 100644
index 0000000..7855b92
--- /dev/null
+++ b/docs/libsndfile.jpg
Binary files differ
diff --git a/docs/linux_games_programming.txt b/docs/linux_games_programming.txt
new file mode 100644
index 0000000..ca6a94f
--- /dev/null
+++ b/docs/linux_games_programming.txt
@@ -0,0 +1,434 @@
+# Here are some some emails I exchanged with a guy trying to use 
+# libsndfile version 1 with code from the book "Linux Games Programming"
+# by John Hall. The email addresses have been changed to foil the spam
+# bots.
+
+Date: Tue, 20 Jul 2004 22:49:21 +0100
+From: Paul <paul@fake-domain-name.co.uk>
+To: erikd@fake-domain-name.com
+Subject: Can you help with a problem?
+Date: Tue, 20 Jul 2004 22:49:21 +0100
+
+Hi,
+
+I'm trying to get the source examples in the "Programming Linux Games" 
+(NoStarch, Loki Software + John R. Hall) which use sndfile.h/libsndfile.
+
+While I can guess some of the newer versions of function calls and 
+enumerations, there are some which I cannot guess.
+
+Would you be able to translate them to the current version of 
+enumeration and function calls so that I can update the source?
+
+These are the three currently failing me:
+
+    sf_open_read(filename, SF_INFO *sfinfo) (guess: sf_open(filename,SFM_READ, &sfinfo))
+    SF_FORMAT_PCM (guess: either SF_FORMAT_PCM_U8 or _RAW)
+    SF_INFO.pcmbitwidth (guess: no idea!)
+
+There are probably more. I'm happy to send you the source files for 
+sound calls, scan the pages or anything else. Failing that, is there 
+somewhere with the changes listed so I can try and fix the code for myself?
+
+Thanks
+
+TTFN
+
+Paul
+
+================================================================================
+
+Date: Wed, 21 Jul 2004 17:38:08 +1000
+From: Erik de Castro Lopo <erikd@fake-domain-name.com>
+To: Paul <paul@fake-domain-name.co.uk>
+Subject: Re: Can you help with a problem?
+
+On Tue, 20 Jul 2004 22:49:21 +0100
+Paul <paul@fake-domain-name.co.uk> wrote:
+
+> Hi,
+> 
+> I'm trying to get the source examples in the "Programming Linux Games" 
+> (NoStarch, Loki Software + John R. Hall) which use sndfile.h/libsndfile.
+> 
+> While I can guess some of the newer versions of function calls and 
+> enumerations, there are some which I cannot guess.
+> 
+> Would you be able to translate them to the current version of 
+> enumeration and function calls so that I can update the source?
+> 
+> These are the three currently failing me:
+> 
+> sf_open_read(filename, SF_INFO *sfinfo) (guess: sf_open(filename, 
+> SFM_READ, &sfinfo))
+
+yes.
+
+> SF_FORMAT_PCM (guess: either SF_FORMAT_PCM_U8 or _RAW)
+
+Actually this list:
+
+    SF_FORMAT_PCM_U8
+    SF_FORMAT_PCM_S8
+    SF_FORMAT_PCM_16
+    SF_FORMAT_PCM_24
+    SF_FORMAT_PCM_32
+
+> SF_INFO.pcmbitwidth (guess: no idea!)
+
+WIth the above change, pcmbitwidth becomes redundant.
+
+> There are probably more. I'm happy to send you the source files for 
+> sound calls, scan the pages or anything else. Failing that, is there 
+> somewhere with the changes listed so I can try and fix the code for
+> myself?
+
+Version 1.0.0 came out some time ago, but I think this:
+
+    http://www.mega-nerd.com/libsndfile/version-1.html
+
+lists most of the changes. You should also look at the API docs:
+
+    http://www.mega-nerd.com/libsndfile/api.html
+
+HTH,
+Erik
+-- 
++-----------------------------------------------------------+
+  Erik de Castro Lopo      nospam@fake-domain-name.com
++-----------------------------------------------------------+
+"There is no reason why anyone would want a computer in their home"
+Ken Olson, DEC, 1977
+
+================================================================================
+
+From: PFJ <paul@fake-domain-name.co.uk>
+To: Erik de Castro Lopo <erikd@fake-domain-name.com>
+Subject: Re: Can you help with a problem?
+Date: Wed, 21 Jul 2004 09:07:39 +0100
+
+
+Hi Erik,
+
+Thanks for getting back to me.
+
+> > sf_open_read(filename, SF_INFO *sfinfo) (guess: sf_open(filename, SFM_READ, &sfinfo))
+>
+> yes.
+
+Yay!
+
+> > SF_FORMAT_PCM (guess: either SF_FORMAT_PCM_U8 or _RAW)
+>
+> Actually this list:
+>
+>     SF_FORMAT_PCM_U8
+>     SF_FORMAT_PCM_S8
+>     SF_FORMAT_PCM_16
+>     SF_FORMAT_PCM_24
+>     SF_FORMAT_PCM_32
+
+I know, but the source code explicitly has SF_FORMAT_PCM which given the
+code afterwards would equate to one of the above, but given that PCM
+files can have a varied bitwidth the author probably wanted to cover all
+bases.
+
+> Version 1.0.0 came out some time ago, but I think this:
+>
+>     http://www.mega-nerd.com/libsndfile/version-1.html
+>
+> lists most of the changes. You should also look at the API docs:
+>
+>     http://www.mega-nerd.com/libsndfile/api.html
+
+I'll download them and see what I can gleen.
+
+Thanks again for getting back to me
+
+TTFN
+
+Paul
+
+================================================================================
+
+Date: Wed, 21 Jul 2004 18:20:29 +1000
+From: Erik de Castro Lopo <erikd@fake-domain-name.com>
+To: PFJ <paul@fake-domain-name.co.uk>
+Subject: Re: Can you help with a problem?
+
+On Wed, 21 Jul 2004 09:07:39 +0100
+PFJ <paul@fake-domain-name.co.uk> wrote:
+
+> I know, but the source code explicitly has SF_FORMAT_PCM which given the
+> code afterwards would equate to one of the above, but given that PCM
+> files can have a varied bitwidth the author probably wanted to cover all
+> bases.
+
+But surely the existing code does something like:
+
+    sfinfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM;
+    sfinfo.pcmbitwidth = 16;
+
+which can be directly translated to:
+
+    sfinfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
+
+and the same for pcmbitwitdhs of 24 and 32. For pcmbitwidth of 8
+you need to know that WAV files use SF_FORMAT_PCM_U8 and AIFF
+files use SF_FORMAT_PCM_S8. That's all there is to it.
+
+Erik
+-- 
++-----------------------------------------------------------+
+  Erik de Castro Lopo      nospam@fake-domain-name.com
++-----------------------------------------------------------+
+"Python addresses true pseudocode's two major failings: that it
+isn't standardized, and it isn't executable."
+- Grant R. Griffin  in comp.dsp
+
+================================================================================
+
+Subject: Re: Can you help with a problem?
+From: PFJ <paul@fake-domain-name.co.uk>
+To: Erik de Castro Lopo <erikd@fake-domain-name.com>
+Date: Wed, 21 Jul 2004 09:50:55 +0100
+
+Hi Erik,
+
+> > I know, but the source code explicitly has SF_FORMAT_PCM which given the
+> > code afterwards would equate to one of the above, but given that PCM
+> > files can have a varied bitwidth the author probably wanted to cover all
+> > bases.
+>
+> But surely the existing code does something like:
+>
+>     sfinfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM;
+>     sfinfo.pcmbitwidth = 16;
+
+If only!
+
+The actual code is this
+
+int LoadSoundFile(char *filename, sound_p sound)
+{
+    SNDFILE *file;
+    SF_INFO file_info;
+    short *buffer_short = NULL;
+    u_int8_t *buffer_8 = NULL;
+    int16_t *buffer_16 = NULL;
+    unsigned int i;
+
+    /* Open the file and retrieve sample information. */
+    file = sf_open_read(filename, &file_info);
+    // I've sorted this one already - PFJ
+
+    /* Make sure the format is acceptable. */
+    if ((file_info.format & 0x0F) != SF_FORMAT_PCM) {
+		printf("'%s' is not a PCM-based audio file.\n", filename);
+		sf_close(file);
+		return -1;
+    }
+
+    if ((file_info.pcmbitwidth == 8) && (file_info.channels == 1)) {
+		sound->format = AL_FORMAT_MONO8;
+    } else if ((file_info.pcmbitwidth == 8) && (file_info.channels == 2)) {
+		sound->format = AL_FORMAT_STEREO8;
+    } else if ((file_info.pcmbitwidth == 16) && (file_info.channels == 1)) {
+		sound->format = AL_FORMAT_MONO16;
+    } else if ((file_info.pcmbitwidth == 16) && (file_info.channels == 2)) {
+		sound->format = AL_FORMAT_STEREO16;
+    } else {
+		printf("Unknown sample format in %s.\n", filename);
+		sf_close(file);
+		return -1;
+    }
+
+    /* Allocate buffers. */
+    buffer_short = (short *)malloc(file_info.samples * file_info.channels * sizeof (short));
+
+    buffer_8 = (u_int8_t *)malloc(file_info.samples * file_info.channels * file_info.pcmbitwidth / 8);
+
+    buffer_16 = (int16_t *)buffer_8;
+
+    if (buffer_short == NULL || buffer_8 == NULL) {
+		printf("Unable to allocate enough memory for '%s'.\n", filename);
+		goto error_cleanup;
+    }
+
+    /* Read the entire sound file. */
+    if (sf_readf_short(file,buffer_short,file_info.samples) == (size_t)-1) {
+		printf("Error while reading samples from '%s'.\n", filename);
+		goto error_cleanup;
+    }
+
+<minor snip>
+
+    /* Fill in the sound data structure. */
+    sound->freq = file_info.samplerate;
+    sound->size = file_info.samples * file_info.channels * file_info.pcmbitwidth / 8;
+
+    /* Give our sound data to OpenAL. */
+    alGenBuffers(1, &sound->name);
+    if (alGetError() != AL_NO_ERROR) {
+		printf("Error creating an AL buffer name for %s.\n", filename);
+		goto error_cleanup;
+    }
+
+    alBufferData(sound->name, sound->format, buffer_8, sound->size,sound->freq);
+    if (alGetError() != AL_NO_ERROR) {
+		printf("Error sending buffer data to OpenAL for %s.\n", filename);
+		goto error_cleanup;
+    }
+
+    /* Close the file and return success. */
+    sf_close(file);
+    free(buffer_short);
+    free(buffer_8);
+
+    return 0;
+
+ error_cleanup:
+    if (file != NULL) fclose(file);
+    free(buffer_short);
+    free(buffer_8);
+    return -1;
+}
+
+As you can see, the PCM material in the listing will not currently
+compile and for the other sndfile material, it probably won't either.
+
+Any help would be appreciated.
+
+TTFN
+
+Paul
+
+================================================================================
+
+From: Erik de Castro Lopo <erikd@fake-domain-name.com>
+To: PFJ <paul@fake-domain-name.co.uk>
+Subject: Re: Can you help with a problem?
+Date: Wed, 21 Jul 2004 19:36:46 +1000
+
+On Wed, 21 Jul 2004 09:50:55 +0100
+PFJ <paul@fake-domain-name.co.uk> wrote:
+
+> Hi Erik,
+> 
+> > > I know, but the source code explicitly has SF_FORMAT_PCM which given the
+> > > code afterwards would equate to one of the above, but given that PCM
+> > > files can have a varied bitwidth the author probably wanted to cover all
+> > > bases.
+> > 
+> > But surely the existing code does something like:
+> > 
+> >     sfinfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM;
+> >     sfinfo.pcmbitwidth = 16;
+> 
+> If only!
+
+No, really.
+
+Drop this completely:
+
+>     /* Make sure the format is acceptable. */
+>     if ((file_info.format & 0x0F) != SF_FORMAT_PCM) {
+> 	printf("'%s' is not a PCM-based audio file.\n", filename);
+> 	sf_close(file);
+> 	return -1;
+>     }
+
+Replace this block:
+
+>     if ((file_info.pcmbitwidth == 8) && (file_info.channels == 1)) {
+> 	        sound->format = AL_FORMAT_MONO8;
+>     } else if ((file_info.pcmbitwidth == 8) && (file_info.channels == 2)) {
+>           sound->format = AL_FORMAT_STEREO8;
+>     } else if ((file_info.pcmbitwidth == 16) && (file_info.channels == 1)) {
+>           sound->format = AL_FORMAT_MONO16;
+>     } else if ((file_info.pcmbitwidth == 16) && (file_info.channels == 2)) {
+>           sound->format = AL_FORMAT_STEREO16;
+>     } else {
+>           printf("Unknown sample format in %s.\n", filename);
+>           sf_close(file);
+>           return -1;
+>     }
+
+with:
+
+    int pcmbitwidth = 0;
+
+    if (file_info.format & SF_FORMAT_SUBMASK != SF_FORMAT_PCM_16)
+    { 	printf("'%s' is not a PCM-based audio file.\n", filename);
+        sf_close(file);
+        return -1;
+        }
+
+    if (file_info.channels < 1 || file_info.channels > 2)
+    { 	printf("'%s' bad channel count.\n", filename);
+        sf_close(file);
+        return -1;
+        }
+
+    switch (file_info.format & SF_FORMAT_SUBMASK + file_info.channels << 16)
+    {    case (SF_FORMAT_PCM_U8 + 1 << 16):
+             sound->format = AL_FORMAT_MONO8;
+             pcmbitwidth = 8;
+             break;
+         case (SF_FORMAT_PCM_U8 + 2 << 16):
+             sound->format = AL_FORMAT_STEREO8;
+             pcmbitwidth = 8;
+             break;
+         case (SF_FORMAT_PCM_16 + 1 << 16):
+             sound->format = AL_FORMAT_MONO16;
+             pcmbitwidth = 16;
+             break;
+         case (SF_FORMAT_PCM_16 + 2 << 16):
+             sound->format = AL_FORMAT_STEREO16;
+             pcmbitwidth = 16;
+             break;
+         default:
+             printf("Unknown sample format in %s.\n", filename);
+             sf_close(file);
+             return -1;
+         }
+
+>     /* Allocate buffers. */
+>     buffer_short = (short *)malloc(file_info.samples *
+> 				   file_info.channels * 
+> 				   sizeof (short));
+> 
+>     buffer_8 = (u_int8_t *)malloc(file_info.samples *
+> 				  file_info.channels *
+> 				  file_info.pcmbitwidth / 8);
+
+Use pcmbitwidth as calculated above.
+
+>     buffer_16 = (int16_t *)buffer_8;
+> 
+>     if (buffer_short == NULL || buffer_8 == NULL) {
+> 	printf("Unable to allocate enough memory for '%s'.\n", filename);
+> 	goto error_cleanup;
+>     }
+> 
+>     /* Read the entire sound file. */
+>     if (sf_readf_short(file,buffer_short,file_info.samples) == (size_t)- 1) {
+
+Replace "(size_t) - 1" with " < 0".
+
+> As you can see, the PCM material in the listing will not currently
+> compile and for the other sndfile material, it probably won't either.
+
+None of the changes above should have been very difficult to figure
+out.
+
+Erik
+-- 
++-----------------------------------------------------------+
+  Erik de Castro Lopo      nospam@fake-domain-name.com
++-----------------------------------------------------------+
+Microsoft is finally bringing all of its Windows operating system families
+under one roof. It will combine all of the features of CE, stability and
+support of ME and the speed of NT.
+It will be called Windows CEMENT...
+
diff --git a/docs/lists.md b/docs/lists.md
new file mode 100644
index 0000000..46b2547
--- /dev/null
+++ b/docs/lists.md
@@ -0,0 +1,27 @@
+---
+layout: page
+title: libsndfile Mailing Lists
+---
+
+# libsndfile Mailing Lists
+
+**Note**: These mailing lists are not maintained by the libsndfile team anymore.
+Use [GitHub issues and pull requests instead](https://docs.github.com/en/free-pro-team@latest/github/collaborating-with-issues-and-pull-requests).
+
+There are three mailing lists for libsndfile:
+
+ - **libsndfile-announce@mega-nerd.com**  
+  [Subscribe](mailto:libsndfile-announce-request@mega-nerd.com?subject=subscribe)  
+  A list which will announce each new release of libsndfile. No one can
+  post to this list except the author.  
+- **libsndfile-devel@mega-nerd.com**  
+  [Subscribe](mailto:libsndfile-devel-request@mega-nerd.com?subject=subscribe)  
+  A list for discussing bugs, porting issues and feature requests.
+  Posting is restricted to subscribers.  
+- **libsndfile-users@mega-nerd.com**  
+  [Subscribe](mailto:libsndfile-users-request@mega-nerd.com?subject=subscribe)  
+  A list for discussing the use of libsndfile in other programs.
+  Posting is restricted to subscribers.  
+
+The libsndfile-devel and libsndfile-users list will automatically receive a copy
+of all emails to the libsndfile-announce list.
diff --git a/docs/new_file_type_howto.md b/docs/new_file_type_howto.md
new file mode 100644
index 0000000..bb42208
--- /dev/null
+++ b/docs/new_file_type_howto.md
@@ -0,0 +1,134 @@
+---
+layout: page
+---
+
+# How to add new file format
+
+   Original : Wed May 23 19:05:07 EST 2001
+   Update 1 : Fri Jul 11 22:12:38 EST 2003
+
+This document will attempt to explain as fully as possible how to add code to
+libsndfile to allow the reading and writing of new file types. By new file
+type I particularly mean a new header type rather than a new encoding method
+for an existing file type.
+
+This HOWTO will take the form of a step by step guide. It will assume that you
+have all required tools including :
+
+- gcc
+- make (should really be the GNU version)
+- autoconf
+- automake
+- libtool
+
+These should all be available on the GNU ftp site: <ftp://ftp.gnu.org/pub/gnu/>.
+
+To help make these steps clearer let's suppose we are adding support for the
+Whacky file format whose files contain 'W','A','C' and 'K' as the first four
+bytes of the file format. Lets also assume that Whacky files contain PCM encoded
+data.
+
+## Step 1
+
+Create a new .c file in the src/ directory of the libsndfile source tree. The
+file name should be reasonable descriptive so that is is obvious that files of
+the new type are handled by this file. In this particular case the file might
+be named 'whacky.c'.
+
+## Step 2
+
+Add your new source code file to the build process.
+
+Edit the file src/Makefile.am and add the name of your file handler to the
+FILESPECIFIC list of handlers. This list looks something like this:  
+
+    FILESPECIFIC = aiff.c au.c au_g72x.c nist.c paf.c raw.c samplitude.c \
+                    svx.c wav.c wav_float.c wav_gsm610.c wav_ima_adpcm.c \
+                    wav_ms_adpcm.c
+
+Then, run the script named 'reconf' in the libsndfile top level directory,
+which will run autoconf and other associated tools. Finally run "./configure"
+in the top level directory. You may want to use the "--disable-gcc-opt" option
+to disable gcc optimisations and make debugging with gdb/ddd easier.
+
+## Step 3
+
+Add a unique identifier for the new file type.
+
+Edit src/sndfile.h and find the enum containing the SF_FORMAT_XXX identifiers.
+Since you will be adding a major file type you should add your identifier to the
+top part of the list where the values are above 0x10000 in value. The easiest
+way to do this is to find the largest value in the list, add 0x10000 to it and
+make that your new identifier value. The identifier should be something like
+SF_FORMAT_WACK.
+
+## Step 4
+
+Add code to the file type recogniser function.
+
+Edit src/sndfile.c and find the function guess_file_type (). This function
+reads the first 3 ints of the file and from that makes a guess at the file
+type. In our case we would add:
+
+    if (buffer [0] == MAKE_MARKER ('W','A','C','K'))
+        return SF_FORMAT_WACK ;
+
+The use of the MAKE_MARKER macro should be pretty obvious and it is defined at
+the top of file should you need to have a look at it.
+
+## Step 5
+
+Add a call to your open function from psf_open_file ().
+
+Edit src/sndfile.c and find the switch statement in psf_open_file (). It starts
+like this:
+
+    switch (filetype)
+    {   case SF_FORMAT_WAV :
+                error = wav_open (psf) ;
+                break ;
+
+        case SF_FORMAT_AIFF :
+                error = aiff_open (psf) ;
+                break ;
+
+Towards the bottom of this switch statement your should add one for the new file
+type. Something like:
+
+        case    SF_FORMAT_WACK :
+                sf_errno = whacky_open (psf) ;
+                break ;
+
+## Step 6
+
+Add prototypes for new open read and open write functions.
+
+Edit src/common.h, go to the bottom of the file and add something like
+
+    int     whacky_open   (SF_PRIVATE *psf) ;
+
+## Step 7
+
+Implement your open read function. The best way to do this is by coding
+something much like one of the other file formats. The file src/au.c might be a
+good place to start.
+
+In src/whacky.c you should now implement the function whacky_open() which
+was prototyped in src/common.h. This function should return 0 on success and
+a non-zero number on error.
+
+Error values are defined in src/common.h in a enum which starts at SFE_NO_ERROR.
+When adding a new error value, you also need to add an error string to the
+SndfileErrors array in src/sndfile.c.
+
+To parse the header of your new file type you should avoid using standard read/
+write/seek functions (and the fread/fwrite/fseek etc) and instead use
+psf_binheader_readf () which is implemented and documented in src/common.h.
+
+During the parsing process, you should also print logging information to
+libsndfile's internal log buffer using the psf_log_printf() function.
+
+At the end of the open read process, you should have set a number of fields in
+the SF_PRIVATE structure pointed to by psf.
+
+**THIS FILE IS INCOMPLETE**
diff --git a/docs/octave.md b/docs/octave.md
new file mode 100644
index 0000000..f619ca6
--- /dev/null
+++ b/docs/octave.md
@@ -0,0 +1,71 @@
+---
+layout: page
+---
+
+# libsndfile and GNU Octave
+
+[GNU Octave](http://www.octave.org/) is a high-level interactive language for
+numerical computations. There are currently two development streams, a stable
+2.0.X series and a development 2.1.X series. Octave reads and writes data in
+binary formats that were originally developed for
+[MATLAB](http://www.mathworks.com/). Version 2.0.X of Octave uses binary data
+files compatible with MATLAB version 4.2 while Octave 2.1.X uses binary data
+files compatible with MATLAB version 5.0 as well as being able to read the older
+MATLAB 4.2 format.
+
+From version 1.0.1 of libsndfile onwards, libsndfile has the ability of reading
+and writing a small subset of the binary data files used by both versions of GNU
+Octave. This gives people using GNU Octave for audio based work an easy method
+of moving audio data between GNU Octave and other programs which use libsndfile.
+
+For instance it is now possible to do the following:
+
+* Load a WAV file into a sound file editor such as
+  [Sweep](http://www.metadecks.org/software/sweep/).
+* Save it as a MAT4 file.
+* Load the data into Octave for manipulation.
+* Save the modified data.
+* Reload it in Sweep.
+
+Another example would be using the MAT4 or MAT5 file formats as a format which
+can be easily loaded into Octave for viewing/analyzing as well as a format which
+can be played with command line players such as the one included with
+libsndfile.
+
+## Details
+
+Octave, like most programming languages, uses variables to store data, and
+Octave variables can contain both arrays and matrices. It is also able to store
+one or more of these variables in a file. When reading Octave files, libsndfile
+expects a file to contain two variables and their associated data. The first
+variable should contain a variable holding the file sample rate while the second
+variable contains the audio data.
+
+For example, to generate a sine wave and store it as a binary file which is
+compatible with libsndfile, do the following:
+
+    octave:1 > samplerate = 44100 ;
+    octave:2 > wavedata = sin ((0:1023)*2*pi/1024) ;
+    octave:3 > save sine.mat samplerate wavedata
+
+The process of reading and writing files compatible with libsndfile can be made
+easier by use of two Octave script files:
+
+    octave:4 > [data fs] = sndfile_load ("sine.mat") ;
+    octave:5 > sndfile_save ("sine2.mat", data, fs) ;
+
+In addition, libsndfile contains a command line program which which is able to
+play the correct types of Octave files. Using this command line player
+**sndfile-play** and a third Octave script file allows Octave data to be played
+from within Octave on any of the platforms which **sndfile-play** supports (at
+the moment: Linux, MacOS X, Solaris and Win32).
+
+    octave:6 > sndfile_play (data, fs) ;
+
+These three Octave scripts are installed automatically in Octave's site script
+directory when libsndfile is installed (except on Win32) ie when libsndfile is
+being installed into /usr/local, the Octave scripts will be installed in
+/usr/local/share/octave/site/m/.
+
+There are some other Octave scripts for audio to be found
+[here](http://octave.sourceforge.net/audio/index.html).
diff --git a/docs/print.css b/docs/print.css
new file mode 100644
index 0000000..deb5b13
--- /dev/null
+++ b/docs/print.css
@@ -0,0 +1,14 @@
+body {
+	background:white;
+	color:black;
+}
+
+h1{
+	background:white;
+	color:black;
+}
+
+h2 {
+	background:white;
+	color:#666;
+}
diff --git a/docs/sndfile_info.md b/docs/sndfile_info.md
new file mode 100644
index 0000000..78d3548
--- /dev/null
+++ b/docs/sndfile_info.md
@@ -0,0 +1,36 @@
+---
+layout: page
+title: sndfile-info
+---
+
+Here is an example of the output from the **sndfile-info** program
+distributed with libsndfile.
+
+This file was opened and parsed correctly but had been truncated so that
+the values in the **FORM** and **SSND** chunks were incorrect.
+
+```
+        erikd@hendrix > examples/sndfile-info truncated.aiff
+        truncated.aiff
+        size : 200000
+        FORM : 307474 (should be 199992)
+         AIFF
+         COMM : 18
+          Sample Rate : 16000
+          Samples     : 76857
+          Channels    : 2
+          Sample Size : 16
+         SSND : 307436 (should be 199946)
+          Offset     : 0
+          Block Size : 0
+
+        --------------------------------
+        Sample Rate : 16000
+        Frames      : 76857
+        Channels    : 2
+        Bit Width   : 16
+        Format      : 0x00020001
+        Sections    : 1
+        Seekable    : TRUE
+        Signal Max  : 32766
+```
diff --git a/docs/tutorial.md b/docs/tutorial.md
new file mode 100644
index 0000000..848c175
--- /dev/null
+++ b/docs/tutorial.md
@@ -0,0 +1,10 @@
+---
+layout: page
+---
+
+# libsndfile Tutorial
+
+**More coming soon.**
+
+For now, the best place to look for example code is the `examples/` directory of the source code distribution and the
+libsndfile test suite which is located in the `tests/` directory of the source code distribution.
diff --git a/docs/win32.md b/docs/win32.md
new file mode 100644
index 0000000..6106517
--- /dev/null
+++ b/docs/win32.md
@@ -0,0 +1,18 @@
+---
+layout: page
+---
+
+# Building libsndfile on Win32
+
+**Note : For pre-compiled binaries for windows, both for win32 and win64, see
+the main web page.**
+
+There are currently two build systems; the official GNU autotool based one and
+a more limited and experimental CMake based build system.
+
+libsndfile is written to be compiled by a compiler which supports large chunks
+of the 1999 ISO C Standard (tested with GCC, Clang and Visual Studio 2015).
+
+It is recommended to use CMake and Visual Studio to build libsndfile on Windows
+but you can try the [MinGW](http://www.mingw.org/) compiler suite with Autotools
+or CMake buildsystem.
diff --git a/examples/generate.c b/examples/generate.c
new file mode 100644
index 0000000..9b59f4a
--- /dev/null
+++ b/examples/generate.c
@@ -0,0 +1,131 @@
+/*
+** Copyright (C) 2002-2011 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in
+**       the documentation and/or other materials provided with the
+**       distribution.
+**     * Neither the author nor the names of any contributors may be used
+**       to endorse or promote products derived from this software without
+**       specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include <sndfile.h>
+
+#define	BUFFER_LEN			4096
+
+static void encode_file (const char *infilename, const char *outfilename, int filetype) ;
+
+int
+main (int argc, char **argv)
+{
+	if (argc != 2)
+	{	puts ("\nEncode a single input file into a number of different output ") ;
+		puts ("encodings. These output encodings can then be moved to another ") ;
+		puts ("OS for testing.\n") ;
+		puts ("    Usage : generate <filename>\n") ;
+		exit (1) ;
+		} ;
+
+	/* A couple of standard WAV files. Make sure Win32 plays these. */
+	encode_file (argv [1], "pcmu8.wav"	, SF_FORMAT_WAV | SF_FORMAT_PCM_U8) ;
+	encode_file (argv [1], "pcm16.wav"	, SF_FORMAT_WAV | SF_FORMAT_PCM_16) ;
+	encode_file (argv [1], "imaadpcm.wav", SF_FORMAT_WAV | SF_FORMAT_MS_ADPCM) ;
+	encode_file (argv [1], "msadpcm.wav", SF_FORMAT_WAV | SF_FORMAT_IMA_ADPCM) ;
+	encode_file (argv [1], "gsm610.wav"	, SF_FORMAT_WAV | SF_FORMAT_GSM610) ;
+
+	/* Soundforge W64. */
+	encode_file (argv [1], "pcmu8.w64"	, SF_FORMAT_W64 | SF_FORMAT_PCM_U8) ;
+	encode_file (argv [1], "pcm16.w64"	, SF_FORMAT_W64 | SF_FORMAT_PCM_16) ;
+	encode_file (argv [1], "imaadpcm.w64", SF_FORMAT_W64 | SF_FORMAT_MS_ADPCM) ;
+	encode_file (argv [1], "msadpcm.w64", SF_FORMAT_W64 | SF_FORMAT_IMA_ADPCM) ;
+	encode_file (argv [1], "gsm610.w64"	, SF_FORMAT_W64 | SF_FORMAT_GSM610) ;
+
+	return 0 ;
+} /* main */
+
+/*============================================================================================
+**	Helper functions and macros.
+*/
+
+#define PUT_DOTS(k)					\
+			{	while (k--)			\
+					putchar ('.') ;	\
+				putchar (' ') ;		\
+				}
+
+/*========================================================================================
+*/
+
+static void
+encode_file (const char *infilename, const char *outfilename, int filetype)
+{	static float buffer [BUFFER_LEN] ;
+
+	SNDFILE		*infile, *outfile ;
+	SF_INFO		sfinfo ;
+	int			k, readcount ;
+
+	printf ("    %s -> %s ", infilename, outfilename) ;
+	fflush (stdout) ;
+
+	k = 16 - strlen (outfilename) ;
+	PUT_DOTS (k) ;
+
+	memset (&sfinfo, 0, sizeof (sfinfo)) ;
+
+	if (! (infile = sf_open (infilename, SFM_READ, &sfinfo)))
+	{	printf ("Error : could not open file : %s\n", infilename) ;
+		puts (sf_strerror (NULL)) ;
+		exit (1) ;
+		}
+
+	sfinfo.format = filetype ;
+
+	if (! sf_format_check (&sfinfo))
+	{	sf_close (infile) ;
+		printf ("Invalid encoding\n") ;
+		return ;
+		} ;
+
+	if (! (outfile = sf_open (outfilename, SFM_WRITE, &sfinfo)))
+	{	printf ("Error : could not open file : %s\n", outfilename) ;
+		puts (sf_strerror (NULL)) ;
+		exit (1) ;
+		} ;
+
+	while ((readcount = (int) sf_read_float (infile, buffer, BUFFER_LEN)) > 0)
+		sf_write_float (outfile, buffer, readcount) ;
+
+	sf_close (infile) ;
+	sf_close (outfile) ;
+
+	printf ("ok\n") ;
+
+	return ;
+} /* encode_file */
+
diff --git a/examples/list_formats.c b/examples/list_formats.c
new file mode 100644
index 0000000..348c81b
--- /dev/null
+++ b/examples/list_formats.c
@@ -0,0 +1,76 @@
+/*
+** Copyright (C) 2001-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in
+**       the documentation and/or other materials provided with the
+**       distribution.
+**     * Neither the author nor the names of any contributors may be used
+**       to endorse or promote products derived from this software without
+**       specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include	<stdio.h>
+#include	<stdlib.h>
+#include	<string.h>
+#include	<math.h>
+
+#include	<sndfile.h>
+
+int
+main (void)
+{	SF_FORMAT_INFO	info ;
+	SF_INFO 		sfinfo ;
+	int format, major_count, subtype_count, m, s ;
+
+	memset (&sfinfo, 0, sizeof (sfinfo)) ;
+	printf ("Version : %s\n\n", sf_version_string ()) ;
+
+	sf_command (NULL, SFC_GET_FORMAT_MAJOR_COUNT, &major_count, sizeof (int)) ;
+	sf_command (NULL, SFC_GET_FORMAT_SUBTYPE_COUNT, &subtype_count, sizeof (int)) ;
+
+	sfinfo.channels = 1 ;
+	for (m = 0 ; m < major_count ; m++)
+	{	info.format = m ;
+		sf_command (NULL, SFC_GET_FORMAT_MAJOR, &info, sizeof (info)) ;
+		printf ("%s  (extension \"%s\")\n", info.name, info.extension) ;
+
+		format = info.format ;
+
+		for (s = 0 ; s < subtype_count ; s++)
+		{	info.format = s ;
+			sf_command (NULL, SFC_GET_FORMAT_SUBTYPE, &info, sizeof (info)) ;
+
+			format = (format & SF_FORMAT_TYPEMASK) | info.format ;
+
+			sfinfo.format = format ;
+			if (sf_format_check (&sfinfo))
+				printf ("   %s\n", info.name) ;
+			} ;
+		puts ("") ;
+		} ;
+	puts ("") ;
+
+	return 0 ;
+} /* main */
+
diff --git a/examples/make_sine.c b/examples/make_sine.c
new file mode 100644
index 0000000..b905b2a
--- /dev/null
+++ b/examples/make_sine.c
@@ -0,0 +1,100 @@
+/*
+** Copyright (C) 1999-2012 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in
+**       the documentation and/or other materials provided with the
+**       distribution.
+**     * Neither the author nor the names of any contributors may be used
+**       to endorse or promote products derived from this software without
+**       specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include	<stdio.h>
+#include	<stdlib.h>
+#include	<string.h>
+#include	<math.h>
+
+#include	<sndfile.h>
+
+#ifndef		M_PI
+#define		M_PI		3.14159265358979323846264338
+#endif
+
+#define		SAMPLE_RATE			44100
+#define		SAMPLE_COUNT		(SAMPLE_RATE * 4)	/* 4 seconds */
+#define		AMPLITUDE			(1.0 * 0x7F000000)
+#define		LEFT_FREQ			(344.0 / SAMPLE_RATE)
+#define		RIGHT_FREQ			(466.0 / SAMPLE_RATE)
+
+int
+main (void)
+{	SNDFILE	*file ;
+	SF_INFO	sfinfo ;
+	int		k ;
+	int	*buffer ;
+
+	if (! (buffer = malloc (2 * SAMPLE_COUNT * sizeof (int))))
+	{	printf ("Error : Malloc failed.\n") ;
+		return 1 ;
+		} ;
+
+	memset (&sfinfo, 0, sizeof (sfinfo)) ;
+
+	sfinfo.samplerate	= SAMPLE_RATE ;
+	sfinfo.frames		= SAMPLE_COUNT ;
+	sfinfo.channels		= 2 ;
+	sfinfo.format		= (SF_FORMAT_WAV | SF_FORMAT_PCM_24) ;
+
+	if (! (file = sf_open ("sine.wav", SFM_WRITE, &sfinfo)))
+	{	printf ("Error : Not able to open output file.\n") ;
+		free (buffer) ;
+		return 1 ;
+		} ;
+
+	if (sfinfo.channels == 1)
+	{	for (k = 0 ; k < SAMPLE_COUNT ; k++)
+			buffer [k] = (int) (AMPLITUDE * sin (LEFT_FREQ * 2 * k * M_PI)) ;
+		}
+	else if (sfinfo.channels == 2)
+	{	for (k = 0 ; k < SAMPLE_COUNT ; k++)
+		{	buffer [2 * k] = (int) (AMPLITUDE * sin (LEFT_FREQ * 2 * k * M_PI)) ;
+			buffer [2 * k + 1] = (int) (AMPLITUDE * sin (RIGHT_FREQ * 2 * k * M_PI)) ;
+			} ;
+		}
+	else
+	{	printf ("Error : make_sine can only generate mono or stereo files.\n") ;
+		sf_close (file) ;
+		free (buffer) ;
+		return 1 ;
+		} ;
+
+	if (sf_write_int (file, buffer, sfinfo.channels * SAMPLE_COUNT) !=
+											sfinfo.channels * SAMPLE_COUNT)
+		puts (sf_strerror (file)) ;
+
+	sf_close (file) ;
+	free (buffer) ;
+	return 0 ;
+} /* main */
+
diff --git a/examples/sfprocess.c b/examples/sfprocess.c
new file mode 100644
index 0000000..ce1bda8
--- /dev/null
+++ b/examples/sfprocess.c
@@ -0,0 +1,144 @@
+/*
+** Copyright (C) 2001-2013 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**	 * Redistributions of source code must retain the above copyright
+**	   notice, this list of conditions and the following disclaimer.
+**	 * Redistributions in binary form must reproduce the above copyright
+**	   notice, this list of conditions and the following disclaimer in
+**	   the documentation and/or other materials provided with the
+**	   distribution.
+**	 * Neither the author nor the names of any contributors may be used
+**	   to endorse or promote products derived from this software without
+**	   specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include	<stdio.h>
+#include	<string.h>
+
+/* Include this header file to use functions from libsndfile. */
+#include	<sndfile.h>
+
+/*	This will be the length of the buffer used to hold.frames while
+**	we process them.
+*/
+#define		BUFFER_LEN	1024
+
+/* libsndfile can handle more than 6 channels but we'll restrict it to 6. */
+#define		MAX_CHANNELS	6
+
+/* Function prototype. */
+static void process_data (double *data, int count, int channels) ;
+
+
+int
+main (void)
+{	/* This is a buffer of double precision floating point values
+	** which will hold our data while we process it.
+	*/
+	static double data [BUFFER_LEN] ;
+
+	/* A SNDFILE is very much like a FILE in the Standard C library. The
+	** sf_open function return an SNDFILE* pointer when they sucessfully
+	** open the specified file.
+	*/
+	SNDFILE	*infile, *outfile ;
+
+	/* A pointer to an SF_INFO struct is passed to sf_open.
+	** On read, the library fills this struct with information about the file.
+	** On write, the struct must be filled in before calling sf_open.
+	*/
+	SF_INFO		sfinfo ;
+	int			readcount ;
+	const char	*infilename = "input.wav" ;
+	const char	*outfilename = "output.wav" ;
+
+	/* The SF_INFO struct must be initialized before using it.
+	*/
+	memset (&sfinfo, 0, sizeof (sfinfo)) ;
+
+	/* Here's where we open the input file. We pass sf_open the file name and
+	** a pointer to an SF_INFO struct.
+	** On successful open, sf_open returns a SNDFILE* pointer which is used
+	** for all subsequent operations on that file.
+	** If an error occurs during sf_open, the function returns a NULL pointer.
+	**
+	** If you are trying to open a raw headerless file you will need to set the
+	** format and channels fields of sfinfo before calling sf_open(). For
+	** instance to open a raw 16 bit stereo PCM file you would need the following
+	** two lines:
+	**
+	**		sfinfo.format   = SF_FORMAT_RAW | SF_FORMAT_PCM_16 ;
+	**		sfinfo.channels = 2 ;
+	*/
+	if (! (infile = sf_open (infilename, SFM_READ, &sfinfo)))
+	{	/* Open failed so print an error message. */
+		printf ("Not able to open input file %s.\n", infilename) ;
+		/* Print the error message from libsndfile. */
+		puts (sf_strerror (NULL)) ;
+		return 1 ;
+		} ;
+
+	if (sfinfo.channels > MAX_CHANNELS)
+	{	printf ("Not able to process more than %d channels\n", MAX_CHANNELS) ;
+		sf_close (infile) ;
+		return 1 ;
+		} ;
+	/* Open the output file. */
+	if (! (outfile = sf_open (outfilename, SFM_WRITE, &sfinfo)))
+	{	printf ("Not able to open output file %s.\n", outfilename) ;
+		puts (sf_strerror (NULL)) ;
+		sf_close (infile) ;
+		return 1 ;
+		} ;
+
+	/* While there are.frames in the input file, read them, process
+	** them and write them to the output file.
+	*/
+	while ((readcount = (int) sf_read_double (infile, data, BUFFER_LEN)))
+	{	process_data (data, readcount, sfinfo.channels) ;
+		sf_write_double (outfile, data, readcount) ;
+		} ;
+
+	/* Close input and output files. */
+	sf_close (infile) ;
+	sf_close (outfile) ;
+
+	return 0 ;
+} /* main */
+
+static void
+process_data (double *data, int count, int channels)
+{	double channel_gain [MAX_CHANNELS] = { 0.5, 0.8, 0.1, 0.4, 0.4, 0.9 } ;
+	int k, chan ;
+
+	/* Process the data here.
+	** If the soundfile contains more then 1 channel you need to take care of
+	** the data interleaving youself.
+	** Current we just apply a channel dependant gain.
+	*/
+
+	for (chan = 0 ; chan < channels ; chan ++)
+		for (k = chan ; k < count ; k+= channels)
+			data [k] *= channel_gain [chan] ;
+
+	return ;
+} /* process_data */
+
diff --git a/examples/sndfile-loopify.c b/examples/sndfile-loopify.c
new file mode 100644
index 0000000..12b6b6b
--- /dev/null
+++ b/examples/sndfile-loopify.c
@@ -0,0 +1,175 @@
+/*
+** Copyright (C) 1999-2015 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in
+**       the documentation and/or other materials provided with the
+**       distribution.
+**     * Neither the author nor the names of any contributors may be used
+**       to endorse or promote products derived from this software without
+**       specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*
+** A quick/rough hack to add SF_INSTRUMENT data to a file. It compiles, but
+** no guarantees beyond that. Happy to receive patches to fix/improve it.
+**
+** Code for this was stolen from programs/sndfile-convert.c and related code.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <sndfile.h>
+
+#define BUFFER_LEN		(1 << 14)
+
+const char * program_name (const char * argv0) ;
+static void sfe_copy_data_int (SNDFILE *outfile, SNDFILE *infile, int channels) ;
+static void add_instrument_data (SNDFILE *outfile, const SF_INFO * in_info) ;
+
+static void
+usage_exit (const char *progname)
+{
+	printf ("\nUsage : %s <input file> <output file>\n", progname) ;
+	puts ("") ;
+	exit (1) ;
+} /* usage_exit */
+
+int
+main (int argc, char * argv [])
+{	const char	*progname, *infilename, *outfilename ;
+	SNDFILE		*infile = NULL, *outfile = NULL ;
+	SF_INFO		in_sfinfo, out_sfinfo ;
+
+	progname = program_name (argv [0]) ;
+
+	if (argc < 3 || argc > 5)
+		usage_exit (progname) ;
+
+	infilename = argv [argc-2] ;
+	outfilename = argv [argc-1] ;
+
+	if (strcmp (infilename, outfilename) == 0)
+	{	printf ("Error : Input and output filenames are the same.\n\n") ;
+		usage_exit (progname) ;
+		} ;
+
+	if (strlen (infilename) > 1 && infilename [0] == '-')
+	{	printf ("Error : Input filename (%s) looks like an option.\n\n", infilename) ;
+		usage_exit (progname) ;
+		} ;
+
+	if (outfilename [0] == '-')
+	{	printf ("Error : Output filename (%s) looks like an option.\n\n", outfilename) ;
+		usage_exit (progname) ;
+		} ;
+
+	memset (&in_sfinfo, 0, sizeof (in_sfinfo)) ;
+
+	if ((infile = sf_open (infilename, SFM_READ, &in_sfinfo)) == NULL)
+	{	printf ("Not able to open input file %s.\n", infilename) ;
+		puts (sf_strerror (NULL)) ;
+		return 1 ;
+		} ;
+
+	memcpy (&out_sfinfo, &in_sfinfo, sizeof (out_sfinfo)) ;
+	/* Open the output file. */
+	if ((outfile = sf_open (outfilename, SFM_WRITE, &out_sfinfo)) == NULL)
+	{	printf ("Not able to open output file %s : %s\n", outfilename, sf_strerror (NULL)) ;
+		sf_close (infile) ;
+		return 1 ;
+		} ;
+
+	/* Add the loop data */
+	add_instrument_data (outfile, &in_sfinfo) ;
+
+	/* Copy the audio data */
+	sfe_copy_data_int (outfile, infile, in_sfinfo.channels) ;
+
+	sf_close (infile) ;
+	sf_close (outfile) ;
+
+	return 0 ;
+} /* main */
+
+const char *
+program_name (const char * argv0)
+{	const char * tmp ;
+
+	tmp = strrchr (argv0, '/') ;
+	argv0 = tmp ? tmp + 1 : argv0 ;
+
+	/* Remove leading libtool name mangling. */
+	if (strstr (argv0, "lt-") == argv0)
+		return argv0 + 3 ;
+
+	return argv0 ;
+} /* program_name */
+
+static void
+sfe_copy_data_int (SNDFILE *outfile, SNDFILE *infile, int channels)
+{	static int	data [BUFFER_LEN] ;
+	int		frames, readcount ;
+
+	frames = BUFFER_LEN / channels ;
+	readcount = frames ;
+
+	while (readcount > 0)
+	{	readcount = (int) sf_readf_int (infile, data, frames) ;
+		sf_writef_int (outfile, data, readcount) ;
+		} ;
+
+	return ;
+} /* sfe_copy_data_int */
+
+static void
+add_instrument_data (SNDFILE *file, const SF_INFO *info)
+{	SF_INSTRUMENT instr ;
+
+	memset (&instr, 0, sizeof (instr)) ;
+
+	instr.gain = 1 ;
+	instr.basenote = 0 ;
+	instr.detune = 0 ;
+	instr.velocity_lo = 0 ;
+	instr.velocity_hi = 0 ;
+	instr.key_lo = 0 ;
+	instr.key_hi = 0 ;
+	instr.loop_count = 1 ;
+
+	instr.loops [0].mode = SF_LOOP_FORWARD ;
+	instr.loops [0].start = 0 ;
+	instr.loops [0].end = info->frames ;
+	instr.loops [0].count = 0 ;
+
+	if (sf_command (file, SFC_SET_INSTRUMENT, &instr, sizeof (instr)) == SF_FALSE)
+	{	printf ("\n\nLine %d : sf_command (SFC_SET_INSTRUMENT) failed.\n\n", __LINE__) ;
+		exit (1) ;
+		} ;
+
+	return ;
+} /* add_instrument_data */
+
diff --git a/examples/sndfile-to-text.c b/examples/sndfile-to-text.c
new file mode 100644
index 0000000..58ee069
--- /dev/null
+++ b/examples/sndfile-to-text.c
@@ -0,0 +1,168 @@
+/*
+** Copyright (C) 2008-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in
+**       the documentation and/or other materials provided with the
+**       distribution.
+**     * Neither the author nor the names of any contributors may be used
+**       to endorse or promote products derived from this software without
+**       specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <float.h>
+
+#include <sndfile.h>
+
+#define	BLOCK_SIZE 4096
+
+#ifdef DBL_DECIMAL_DIG
+	#define OP_DBL_Digs (DBL_DECIMAL_DIG)
+#else
+	#ifdef DECIMAL_DIG
+		#define OP_DBL_Digs (DECIMAL_DIG)
+	#else
+		#define OP_DBL_Digs (DBL_DIG + 3)
+	#endif
+#endif
+
+static void
+print_usage (char *progname)
+{	printf ("\nUsage : %s [--full-precision] <input file> <output file>\n", progname) ;
+	puts ("\n"
+		"    Where the output file will contain a line for each frame\n"
+		"    and a column for each channel.\n"
+		) ;
+
+} /* print_usage */
+
+static int
+convert_to_text (SNDFILE * infile, FILE * outfile, int channels, int full_precision)
+{	float *buf ;
+	sf_count_t frames ;
+	int k, m, readcount ;
+
+	buf = malloc (BLOCK_SIZE * sizeof (float)) ;
+	if (buf == NULL)
+	{	printf ("Error : Out of memory.\n\n") ;
+		return 1 ;
+		} ;
+
+	frames = BLOCK_SIZE / channels ;
+
+	while ((readcount = (int) sf_readf_float (infile, buf, frames)) > 0)
+	{	for (k = 0 ; k < readcount ; k++)
+		{	for (m = 0 ; m < channels ; m++)
+				if (full_precision)
+					fprintf (outfile, " %.*e", OP_DBL_Digs - 1, buf [k * channels + m]) ;
+				else
+					fprintf (outfile, " % 12.10f", buf [k * channels + m]) ;
+			fprintf (outfile, "\n") ;
+			} ;
+		} ;
+
+	free (buf) ;
+
+	return 0 ;
+} /* convert_to_text */
+
+int
+main (int argc, char * argv [])
+{	char 		*progname, *infilename, *outfilename ;
+	SNDFILE		*infile = NULL ;
+	FILE		*outfile = NULL ;
+	SF_INFO		sfinfo ;
+	int		full_precision = 0 ;
+	int 	ret = 1 ;
+
+	progname = strrchr (argv [0], '/') ;
+	progname = progname ? progname + 1 : argv [0] ;
+
+	switch (argc)
+	{	case 4 :
+			if (!strcmp ("--full-precision", argv [3]))
+			{	print_usage (progname) ;
+				goto cleanup ;
+				} ;
+			full_precision = 1 ;
+			argv++ ;
+		case 3 :
+			break ;
+		default:
+			print_usage (progname) ;
+			goto cleanup ;
+		} ;
+
+	infilename = argv [1] ;
+	outfilename = argv [2] ;
+
+	if (strcmp (infilename, outfilename) == 0)
+	{	printf ("Error : Input and output filenames are the same.\n\n") ;
+		print_usage (progname) ;
+		goto cleanup ;
+		} ;
+
+	if (infilename [0] == '-')
+	{	printf ("Error : Input filename (%s) looks like an option.\n\n", infilename) ;
+		print_usage (progname) ;
+		goto cleanup ;
+		} ;
+
+	if (outfilename [0] == '-')
+	{	printf ("Error : Output filename (%s) looks like an option.\n\n", outfilename) ;
+		print_usage (progname) ;
+		goto cleanup ;
+		} ;
+
+	memset (&sfinfo, 0, sizeof (sfinfo)) ;
+
+	if ((infile = sf_open (infilename, SFM_READ, &sfinfo)) == NULL)
+	{	printf ("Not able to open input file %s.\n", infilename) ;
+		puts (sf_strerror (NULL)) ;
+		goto cleanup ;
+		} ;
+
+	/* Open the output file. */
+	if ((outfile = fopen (outfilename, "w")) == NULL)
+	{	printf ("Not able to open output file %s : %s\n", outfilename, sf_strerror (NULL)) ;
+		goto cleanup ;
+		} ;
+
+	fprintf (outfile, "# Converted from file %s.\n", infilename) ;
+	fprintf (outfile, "# Channels %d, Sample rate %d\n", sfinfo.channels, sfinfo.samplerate) ;
+
+	ret = convert_to_text (infile, outfile, sfinfo.channels, full_precision) ;
+
+cleanup :
+
+	sf_close (infile) ;
+	if (outfile != NULL)
+		fclose (outfile) ;
+
+	return ret ;
+} /* main */
+
diff --git a/include/sndfile.h b/include/sndfile.h
new file mode 100644
index 0000000..ed99208
--- /dev/null
+++ b/include/sndfile.h
@@ -0,0 +1,885 @@
+/*
+** Copyright (C) 1999-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*
+** sndfile.h -- system-wide definitions
+**
+** API documentation is in the doc/ directory of the source code tarball
+** and at http://libsndfile.github.io/libsndfile/api.html.
+*/
+
+#ifndef SNDFILE_H
+#define SNDFILE_H
+
+/* This is the version 1.0.X header file. */
+#define	SNDFILE_1
+
+#include <stdio.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif	/* __cplusplus */
+
+/* The following file types can be read and written.
+** A file type would consist of a major type (ie SF_FORMAT_WAV) bitwise
+** ORed with a minor type (ie SF_FORMAT_PCM). SF_FORMAT_TYPEMASK and
+** SF_FORMAT_SUBMASK can be used to separate the major and minor file
+** types.
+*/
+
+enum
+{	/* Major formats. */
+	SF_FORMAT_WAV			= 0x010000,		/* Microsoft WAV format (little endian default). */
+	SF_FORMAT_AIFF			= 0x020000,		/* Apple/SGI AIFF format (big endian). */
+	SF_FORMAT_AU			= 0x030000,		/* Sun/NeXT AU format (big endian). */
+	SF_FORMAT_RAW			= 0x040000,		/* RAW PCM data. */
+	SF_FORMAT_PAF			= 0x050000,		/* Ensoniq PARIS file format. */
+	SF_FORMAT_SVX			= 0x060000,		/* Amiga IFF / SVX8 / SV16 format. */
+	SF_FORMAT_NIST			= 0x070000,		/* Sphere NIST format. */
+	SF_FORMAT_VOC			= 0x080000,		/* VOC files. */
+	SF_FORMAT_IRCAM			= 0x0A0000,		/* Berkeley/IRCAM/CARL */
+	SF_FORMAT_W64			= 0x0B0000,		/* Sonic Foundry's 64 bit RIFF/WAV */
+	SF_FORMAT_MAT4			= 0x0C0000,		/* Matlab (tm) V4.2 / GNU Octave 2.0 */
+	SF_FORMAT_MAT5			= 0x0D0000,		/* Matlab (tm) V5.0 / GNU Octave 2.1 */
+	SF_FORMAT_PVF			= 0x0E0000,		/* Portable Voice Format */
+	SF_FORMAT_XI			= 0x0F0000,		/* Fasttracker 2 Extended Instrument */
+	SF_FORMAT_HTK			= 0x100000,		/* HMM Tool Kit format */
+	SF_FORMAT_SDS			= 0x110000,		/* Midi Sample Dump Standard */
+	SF_FORMAT_AVR			= 0x120000,		/* Audio Visual Research */
+	SF_FORMAT_WAVEX			= 0x130000,		/* MS WAVE with WAVEFORMATEX */
+	SF_FORMAT_SD2			= 0x160000,		/* Sound Designer 2 */
+	SF_FORMAT_FLAC			= 0x170000,		/* FLAC lossless file format */
+	SF_FORMAT_CAF			= 0x180000,		/* Core Audio File format */
+	SF_FORMAT_WVE			= 0x190000,		/* Psion WVE format */
+	SF_FORMAT_OGG			= 0x200000,		/* Xiph OGG container */
+	SF_FORMAT_MPC2K			= 0x210000,		/* Akai MPC 2000 sampler */
+	SF_FORMAT_RF64			= 0x220000,		/* RF64 WAV file */
+	SF_FORMAT_MPEG			= 0x230000,		/* MPEG-1/2 audio stream */
+
+	/* Subtypes from here on. */
+
+	SF_FORMAT_PCM_S8		= 0x0001,		/* Signed 8 bit data */
+	SF_FORMAT_PCM_16		= 0x0002,		/* Signed 16 bit data */
+	SF_FORMAT_PCM_24		= 0x0003,		/* Signed 24 bit data */
+	SF_FORMAT_PCM_32		= 0x0004,		/* Signed 32 bit data */
+
+	SF_FORMAT_PCM_U8		= 0x0005,		/* Unsigned 8 bit data (WAV and RAW only) */
+
+	SF_FORMAT_FLOAT			= 0x0006,		/* 32 bit float data */
+	SF_FORMAT_DOUBLE		= 0x0007,		/* 64 bit float data */
+
+	SF_FORMAT_ULAW			= 0x0010,		/* U-Law encoded. */
+	SF_FORMAT_ALAW			= 0x0011,		/* A-Law encoded. */
+	SF_FORMAT_IMA_ADPCM		= 0x0012,		/* IMA ADPCM. */
+	SF_FORMAT_MS_ADPCM		= 0x0013,		/* Microsoft ADPCM. */
+
+	SF_FORMAT_GSM610		= 0x0020,		/* GSM 6.10 encoding. */
+	SF_FORMAT_VOX_ADPCM		= 0x0021,		/* OKI / Dialogix ADPCM */
+
+	SF_FORMAT_NMS_ADPCM_16	= 0x0022,		/* 16kbs NMS G721-variant encoding. */
+	SF_FORMAT_NMS_ADPCM_24	= 0x0023,		/* 24kbs NMS G721-variant encoding. */
+	SF_FORMAT_NMS_ADPCM_32	= 0x0024,		/* 32kbs NMS G721-variant encoding. */
+
+	SF_FORMAT_G721_32		= 0x0030,		/* 32kbs G721 ADPCM encoding. */
+	SF_FORMAT_G723_24		= 0x0031,		/* 24kbs G723 ADPCM encoding. */
+	SF_FORMAT_G723_40		= 0x0032,		/* 40kbs G723 ADPCM encoding. */
+
+	SF_FORMAT_DWVW_12		= 0x0040, 		/* 12 bit Delta Width Variable Word encoding. */
+	SF_FORMAT_DWVW_16		= 0x0041, 		/* 16 bit Delta Width Variable Word encoding. */
+	SF_FORMAT_DWVW_24		= 0x0042, 		/* 24 bit Delta Width Variable Word encoding. */
+	SF_FORMAT_DWVW_N		= 0x0043, 		/* N bit Delta Width Variable Word encoding. */
+
+	SF_FORMAT_DPCM_8		= 0x0050,		/* 8 bit differential PCM (XI only) */
+	SF_FORMAT_DPCM_16		= 0x0051,		/* 16 bit differential PCM (XI only) */
+
+	SF_FORMAT_VORBIS		= 0x0060,		/* Xiph Vorbis encoding. */
+	SF_FORMAT_OPUS			= 0x0064,		/* Xiph/Skype Opus encoding. */
+
+	SF_FORMAT_ALAC_16		= 0x0070,		/* Apple Lossless Audio Codec (16 bit). */
+	SF_FORMAT_ALAC_20		= 0x0071,		/* Apple Lossless Audio Codec (20 bit). */
+	SF_FORMAT_ALAC_24		= 0x0072,		/* Apple Lossless Audio Codec (24 bit). */
+	SF_FORMAT_ALAC_32		= 0x0073,		/* Apple Lossless Audio Codec (32 bit). */
+
+	SF_FORMAT_MPEG_LAYER_I	= 0x0080,		/* MPEG-1 Audio Layer I */
+	SF_FORMAT_MPEG_LAYER_II	= 0x0081,		/* MPEG-1 Audio Layer II */
+	SF_FORMAT_MPEG_LAYER_III = 0x0082,		/* MPEG-2 Audio Layer III */
+
+	/* Endian-ness options. */
+
+	SF_ENDIAN_FILE			= 0x00000000,	/* Default file endian-ness. */
+	SF_ENDIAN_LITTLE		= 0x10000000,	/* Force little endian-ness. */
+	SF_ENDIAN_BIG			= 0x20000000,	/* Force big endian-ness. */
+	SF_ENDIAN_CPU			= 0x30000000,	/* Force CPU endian-ness. */
+
+	SF_FORMAT_SUBMASK		= 0x0000FFFF,
+	SF_FORMAT_TYPEMASK		= 0x0FFF0000,
+	SF_FORMAT_ENDMASK		= 0x30000000
+} ;
+
+/*
+** The following are the valid command numbers for the sf_command()
+** interface.  The use of these commands is documented in the file
+** command.html in the doc directory of the source code distribution.
+*/
+
+enum
+{	SFC_GET_LIB_VERSION				= 0x1000,
+	SFC_GET_LOG_INFO				= 0x1001,
+	SFC_GET_CURRENT_SF_INFO			= 0x1002,
+
+
+	SFC_GET_NORM_DOUBLE				= 0x1010,
+	SFC_GET_NORM_FLOAT				= 0x1011,
+	SFC_SET_NORM_DOUBLE				= 0x1012,
+	SFC_SET_NORM_FLOAT				= 0x1013,
+	SFC_SET_SCALE_FLOAT_INT_READ	= 0x1014,
+	SFC_SET_SCALE_INT_FLOAT_WRITE	= 0x1015,
+
+	SFC_GET_SIMPLE_FORMAT_COUNT		= 0x1020,
+	SFC_GET_SIMPLE_FORMAT			= 0x1021,
+
+	SFC_GET_FORMAT_INFO				= 0x1028,
+
+	SFC_GET_FORMAT_MAJOR_COUNT		= 0x1030,
+	SFC_GET_FORMAT_MAJOR			= 0x1031,
+	SFC_GET_FORMAT_SUBTYPE_COUNT	= 0x1032,
+	SFC_GET_FORMAT_SUBTYPE			= 0x1033,
+
+	SFC_CALC_SIGNAL_MAX				= 0x1040,
+	SFC_CALC_NORM_SIGNAL_MAX		= 0x1041,
+	SFC_CALC_MAX_ALL_CHANNELS		= 0x1042,
+	SFC_CALC_NORM_MAX_ALL_CHANNELS	= 0x1043,
+	SFC_GET_SIGNAL_MAX				= 0x1044,
+	SFC_GET_MAX_ALL_CHANNELS		= 0x1045,
+
+	SFC_SET_ADD_PEAK_CHUNK			= 0x1050,
+
+	SFC_UPDATE_HEADER_NOW			= 0x1060,
+	SFC_SET_UPDATE_HEADER_AUTO		= 0x1061,
+
+	SFC_FILE_TRUNCATE				= 0x1080,
+
+	SFC_SET_RAW_START_OFFSET		= 0x1090,
+
+	/* Commands reserved for dithering, which is not implemented. */
+	SFC_SET_DITHER_ON_WRITE			= 0x10A0,
+	SFC_SET_DITHER_ON_READ			= 0x10A1,
+
+	SFC_GET_DITHER_INFO_COUNT		= 0x10A2,
+	SFC_GET_DITHER_INFO				= 0x10A3,
+
+	SFC_GET_EMBED_FILE_INFO			= 0x10B0,
+
+	SFC_SET_CLIPPING				= 0x10C0,
+	SFC_GET_CLIPPING				= 0x10C1,
+
+	SFC_GET_CUE_COUNT				= 0x10CD,
+	SFC_GET_CUE						= 0x10CE,
+	SFC_SET_CUE						= 0x10CF,
+
+	SFC_GET_INSTRUMENT				= 0x10D0,
+	SFC_SET_INSTRUMENT				= 0x10D1,
+
+	SFC_GET_LOOP_INFO				= 0x10E0,
+
+	SFC_GET_BROADCAST_INFO			= 0x10F0,
+	SFC_SET_BROADCAST_INFO			= 0x10F1,
+
+	SFC_GET_CHANNEL_MAP_INFO		= 0x1100,
+	SFC_SET_CHANNEL_MAP_INFO		= 0x1101,
+
+	SFC_RAW_DATA_NEEDS_ENDSWAP		= 0x1110,
+
+	/* Support for Wavex Ambisonics Format */
+	SFC_WAVEX_SET_AMBISONIC			= 0x1200,
+	SFC_WAVEX_GET_AMBISONIC			= 0x1201,
+
+	/*
+	** RF64 files can be set so that on-close, writable files that have less
+	** than 4GB of data in them are converted to RIFF/WAV, as per EBU
+	** recommendations.
+	*/
+	SFC_RF64_AUTO_DOWNGRADE			= 0x1210,
+
+	SFC_SET_VBR_ENCODING_QUALITY	= 0x1300,
+	SFC_SET_COMPRESSION_LEVEL		= 0x1301,
+
+	/* Ogg format commands */
+	SFC_SET_OGG_PAGE_LATENCY_MS		= 0x1302,
+	SFC_SET_OGG_PAGE_LATENCY		= 0x1303,
+	SFC_GET_OGG_STREAM_SERIALNO		= 0x1306,
+
+	SFC_GET_BITRATE_MODE			= 0x1304,
+	SFC_SET_BITRATE_MODE			= 0x1305,
+
+	/* Cart Chunk support */
+	SFC_SET_CART_INFO				= 0x1400,
+	SFC_GET_CART_INFO				= 0x1401,
+
+	/* Opus files original samplerate metadata */
+	SFC_SET_ORIGINAL_SAMPLERATE		= 0x1500,
+	SFC_GET_ORIGINAL_SAMPLERATE		= 0x1501,
+
+	/* Following commands for testing only. */
+	SFC_TEST_IEEE_FLOAT_REPLACE		= 0x6001,
+
+	/*
+	** These SFC_SET_ADD_* values are deprecated and will disappear at some
+	** time in the future. They are guaranteed to be here up to and
+	** including version 1.0.8 to avoid breakage of existing software.
+	** They currently do nothing and will continue to do nothing.
+	*/
+	SFC_SET_ADD_HEADER_PAD_CHUNK	= 0x1051,
+
+	SFC_SET_ADD_DITHER_ON_WRITE		= 0x1070,
+	SFC_SET_ADD_DITHER_ON_READ		= 0x1071
+} ;
+
+
+/*
+** String types that can be set and read from files. Not all file types
+** support this and even the file types which support one, may not support
+** all string types.
+*/
+
+enum
+{	SF_STR_TITLE					= 0x01,
+	SF_STR_COPYRIGHT				= 0x02,
+	SF_STR_SOFTWARE					= 0x03,
+	SF_STR_ARTIST					= 0x04,
+	SF_STR_COMMENT					= 0x05,
+	SF_STR_DATE						= 0x06,
+	SF_STR_ALBUM					= 0x07,
+	SF_STR_LICENSE					= 0x08,
+	SF_STR_TRACKNUMBER				= 0x09,
+	SF_STR_GENRE					= 0x10
+} ;
+
+/*
+** Use the following as the start and end index when doing metadata
+** transcoding.
+*/
+
+#define	SF_STR_FIRST	SF_STR_TITLE
+#define	SF_STR_LAST		SF_STR_GENRE
+
+enum
+{	/* True and false */
+	SF_FALSE	= 0,
+	SF_TRUE		= 1,
+
+	/* Modes for opening files. */
+	SFM_READ	= 0x10,
+	SFM_WRITE	= 0x20,
+	SFM_RDWR	= 0x30,
+
+	SF_AMBISONIC_NONE		= 0x40,
+	SF_AMBISONIC_B_FORMAT	= 0x41
+} ;
+
+/* Public error values. These are guaranteed to remain unchanged for the duration
+** of the library major version number.
+** There are also a large number of private error numbers which are internal to
+** the library which can change at any time.
+*/
+
+enum
+{	SF_ERR_NO_ERROR				= 0,
+	SF_ERR_UNRECOGNISED_FORMAT	= 1,
+	SF_ERR_SYSTEM				= 2,
+	SF_ERR_MALFORMED_FILE		= 3,
+	SF_ERR_UNSUPPORTED_ENCODING	= 4
+} ;
+
+
+/* Channel map values (used with SFC_SET/GET_CHANNEL_MAP).
+*/
+
+enum
+{	SF_CHANNEL_MAP_INVALID = 0,
+	SF_CHANNEL_MAP_MONO = 1,
+	SF_CHANNEL_MAP_LEFT,					/* Apple calls this 'Left' */
+	SF_CHANNEL_MAP_RIGHT,					/* Apple calls this 'Right' */
+	SF_CHANNEL_MAP_CENTER,					/* Apple calls this 'Center' */
+	SF_CHANNEL_MAP_FRONT_LEFT,
+	SF_CHANNEL_MAP_FRONT_RIGHT,
+	SF_CHANNEL_MAP_FRONT_CENTER,
+	SF_CHANNEL_MAP_REAR_CENTER,				/* Apple calls this 'Center Surround', Msft calls this 'Back Center' */
+	SF_CHANNEL_MAP_REAR_LEFT,				/* Apple calls this 'Left Surround', Msft calls this 'Back Left' */
+	SF_CHANNEL_MAP_REAR_RIGHT,				/* Apple calls this 'Right Surround', Msft calls this 'Back Right' */
+	SF_CHANNEL_MAP_LFE,						/* Apple calls this 'LFEScreen', Msft calls this 'Low Frequency'  */
+	SF_CHANNEL_MAP_FRONT_LEFT_OF_CENTER,	/* Apple calls this 'Left Center' */
+	SF_CHANNEL_MAP_FRONT_RIGHT_OF_CENTER,	/* Apple calls this 'Right Center */
+	SF_CHANNEL_MAP_SIDE_LEFT,				/* Apple calls this 'Left Surround Direct' */
+	SF_CHANNEL_MAP_SIDE_RIGHT,				/* Apple calls this 'Right Surround Direct' */
+	SF_CHANNEL_MAP_TOP_CENTER,				/* Apple calls this 'Top Center Surround' */
+	SF_CHANNEL_MAP_TOP_FRONT_LEFT,			/* Apple calls this 'Vertical Height Left' */
+	SF_CHANNEL_MAP_TOP_FRONT_RIGHT,			/* Apple calls this 'Vertical Height Right' */
+	SF_CHANNEL_MAP_TOP_FRONT_CENTER,		/* Apple calls this 'Vertical Height Center' */
+	SF_CHANNEL_MAP_TOP_REAR_LEFT,			/* Apple and MS call this 'Top Back Left' */
+	SF_CHANNEL_MAP_TOP_REAR_RIGHT,			/* Apple and MS call this 'Top Back Right' */
+	SF_CHANNEL_MAP_TOP_REAR_CENTER,			/* Apple and MS call this 'Top Back Center' */
+
+	SF_CHANNEL_MAP_AMBISONIC_B_W,
+	SF_CHANNEL_MAP_AMBISONIC_B_X,
+	SF_CHANNEL_MAP_AMBISONIC_B_Y,
+	SF_CHANNEL_MAP_AMBISONIC_B_Z,
+
+	SF_CHANNEL_MAP_MAX
+} ;
+
+/* Bitrate mode values (for use with SFC_GET/SET_BITRATE_MODE)
+*/
+enum
+{	SF_BITRATE_MODE_CONSTANT = 0,
+	SF_BITRATE_MODE_AVERAGE,
+	SF_BITRATE_MODE_VARIABLE
+} ;
+
+
+/* A SNDFILE* pointer can be passed around much like stdio.h's FILE* pointer. */
+
+typedef	struct sf_private_tag	SNDFILE ;
+
+/* The following typedef is system specific and is defined when libsndfile is
+** compiled. sf_count_t will be a 64 bit value when the underlying OS allows
+** 64 bit file offsets.
+** On windows, we need to allow the same header file to be compiler by both GCC
+** and the Microsoft compiler.
+*/
+
+typedef int64_t			sf_count_t ;
+#ifndef SF_COUNT_MAX
+#define SF_COUNT_MAX	INT64_MAX
+#endif
+
+
+/* A pointer to a SF_INFO structure is passed to sf_open () and filled in.
+** On write, the SF_INFO structure is filled in by the user and passed into
+** sf_open ().
+*/
+
+struct SF_INFO
+{	sf_count_t	frames ;		/* Used to be called samples.  Changed to avoid confusion. */
+	int			samplerate ;
+	int			channels ;
+	int			format ;
+	int			sections ;
+	int			seekable ;
+} ;
+
+typedef	struct SF_INFO SF_INFO ;
+
+/* The SF_FORMAT_INFO struct is used to retrieve information about the sound
+** file formats libsndfile supports using the sf_command () interface.
+**
+** Using this interface will allow applications to support new file formats
+** and encoding types when libsndfile is upgraded, without requiring
+** re-compilation of the application.
+**
+** Please consult the libsndfile documentation (particularly the information
+** on the sf_command () interface) for examples of its use.
+*/
+
+typedef struct
+{	int			format ;
+	const char	*name ;
+	const char	*extension ;
+} SF_FORMAT_INFO ;
+
+/*
+** Enums and typedefs for adding dither on read and write.
+** Reserved for future implementation.
+*/
+
+enum
+{	SFD_DEFAULT_LEVEL	= 0,
+	SFD_CUSTOM_LEVEL	= 0x40000000,
+
+	SFD_NO_DITHER		= 500,
+	SFD_WHITE			= 501,
+	SFD_TRIANGULAR_PDF	= 502
+} ;
+
+typedef struct
+{	int			type ;
+	double		level ;
+	const char	*name ;
+} SF_DITHER_INFO ;
+
+/* Struct used to retrieve information about a file embedded within a
+** larger file. See SFC_GET_EMBED_FILE_INFO.
+*/
+
+typedef struct
+{	sf_count_t	offset ;
+	sf_count_t	length ;
+} SF_EMBED_FILE_INFO ;
+
+/*
+**	Struct used to retrieve cue marker information from a file
+*/
+
+typedef struct
+{	int32_t 	indx ;
+	uint32_t 	position ;
+	int32_t 	fcc_chunk ;
+	int32_t 	chunk_start ;
+	int32_t		block_start ;
+	uint32_t 	sample_offset ;
+	char name [256] ;
+} SF_CUE_POINT ;
+
+#define	SF_CUES_VAR(count) \
+	struct \
+	{	uint32_t cue_count ; \
+		SF_CUE_POINT cue_points [count] ; \
+	}
+
+typedef SF_CUES_VAR (100) SF_CUES ;
+
+/*
+**	Structs used to retrieve music sample information from a file.
+*/
+
+enum
+{	/*
+	**	The loop mode field in SF_INSTRUMENT will be one of the following.
+	*/
+	SF_LOOP_NONE = 800,
+	SF_LOOP_FORWARD,
+	SF_LOOP_BACKWARD,
+	SF_LOOP_ALTERNATING
+} ;
+
+typedef struct
+{	int gain ;
+	char basenote, detune ;
+	char velocity_lo, velocity_hi ;
+	char key_lo, key_hi ;
+	int loop_count ;
+
+	struct
+	{	int mode ;
+		uint32_t start ;
+		uint32_t end ;
+		uint32_t count ;
+	} loops [16] ; /* make variable in a sensible way */
+} SF_INSTRUMENT ;
+
+
+
+/* Struct used to retrieve loop information from a file.*/
+typedef struct
+{
+	short	time_sig_num ;	/* any positive integer    > 0  */
+	short	time_sig_den ;	/* any positive power of 2 > 0  */
+	int		loop_mode ;		/* see SF_LOOP enum             */
+
+	int		num_beats ;		/* this is NOT the amount of quarter notes !!!*/
+							/* a full bar of 4/4 is 4 beats */
+							/* a full bar of 7/8 is 7 beats */
+
+	float	bpm ;			/* suggestion, as it can be calculated using other fields:*/
+							/* file's length, file's sampleRate and our time_sig_den*/
+							/* -> bpms are always the amount of _quarter notes_ per minute */
+
+	int	root_key ;			/* MIDI note, or -1 for None */
+	int future [6] ;
+} SF_LOOP_INFO ;
+
+
+/*	Struct used to retrieve broadcast (EBU) information from a file.
+**	Strongly (!) based on EBU "bext" chunk format used in Broadcast WAVE.
+*/
+#define	SF_BROADCAST_INFO_VAR(coding_hist_size) \
+			struct \
+			{	char		description [256] ; \
+				char		originator [32] ; \
+				char		originator_reference [32] ; \
+				char		origination_date [10] ; \
+				char		origination_time [8] ; \
+				uint32_t	time_reference_low ; \
+				uint32_t	time_reference_high ; \
+				short		version ; \
+				char		umid [64] ; \
+				int16_t	loudness_value ; \
+				int16_t	loudness_range ; \
+				int16_t	max_true_peak_level ; \
+				int16_t	max_momentary_loudness ; \
+				int16_t	max_shortterm_loudness ; \
+				char		reserved [180] ; \
+				uint32_t	coding_history_size ; \
+				char		coding_history [coding_hist_size] ; \
+			}
+
+/* SF_BROADCAST_INFO is the above struct with coding_history field of 256 bytes. */
+typedef SF_BROADCAST_INFO_VAR (256) SF_BROADCAST_INFO ;
+
+struct SF_CART_TIMER
+{	char	usage [4] ;
+	int32_t	value ;
+} ;
+
+typedef struct SF_CART_TIMER SF_CART_TIMER ;
+
+#define	SF_CART_INFO_VAR(p_tag_text_size) \
+			struct \
+			{	char		version [4] ; \
+				char		title [64] ; \
+				char		artist [64] ; \
+				char		cut_id [64] ; \
+				char		client_id [64] ; \
+				char		category [64] ; \
+				char		classification [64] ; \
+				char		out_cue [64] ; \
+				char		start_date [10] ; \
+				char		start_time [8] ; \
+				char		end_date [10] ; \
+				char		end_time [8] ; \
+				char		producer_app_id [64] ; \
+				char		producer_app_version [64] ; \
+				char		user_def [64] ; \
+				int32_t		level_reference ; \
+				SF_CART_TIMER	post_timers [8] ; \
+				char		reserved [276] ; \
+				char		url [1024] ; \
+				uint32_t	tag_text_size ; \
+				char		tag_text [p_tag_text_size] ; \
+			}
+
+typedef SF_CART_INFO_VAR (256) SF_CART_INFO ;
+
+/*	Virtual I/O functionality. */
+
+typedef sf_count_t		(*sf_vio_get_filelen)	(void *user_data) ;
+typedef sf_count_t		(*sf_vio_seek)		(sf_count_t offset, int whence, void *user_data) ;
+typedef sf_count_t		(*sf_vio_read)		(void *ptr, sf_count_t count, void *user_data) ;
+typedef sf_count_t		(*sf_vio_write)		(const void *ptr, sf_count_t count, void *user_data) ;
+typedef sf_count_t		(*sf_vio_tell)		(void *user_data) ;
+
+struct SF_VIRTUAL_IO
+{	sf_vio_get_filelen	get_filelen ;
+	sf_vio_seek			seek ;
+	sf_vio_read			read ;
+	sf_vio_write		write ;
+	sf_vio_tell			tell ;
+} ;
+
+typedef	struct SF_VIRTUAL_IO SF_VIRTUAL_IO ;
+
+
+/* Open the specified file for read, write or both. On error, this will
+** return a NULL pointer. To find the error number, pass a NULL SNDFILE
+** to sf_strerror ().
+** All calls to sf_open() should be matched with a call to sf_close().
+*/
+
+SNDFILE* 	sf_open		(const char *path, int mode, SF_INFO *sfinfo) ;
+
+
+/* Use the existing file descriptor to create a SNDFILE object. If close_desc
+** is TRUE, the file descriptor will be closed when sf_close() is called. If
+** it is FALSE, the descriptor will not be closed.
+** When passed a descriptor like this, the library will assume that the start
+** of file header is at the current file offset. This allows sound files within
+** larger container files to be read and/or written.
+** On error, this will return a NULL pointer. To find the error number, pass a
+** NULL SNDFILE to sf_strerror ().
+** All calls to sf_open_fd() should be matched with a call to sf_close().
+
+*/
+
+SNDFILE* 	sf_open_fd	(int fd, int mode, SF_INFO *sfinfo, int close_desc) ;
+
+SNDFILE* 	sf_open_virtual	(SF_VIRTUAL_IO *sfvirtual, int mode, SF_INFO *sfinfo, void *user_data) ;
+
+
+/* sf_error () returns a error number which can be translated to a text
+** string using sf_error_number().
+*/
+
+int		sf_error		(SNDFILE *sndfile) ;
+
+
+/* sf_strerror () returns to the caller a pointer to the current error message for
+** the given SNDFILE.
+*/
+
+const char* sf_strerror (SNDFILE *sndfile) ;
+
+
+/* sf_error_number () allows the retrieval of the error string for each internal
+** error number.
+**
+*/
+
+const char*	sf_error_number	(int errnum) ;
+
+
+/* The following two error functions are deprecated but they will remain in the
+** library for the foreseeable future. The function sf_strerror() should be used
+** in their place.
+*/
+
+int		sf_perror		(SNDFILE *sndfile) ;
+int		sf_error_str	(SNDFILE *sndfile, char* str, size_t len) ;
+
+
+/* Allow the caller to retrieve information from or change aspects of the
+** library behaviour.
+*/
+
+int		sf_command	(SNDFILE *sndfile, int command, void *data, int datasize) ;
+
+
+/* Return TRUE if fields of the SF_INFO struct are a valid combination of values. */
+
+int		sf_format_check	(const SF_INFO *info) ;
+
+
+/* Seek within the waveform data chunk of the SNDFILE. sf_seek () uses
+** the same values for whence (SEEK_SET, SEEK_CUR and SEEK_END) as
+** stdio.h function fseek ().
+** An offset of zero with whence set to SEEK_SET will position the
+** read / write pointer to the first data sample.
+** On success sf_seek returns the current position in (multi-channel)
+** samples from the start of the file.
+** Please see the libsndfile documentation for moving the read pointer
+** separately from the write pointer on files open in mode SFM_RDWR.
+** On error all of these functions return -1.
+*/
+
+enum
+{	SF_SEEK_SET = SEEK_SET,
+	SF_SEEK_CUR = SEEK_CUR,
+	SF_SEEK_END = SEEK_END
+} ;
+
+sf_count_t	sf_seek 		(SNDFILE *sndfile, sf_count_t frames, int whence) ;
+
+
+/* Functions for retrieving and setting string data within sound files.
+** Not all file types support this features; AIFF and WAV do. For both
+** functions, the str_type parameter must be one of the SF_STR_* values
+** defined above.
+** On error, sf_set_string() returns non-zero while sf_get_string()
+** returns NULL.
+*/
+
+int sf_set_string (SNDFILE *sndfile, int str_type, const char* str) ;
+
+const char* sf_get_string (SNDFILE *sndfile, int str_type) ;
+
+
+/* Return the library version string. */
+
+const char * sf_version_string (void) ;
+
+/* Return the current byterate at this point in the file. The byte rate in this
+** case is the number of bytes per second of audio data. For instance, for a
+** stereo, 18 bit PCM encoded file with an 16kHz sample rate, the byte rate
+** would be 2 (stereo) * 2 (two bytes per sample) * 16000 => 64000 bytes/sec.
+** For some file formats the returned value will be accurate and exact, for some
+** it will be a close approximation, for some it will be the average bitrate for
+** the whole file and for some it will be a time varying value that was accurate
+** when the file was most recently read or written.
+** To get the bitrate, multiple this value by 8.
+** Returns -1 for unknown.
+*/
+int sf_current_byterate (SNDFILE *sndfile) ;
+
+/* Functions for reading/writing the waveform data of a sound file.
+*/
+
+sf_count_t	sf_read_raw		(SNDFILE *sndfile, void *ptr, sf_count_t bytes) ;
+sf_count_t	sf_write_raw 	(SNDFILE *sndfile, const void *ptr, sf_count_t bytes) ;
+
+
+/* Functions for reading and writing the data chunk in terms of frames.
+** The number of items actually read/written = frames * number of channels.
+**     sf_xxxx_raw		read/writes the raw data bytes from/to the file
+**     sf_xxxx_short	passes data in the native short format
+**     sf_xxxx_int		passes data in the native int format
+**     sf_xxxx_float	passes data in the native float format
+**     sf_xxxx_double	passes data in the native double format
+** All of these read/write function return number of frames read/written.
+*/
+
+sf_count_t	sf_readf_short	(SNDFILE *sndfile, short *ptr, sf_count_t frames) ;
+sf_count_t	sf_writef_short	(SNDFILE *sndfile, const short *ptr, sf_count_t frames) ;
+
+sf_count_t	sf_readf_int	(SNDFILE *sndfile, int *ptr, sf_count_t frames) ;
+sf_count_t	sf_writef_int 	(SNDFILE *sndfile, const int *ptr, sf_count_t frames) ;
+
+sf_count_t	sf_readf_float	(SNDFILE *sndfile, float *ptr, sf_count_t frames) ;
+sf_count_t	sf_writef_float	(SNDFILE *sndfile, const float *ptr, sf_count_t frames) ;
+
+sf_count_t	sf_readf_double		(SNDFILE *sndfile, double *ptr, sf_count_t frames) ;
+sf_count_t	sf_writef_double	(SNDFILE *sndfile, const double *ptr, sf_count_t frames) ;
+
+
+/* Functions for reading and writing the data chunk in terms of items.
+** Otherwise similar to above.
+** All of these read/write function return number of items read/written.
+*/
+
+sf_count_t	sf_read_short	(SNDFILE *sndfile, short *ptr, sf_count_t items) ;
+sf_count_t	sf_write_short	(SNDFILE *sndfile, const short *ptr, sf_count_t items) ;
+
+sf_count_t	sf_read_int		(SNDFILE *sndfile, int *ptr, sf_count_t items) ;
+sf_count_t	sf_write_int 	(SNDFILE *sndfile, const int *ptr, sf_count_t items) ;
+
+sf_count_t	sf_read_float	(SNDFILE *sndfile, float *ptr, sf_count_t items) ;
+sf_count_t	sf_write_float	(SNDFILE *sndfile, const float *ptr, sf_count_t items) ;
+
+sf_count_t	sf_read_double	(SNDFILE *sndfile, double *ptr, sf_count_t items) ;
+sf_count_t	sf_write_double	(SNDFILE *sndfile, const double *ptr, sf_count_t items) ;
+
+
+/* Close the SNDFILE and clean up all memory allocations associated with this
+** file.
+** Returns 0 on success, or an error number.
+*/
+
+int		sf_close		(SNDFILE *sndfile) ;
+
+
+/* If the file is opened SFM_WRITE or SFM_RDWR, call fsync() on the file
+** to force the writing of data to disk. If the file is opened SFM_READ
+** no action is taken.
+*/
+
+void	sf_write_sync	(SNDFILE *sndfile) ;
+
+
+
+/* The function sf_wchar_open() is Windows Only!
+** Open a file passing in a Windows Unicode filename. Otherwise, this is
+** the same as sf_open().
+*/
+
+#ifdef _WIN32
+SNDFILE* sf_wchar_open (const wchar_t *wpath, int mode, SF_INFO *sfinfo) ;
+#endif
+
+
+
+
+/* Getting and setting of chunks from within a sound file.
+**
+** These functions allow the getting and setting of chunks within a sound file
+** (for those formats which allow it).
+**
+** These functions fail safely. Specifically, they will not allow you to overwrite
+** existing chunks or add extra versions of format specific reserved chunks but
+** should allow you to retrieve any and all chunks (may not be implemented for
+** all chunks or all file formats).
+*/
+
+struct SF_CHUNK_INFO
+{	char		id [64] ;	/* The chunk identifier. */
+	unsigned	id_size ;	/* The size of the chunk identifier. */
+	unsigned	datalen ;	/* The size of that data. */
+	void		*data ;		/* Pointer to the data. */
+} ;
+
+typedef struct SF_CHUNK_INFO SF_CHUNK_INFO ;
+
+/* Set the specified chunk info (must be done before any audio data is written
+** to the file). This will fail for format specific reserved chunks.
+** The chunk_info->data pointer must be valid until the file is closed.
+** Returns SF_ERR_NO_ERROR on success or non-zero on failure.
+*/
+int sf_set_chunk (SNDFILE * sndfile, const SF_CHUNK_INFO * chunk_info) ;
+
+/*
+** An opaque structure to an iterator over the all chunks of a given id
+*/
+typedef	struct SF_CHUNK_ITERATOR SF_CHUNK_ITERATOR ;
+
+/* Get an iterator for all chunks matching chunk_info.
+** The iterator will point to the first chunk matching chunk_info.
+** Chunks are matching, if (chunk_info->id) matches the first
+**     (chunk_info->id_size) bytes of a chunk found in the SNDFILE* handle.
+** If chunk_info is NULL, an iterator to all chunks in the SNDFILE* handle
+**     is returned.
+** The values of chunk_info->datalen and chunk_info->data are ignored.
+** If no matching chunks are found in the sndfile, NULL is returned.
+** The returned iterator will stay valid until one of the following occurs:
+**     a) The sndfile is closed.
+**     b) A new chunk is added using sf_set_chunk().
+**     c) Another chunk iterator function is called on the same SNDFILE* handle
+**        that causes the iterator to be modified.
+** The memory for the iterator belongs to the SNDFILE* handle and is freed when
+** sf_close() is called.
+*/
+SF_CHUNK_ITERATOR *
+sf_get_chunk_iterator (SNDFILE * sndfile, const SF_CHUNK_INFO * chunk_info) ;
+
+/* Iterate through chunks by incrementing the iterator.
+** Increments the iterator and returns a handle to the new one.
+** After this call, iterator will no longer be valid, and you must use the
+**      newly returned handle from now on.
+** The returned handle can be used to access the next chunk matching
+**      the criteria as defined in sf_get_chunk_iterator().
+** If iterator points to the last chunk, this will free all resources
+**      associated with iterator and return NULL.
+** The returned iterator will stay valid until sf_get_chunk_iterator_next
+**      is called again, the sndfile is closed or a new chunk us added.
+*/
+SF_CHUNK_ITERATOR *
+sf_next_chunk_iterator (SF_CHUNK_ITERATOR * iterator) ;
+
+
+/* Get the size of the specified chunk.
+** If the specified chunk exists, the size will be returned in the
+**      datalen field of the SF_CHUNK_INFO struct.
+**      Additionally, the id of the chunk will be copied to the id
+**      field of the SF_CHUNK_INFO struct and it's id_size field will
+**      be updated accordingly.
+** If the chunk doesn't exist chunk_info->datalen will be zero, and the
+**      id and id_size fields will be undefined.
+** The function will return SF_ERR_NO_ERROR on success or non-zero on
+** failure.
+*/
+int
+sf_get_chunk_size (const SF_CHUNK_ITERATOR * it, SF_CHUNK_INFO * chunk_info) ;
+
+/* Get the specified chunk data.
+** If the specified chunk exists, up to chunk_info->datalen bytes of
+**      the chunk data will be copied into the chunk_info->data buffer
+**      (allocated by the caller) and the chunk_info->datalen field
+**      updated to reflect the size of the data. The id and id_size
+**      field will be updated according to the retrieved chunk
+** If the chunk doesn't exist chunk_info->datalen will be zero, and the
+**      id and id_size fields will be undefined.
+** The function will return SF_ERR_NO_ERROR on success or non-zero on
+** failure.
+*/
+int
+sf_get_chunk_data (const SF_CHUNK_ITERATOR * it, SF_CHUNK_INFO * chunk_info) ;
+
+
+#ifdef __cplusplus
+}		/* extern "C" */
+#endif	/* __cplusplus */
+
+#endif	/* SNDFILE_H */
diff --git a/include/sndfile.hh b/include/sndfile.hh
new file mode 100644
index 0000000..f8beb71
--- /dev/null
+++ b/include/sndfile.hh
@@ -0,0 +1,484 @@
+/*
+** Copyright (C) 2005-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in
+**       the documentation and/or other materials provided with the
+**       distribution.
+**     * Neither the author nor the names of any contributors may be used
+**       to endorse or promote products derived from this software without
+**       specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*
+** The above modified BSD style license (GPL and LGPL compatible) applies to
+** this file. It does not apply to libsndfile itself which is released under
+** the GNU LGPL or the libsndfile test suite which is released under the GNU
+** GPL.
+** This means that this header file can be used under this modified BSD style
+** license, but the LGPL still holds for the libsndfile library itself.
+*/
+
+/*
+** sndfile.hh -- A lightweight C++ wrapper for the libsndfile API.
+**
+** All the methods are inlines and all functionality is contained in this
+** file. There is no separate implementation file.
+**
+** API documentation is in the doc/ directory of the source code tarball
+** and at http://libsndfile.github.io/libsndfile/api.html.
+**
+** This file is intended to compile with C++98 and newer.
+*/
+
+#ifndef SNDFILE_HH
+#define SNDFILE_HH
+
+#include <sndfile.h>
+
+#include <string>
+#include <new> // for std::nothrow
+
+#if ((defined (_MSC_VER) && (_MSC_VER >= 1600)) || (__cplusplus >= 201100L))
+#define SF_NULL nullptr
+#else
+#define SF_NULL NULL
+#endif
+
+class SndfileHandle
+{	private :
+		struct SNDFILE_ref
+		{	SNDFILE_ref (void) ;
+			~SNDFILE_ref (void) ;
+
+			SNDFILE *sf ;
+			SF_INFO sfinfo ;
+			int ref ;
+			} ;
+
+		SNDFILE_ref *p ;
+
+	public :
+			/* Default constructor */
+			SndfileHandle (void) : p (SF_NULL) {} ;
+			SndfileHandle (const char *path, int mode = SFM_READ,
+							int format = 0, int channels = 0, int samplerate = 0) ;
+			SndfileHandle (std::string const & path, int mode = SFM_READ,
+							int format = 0, int channels = 0, int samplerate = 0) ;
+			SndfileHandle (int fd, bool close_desc, int mode = SFM_READ,
+							int format = 0, int channels = 0, int samplerate = 0) ;
+			SndfileHandle (SF_VIRTUAL_IO &sfvirtual, void *user_data, int mode = SFM_READ,
+							int format = 0, int channels = 0, int samplerate = 0) ;
+
+#ifdef _WIN32
+			SndfileHandle (const wchar_t *wpath, int mode = SFM_READ,
+							int format = 0, int channels = 0, int samplerate = 0) ;
+#endif
+
+			~SndfileHandle (void) ;
+
+			SndfileHandle (const SndfileHandle &orig) ;
+			SndfileHandle & operator = (const SndfileHandle &rhs) ;
+
+#if (__cplusplus >= 201100L)
+			SndfileHandle (SndfileHandle &&orig) noexcept ;
+			SndfileHandle & operator = (SndfileHandle &&rhs) noexcept ;
+#endif
+
+		/* Mainly for debugging/testing. */
+		int refCount (void) const { return (p == SF_NULL) ? 0 : p->ref ; }
+
+		operator bool () const { return (p != SF_NULL) ; }
+
+		bool operator == (const SndfileHandle &rhs) const { return (p == rhs.p) ; }
+
+		sf_count_t	frames (void) const		{ return p ? p->sfinfo.frames : 0 ; }
+		int			format (void) const		{ return p ? p->sfinfo.format : 0 ; }
+		int			channels (void) const	{ return p ? p->sfinfo.channels : 0 ; }
+		int			samplerate (void) const { return p ? p->sfinfo.samplerate : 0 ; }
+
+		int error (void) const ;
+		const char * strError (void) const ;
+
+		int command (int cmd, void *data, int datasize) ;
+
+		sf_count_t	seek (sf_count_t frames, int whence) ;
+
+		void writeSync (void) ;
+
+		int setString (int str_type, const char* str) ;
+
+		const char* getString (int str_type) const ;
+
+		static int formatCheck (int format, int channels, int samplerate) ;
+
+		sf_count_t read (short *ptr, sf_count_t items) ;
+		sf_count_t read (int *ptr, sf_count_t items) ;
+		sf_count_t read (float *ptr, sf_count_t items) ;
+		sf_count_t read (double *ptr, sf_count_t items) ;
+
+		sf_count_t write (const short *ptr, sf_count_t items) ;
+		sf_count_t write (const int *ptr, sf_count_t items) ;
+		sf_count_t write (const float *ptr, sf_count_t items) ;
+		sf_count_t write (const double *ptr, sf_count_t items) ;
+
+		sf_count_t readf (short *ptr, sf_count_t frames) ;
+		sf_count_t readf (int *ptr, sf_count_t frames) ;
+		sf_count_t readf (float *ptr, sf_count_t frames) ;
+		sf_count_t readf (double *ptr, sf_count_t frames) ;
+
+		sf_count_t writef (const short *ptr, sf_count_t frames) ;
+		sf_count_t writef (const int *ptr, sf_count_t frames) ;
+		sf_count_t writef (const float *ptr, sf_count_t frames) ;
+		sf_count_t writef (const double *ptr, sf_count_t frames) ;
+
+		sf_count_t	readRaw		(void *ptr, sf_count_t bytes) ;
+		sf_count_t	writeRaw	(const void *ptr, sf_count_t bytes) ;
+
+		/**< Raw access to the handle. SndfileHandle keeps ownership. */
+		SNDFILE * rawHandle (void) ;
+
+		/**< Take ownership of handle, if reference count is 1. */
+		SNDFILE * takeOwnership (void) ;
+} ;
+
+/*==============================================================================
+**	Nothing but implementation below.
+*/
+
+inline
+SndfileHandle::SNDFILE_ref::SNDFILE_ref (void)
+: sf (SF_NULL), sfinfo (), ref (1)
+{}
+
+inline
+SndfileHandle::SNDFILE_ref::~SNDFILE_ref (void)
+{	if (sf != SF_NULL) sf_close (sf) ; }
+
+inline
+SndfileHandle::SndfileHandle (const char *path, int mode, int fmt, int chans, int srate)
+: p (SF_NULL)
+{
+	p = new (std::nothrow) SNDFILE_ref () ;
+
+	if (p != SF_NULL)
+	{	p->ref = 1 ;
+
+		p->sfinfo.frames = 0 ;
+		p->sfinfo.channels = chans ;
+		p->sfinfo.format = fmt ;
+		p->sfinfo.samplerate = srate ;
+		p->sfinfo.sections = 0 ;
+		p->sfinfo.seekable = 0 ;
+
+		p->sf = sf_open (path, mode, &p->sfinfo) ;
+		} ;
+
+	return ;
+} /* SndfileHandle const char * constructor */
+
+inline
+SndfileHandle::SndfileHandle (std::string const & path, int mode, int fmt, int chans, int srate)
+: p (SF_NULL)
+{
+	p = new (std::nothrow) SNDFILE_ref () ;
+
+	if (p != SF_NULL)
+	{	p->ref = 1 ;
+
+		p->sfinfo.frames = 0 ;
+		p->sfinfo.channels = chans ;
+		p->sfinfo.format = fmt ;
+		p->sfinfo.samplerate = srate ;
+		p->sfinfo.sections = 0 ;
+		p->sfinfo.seekable = 0 ;
+
+		p->sf = sf_open (path.c_str (), mode, &p->sfinfo) ;
+		} ;
+
+	return ;
+} /* SndfileHandle std::string constructor */
+
+inline
+SndfileHandle::SndfileHandle (int fd, bool close_desc, int mode, int fmt, int chans, int srate)
+: p (SF_NULL)
+{
+	if (fd < 0)
+		return ;
+
+	p = new (std::nothrow) SNDFILE_ref () ;
+
+	if (p != SF_NULL)
+	{	p->ref = 1 ;
+
+		p->sfinfo.frames = 0 ;
+		p->sfinfo.channels = chans ;
+		p->sfinfo.format = fmt ;
+		p->sfinfo.samplerate = srate ;
+		p->sfinfo.sections = 0 ;
+		p->sfinfo.seekable = 0 ;
+
+		p->sf = sf_open_fd (fd, mode, &p->sfinfo, close_desc) ;
+		} ;
+
+	return ;
+} /* SndfileHandle fd constructor */
+
+inline
+SndfileHandle::SndfileHandle (SF_VIRTUAL_IO &sfvirtual, void *user_data, int mode, int fmt, int chans, int srate)
+: p (SF_NULL)
+{
+	p = new (std::nothrow) SNDFILE_ref () ;
+
+	if (p != SF_NULL)
+	{	p->ref = 1 ;
+
+		p->sfinfo.frames = 0 ;
+		p->sfinfo.channels = chans ;
+		p->sfinfo.format = fmt ;
+		p->sfinfo.samplerate = srate ;
+		p->sfinfo.sections = 0 ;
+		p->sfinfo.seekable = 0 ;
+
+		p->sf = sf_open_virtual (&sfvirtual, mode, &p->sfinfo, user_data) ;
+		} ;
+
+	return ;
+} /* SndfileHandle std::string constructor */
+
+inline
+SndfileHandle::~SndfileHandle (void)
+{	if (p != SF_NULL && -- p->ref == 0)
+		delete p ;
+} /* SndfileHandle destructor */
+
+
+inline
+SndfileHandle::SndfileHandle (const SndfileHandle &orig)
+: p (orig.p)
+{	if (p != SF_NULL)
+		++ p->ref ;
+} /* SndfileHandle copy constructor */
+
+inline SndfileHandle &
+SndfileHandle::operator = (const SndfileHandle &rhs)
+{
+	if (&rhs == this)
+		return *this ;
+	if (p != SF_NULL && -- p->ref == 0)
+		delete p ;
+
+	p = rhs.p ;
+	if (p != SF_NULL)
+		++ p->ref ;
+
+	return *this ;
+} /* SndfileHandle copy assignment */
+
+#if (__cplusplus >= 201100L)
+
+inline
+SndfileHandle::SndfileHandle (SndfileHandle &&orig) noexcept
+: p (orig.p)
+{
+	orig.p = SF_NULL ;
+} /* SndfileHandle move constructor */
+
+inline SndfileHandle &
+SndfileHandle::operator = (SndfileHandle &&rhs) noexcept
+{
+	if (&rhs == this)
+		return *this ;
+	if (p != SF_NULL && -- p->ref == 0)
+		delete p ;
+
+	p = rhs.p ;
+	rhs.p = SF_NULL ;
+
+	return *this ;
+} /* SndfileHandle move assignment */
+
+#endif
+
+inline int
+SndfileHandle::error (void) const
+{	return sf_error (p->sf) ; }
+
+inline const char *
+SndfileHandle::strError (void) const
+{	return sf_strerror (p->sf) ; }
+
+inline int
+SndfileHandle::command (int cmd, void *data, int datasize)
+{	return sf_command (p->sf, cmd, data, datasize) ; }
+
+inline sf_count_t
+SndfileHandle::seek (sf_count_t frame_count, int whence)
+{	return sf_seek (p->sf, frame_count, whence) ; }
+
+inline void
+SndfileHandle::writeSync (void)
+{	sf_write_sync (p->sf) ; }
+
+inline int
+SndfileHandle::setString (int str_type, const char* str)
+{	return sf_set_string (p->sf, str_type, str) ; }
+
+inline const char*
+SndfileHandle::getString (int str_type) const
+{	return sf_get_string (p->sf, str_type) ; }
+
+inline int
+SndfileHandle::formatCheck (int fmt, int chans, int srate)
+{
+	SF_INFO sfinfo ;
+
+	sfinfo.frames = 0 ;
+	sfinfo.channels = chans ;
+	sfinfo.format = fmt ;
+	sfinfo.samplerate = srate ;
+	sfinfo.sections = 0 ;
+	sfinfo.seekable = 0 ;
+
+	return sf_format_check (&sfinfo) ;
+}
+
+/*---------------------------------------------------------------------*/
+
+inline sf_count_t
+SndfileHandle::read (short *ptr, sf_count_t items)
+{	return sf_read_short (p->sf, ptr, items) ; }
+
+inline sf_count_t
+SndfileHandle::read (int *ptr, sf_count_t items)
+{	return sf_read_int (p->sf, ptr, items) ; }
+
+inline sf_count_t
+SndfileHandle::read (float *ptr, sf_count_t items)
+{	return sf_read_float (p->sf, ptr, items) ; }
+
+inline sf_count_t
+SndfileHandle::read (double *ptr, sf_count_t items)
+{	return sf_read_double (p->sf, ptr, items) ; }
+
+inline sf_count_t
+SndfileHandle::write (const short *ptr, sf_count_t items)
+{	return sf_write_short (p->sf, ptr, items) ; }
+
+inline sf_count_t
+SndfileHandle::write (const int *ptr, sf_count_t items)
+{	return sf_write_int (p->sf, ptr, items) ; }
+
+inline sf_count_t
+SndfileHandle::write (const float *ptr, sf_count_t items)
+{	return sf_write_float (p->sf, ptr, items) ; }
+
+inline sf_count_t
+SndfileHandle::write (const double *ptr, sf_count_t items)
+{	return sf_write_double (p->sf, ptr, items) ; }
+
+inline sf_count_t
+SndfileHandle::readf (short *ptr, sf_count_t frame_count)
+{	return sf_readf_short (p->sf, ptr, frame_count) ; }
+
+inline sf_count_t
+SndfileHandle::readf (int *ptr, sf_count_t frame_count)
+{	return sf_readf_int (p->sf, ptr, frame_count) ; }
+
+inline sf_count_t
+SndfileHandle::readf (float *ptr, sf_count_t frame_count)
+{	return sf_readf_float (p->sf, ptr, frame_count) ; }
+
+inline sf_count_t
+SndfileHandle::readf (double *ptr, sf_count_t frame_count)
+{	return sf_readf_double (p->sf, ptr, frame_count) ; }
+
+inline sf_count_t
+SndfileHandle::writef (const short *ptr, sf_count_t frame_count)
+{	return sf_writef_short (p->sf, ptr, frame_count) ; }
+
+inline sf_count_t
+SndfileHandle::writef (const int *ptr, sf_count_t frame_count)
+{	return sf_writef_int (p->sf, ptr, frame_count) ; }
+
+inline sf_count_t
+SndfileHandle::writef (const float *ptr, sf_count_t frame_count)
+{	return sf_writef_float (p->sf, ptr, frame_count) ; }
+
+inline sf_count_t
+SndfileHandle::writef (const double *ptr, sf_count_t frame_count)
+{	return sf_writef_double (p->sf, ptr, frame_count) ; }
+
+inline sf_count_t
+SndfileHandle::readRaw (void *ptr, sf_count_t bytes)
+{	return sf_read_raw (p->sf, ptr, bytes) ; }
+
+inline sf_count_t
+SndfileHandle::writeRaw (const void *ptr, sf_count_t bytes)
+{	return sf_write_raw (p->sf, ptr, bytes) ; }
+
+inline SNDFILE *
+SndfileHandle::rawHandle (void)
+{	return (p ? p->sf : SF_NULL) ; }
+
+inline SNDFILE *
+SndfileHandle::takeOwnership (void)
+{
+	if (p == SF_NULL || (p->ref != 1))
+		return SF_NULL ;
+
+	SNDFILE * sf = p->sf ;
+	p->sf = SF_NULL ;
+	delete p ;
+	p = SF_NULL ;
+	return sf ;
+}
+
+#ifdef _WIN32
+
+inline
+SndfileHandle::SndfileHandle (const wchar_t *wpath, int mode, int fmt, int chans, int srate)
+: p (SF_NULL)
+{
+	p = new (std::nothrow) SNDFILE_ref () ;
+
+	if (p != SF_NULL)
+	{	p->ref = 1 ;
+
+		p->sfinfo.frames = 0 ;
+		p->sfinfo.channels = chans ;
+		p->sfinfo.format = fmt ;
+		p->sfinfo.samplerate = srate ;
+		p->sfinfo.sections = 0 ;
+		p->sfinfo.seekable = 0 ;
+
+		p->sf = sf_wchar_open (wpath, mode, &p->sfinfo) ;
+		} ;
+
+	return ;
+} /* SndfileHandle const wchar_t * constructor */
+
+#endif
+
+#endif	/* SNDFILE_HH */
+
diff --git a/license.bzl b/license.bzl
new file mode 100644
index 0000000..c42818e
--- /dev/null
+++ b/license.bzl
@@ -0,0 +1,1099 @@
+"""Autogenerated license extraction symbolic macro."""
+
+load("//stardust-build/compliance:defs.bzl", "concat_licenses", "extract_lines")
+
+def _concat_package_licenses_impl(name, visibility):
+    extract_lines(
+        name = name + "_extract/COPYING_L1_456",
+        end_line = 456,
+        src = Label(":COPYING"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/COPYING_L458_502",
+        end_line = 502,
+        src = Label(":COPYING"),
+        start_line = 458,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/Scripts/android-configure.sh_L1_27",
+        end_line = 27,
+        src = Label(":Scripts/android-configure.sh"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/docs/index.md_L9_C37_9",
+        end_line = 9,
+        src = Label(":docs/index.md"),
+        start_col = 37,
+        start_line = 9,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/docs/index.md_L196_C36_322",
+        end_line = 322,
+        src = Label(":docs/index.md"),
+        start_col = 36,
+        start_line = 196,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/examples/generate.c_L1_30",
+        end_line = 30,
+        src = Label(":examples/generate.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/examples/list_formats.c_L1_30",
+        end_line = 30,
+        src = Label(":examples/list_formats.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/examples/make_sine.c_L1_30",
+        end_line = 30,
+        src = Label(":examples/make_sine.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/examples/sfprocess.c_L1_30",
+        end_line = 30,
+        src = Label(":examples/sfprocess.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/examples/sndfile-loopify.c_L1_30",
+        end_line = 30,
+        src = Label(":examples/sndfile-loopify.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/examples/sndfile-to-text.c_L1_30",
+        end_line = 30,
+        src = Label(":examples/sndfile-to-text.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/include/sndfile.h_L1_16",
+        end_line = 16,
+        src = Label(":include/sndfile.h"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/include/sndfile.hh_L1_30",
+        end_line = 30,
+        src = Label(":include/sndfile.hh"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/programs/common.c_L1_31",
+        end_line = 31,
+        src = Label(":programs/common.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/programs/common.h_L1_30",
+        end_line = 30,
+        src = Label(":programs/common.h"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/programs/sndfile-cmp.c_L1_31",
+        end_line = 31,
+        src = Label(":programs/sndfile-cmp.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/programs/sndfile-concat.c_L1_30",
+        end_line = 30,
+        src = Label(":programs/sndfile-concat.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/programs/sndfile-convert.c_L1_30",
+        end_line = 30,
+        src = Label(":programs/sndfile-convert.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/programs/sndfile-deinterleave.c_L1_30",
+        end_line = 30,
+        src = Label(":programs/sndfile-deinterleave.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/programs/sndfile-interleave.c_L1_30",
+        end_line = 30,
+        src = Label(":programs/sndfile-interleave.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/programs/sndfile-metadata-get.c_L1_31",
+        end_line = 31,
+        src = Label(":programs/sndfile-metadata-get.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/programs/sndfile-metadata-set.c_L1_31",
+        end_line = 31,
+        src = Label(":programs/sndfile-metadata-set.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/programs/sndfile-play.c_L1_30",
+        end_line = 30,
+        src = Label(":programs/sndfile-play.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/programs/sndfile-salvage.c_L1_30",
+        end_line = 30,
+        src = Label(":programs/sndfile-salvage.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/programs/test-sndfile-metadata-set.py_L1_31",
+        end_line = 31,
+        src = Label(":programs/test-sndfile-metadata-set.py"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/ALAC/ALACAudioTypes.h_L1_16",
+        end_line = 16,
+        src = Label(":src/ALAC/ALACAudioTypes.h"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/ALAC/ALACBitUtilities.c_L1_24",
+        end_line = 24,
+        src = Label(":src/ALAC/ALACBitUtilities.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/ALAC/ALACBitUtilities.h_L1_24",
+        end_line = 24,
+        src = Label(":src/ALAC/ALACBitUtilities.h"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/ALAC/ALACDecoder.h_L1_16",
+        end_line = 16,
+        src = Label(":src/ALAC/ALACDecoder.h"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/ALAC/EndianPortable.h_L1_24",
+        end_line = 24,
+        src = Label(":src/ALAC/EndianPortable.h"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/ALAC/LICENSE_L1_170",
+        end_line = 170,
+        src = Label(":src/ALAC/LICENSE"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/ALAC/ag_dec.c_L1_24",
+        end_line = 24,
+        src = Label(":src/ALAC/ag_dec.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/ALAC/ag_enc.c_L1_25",
+        end_line = 25,
+        src = Label(":src/ALAC/ag_enc.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/ALAC/aglib.h_L1_22",
+        end_line = 22,
+        src = Label(":src/ALAC/aglib.h"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/ALAC/alac_codec.h_L1_17",
+        end_line = 17,
+        src = Label(":src/ALAC/alac_codec.h"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/ALAC/alac_decoder.c_L1_57",
+        end_line = 57,
+        src = Label(":src/ALAC/alac_decoder.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/ALAC/alac_encoder.c_L1_17",
+        end_line = 17,
+        src = Label(":src/ALAC/alac_encoder.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/ALAC/dp_dec.c_L1_24",
+        end_line = 24,
+        src = Label(":src/ALAC/dp_dec.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/ALAC/dp_enc.c_L1_24",
+        end_line = 24,
+        src = Label(":src/ALAC/dp_enc.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/ALAC/dplib.h_L1_24",
+        end_line = 24,
+        src = Label(":src/ALAC/dplib.h"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/ALAC/matrix_dec.c_L1_25",
+        end_line = 25,
+        src = Label(":src/ALAC/matrix_dec.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/ALAC/matrix_enc.c_L1_25",
+        end_line = 25,
+        src = Label(":src/ALAC/matrix_enc.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/ALAC/matrixlib.h_L1_25",
+        end_line = 25,
+        src = Label(":src/ALAC/matrixlib.h"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/ALAC/shift.h_L1_16",
+        end_line = 16,
+        src = Label(":src/ALAC/shift.h"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/G72x/g721.c_L1_24",
+        end_line = 24,
+        src = Label(":src/G72x/g721.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/G72x/g723_16.c_L1_26",
+        end_line = 26,
+        src = Label(":src/G72x/g723_16.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/G72x/g72x.h_L1_16",
+        end_line = 16,
+        src = Label(":src/G72x/g72x.h"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/G72x/g72x_priv.h_L1_27_C13",
+        end_col = 13,
+        end_line = 27,
+        src = Label(":src/G72x/g72x_priv.h"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/GSM610/COPYRIGHT_L1_16",
+        end_line = 16,
+        src = Label(":src/GSM610/COPYRIGHT"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/GSM610/README_L11_35",
+        end_line = 35,
+        src = Label(":src/GSM610/README"),
+        start_line = 11,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/aiff.c_L1_32",
+        end_line = 32,
+        src = Label(":src/aiff.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/alac.c_L1_16",
+        end_line = 16,
+        src = Label(":src/alac.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/alaw.c_L1_16",
+        end_line = 16,
+        src = Label(":src/alaw.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/au.c_L1_39",
+        end_line = 39,
+        src = Label(":src/au.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/audio_detect.c_L1_16",
+        end_line = 16,
+        src = Label(":src/audio_detect.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/avr.c_L1_16",
+        end_line = 16,
+        src = Label(":src/avr.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/binheader_writef_check.py_L1_31",
+        end_line = 31,
+        src = Label(":src/binheader_writef_check.py"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/broadcast.c_L1_17",
+        end_line = 17,
+        src = Label(":src/broadcast.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/caf.c_L1_31",
+        end_line = 31,
+        src = Label(":src/caf.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/cart.c_L1_18",
+        end_line = 18,
+        src = Label(":src/cart.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/chanmap.c_L1_16",
+        end_line = 16,
+        src = Label(":src/chanmap.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/chanmap.h_L1_16",
+        end_line = 16,
+        src = Label(":src/chanmap.h"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/chunk.c_L1_17",
+        end_line = 17,
+        src = Label(":src/chunk.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/command.c_L1_16",
+        end_line = 16,
+        src = Label(":src/command.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/common.c_L1_16",
+        end_line = 16,
+        src = Label(":src/common.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/common.h_L1_16",
+        end_line = 16,
+        src = Label(":src/common.h"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/create_symbols_file.py_L1_33",
+        end_line = 33,
+        src = Label(":src/create_symbols_file.py"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/dither.c_L1_37",
+        end_line = 37,
+        src = Label(":src/dither.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/double64.c_L1_55",
+        end_line = 55,
+        src = Label(":src/double64.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/dwd.c_L1_66",
+        end_line = 66,
+        src = Label(":src/dwd.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/dwvw.c_L1_25",
+        end_line = 25,
+        src = Label(":src/dwvw.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/file_io.c_L1_17",
+        end_line = 17,
+        src = Label(":src/file_io.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/flac.c_L1_38",
+        end_line = 38,
+        src = Label(":src/flac.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/float32.c_L1_52",
+        end_line = 52,
+        src = Label(":src/float32.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/g72x.c_L1_16",
+        end_line = 16,
+        src = Label(":src/g72x.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/htk.c_L1_37",
+        end_line = 37,
+        src = Label(":src/htk.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/id3.c_L1_16",
+        end_line = 16,
+        src = Label(":src/id3.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/id3.h_L1_17",
+        end_line = 17,
+        src = Label(":src/id3.h"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/ima_adpcm.c_L1_16",
+        end_line = 16,
+        src = Label(":src/ima_adpcm.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/ima_oki_adpcm.c_L1_17",
+        end_line = 17,
+        src = Label(":src/ima_oki_adpcm.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/ima_oki_adpcm.h_L1_17",
+        end_line = 17,
+        src = Label(":src/ima_oki_adpcm.h"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/interleave.c_L1_16",
+        end_line = 16,
+        src = Label(":src/interleave.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/ircam.c_L1_28",
+        end_line = 28,
+        src = Label(":src/ircam.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/macos.c_L1_16",
+        end_line = 16,
+        src = Label(":src/macos.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/mat4.c_L1_36",
+        end_line = 36,
+        src = Label(":src/mat4.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/mat5.c_L1_72",
+        end_line = 72,
+        src = Label(":src/mat5.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/mpc2k.c_L1_16",
+        end_line = 16,
+        src = Label(":src/mpc2k.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/mpeg.c_L1_17",
+        end_line = 17,
+        src = Label(":src/mpeg.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/mpeg.h_L1_17",
+        end_line = 17,
+        src = Label(":src/mpeg.h"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/mpeg_decode.c_L1_17",
+        end_line = 17,
+        src = Label(":src/mpeg_decode.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/mpeg_l3_encode.c_L1_17",
+        end_line = 17,
+        src = Label(":src/mpeg_l3_encode.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/ms_adpcm.c_L1_16",
+        end_line = 16,
+        src = Label(":src/ms_adpcm.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/new.c_L1_55",
+        end_line = 55,
+        src = Label(":src/new.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/nist.c_L1_42",
+        end_line = 42,
+        src = Label(":src/nist.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/nms_adpcm.c_L1_17",
+        end_line = 17,
+        src = Label(":src/nms_adpcm.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/ogg.c_L1_24",
+        end_line = 24,
+        src = Label(":src/ogg.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/ogg.c_L25_52",
+        end_line = 52,
+        src = Label(":src/ogg.c"),
+        start_line = 25,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/ogg.h_L1_17",
+        end_line = 17,
+        src = Label(":src/ogg.h"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/ogg_opus.c_L1_23",
+        end_line = 23,
+        src = Label(":src/ogg_opus.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/ogg_pcm.c_L1_16",
+        end_line = 16,
+        src = Label(":src/ogg_pcm.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/ogg_vcomment.c_L1_17",
+        end_line = 17,
+        src = Label(":src/ogg_vcomment.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/ogg_vcomment.h_L1_17",
+        end_line = 17,
+        src = Label(":src/ogg_vcomment.h"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/ogg_vorbis.c_L1_24",
+        end_line = 24,
+        src = Label(":src/ogg_vorbis.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/ogg_vorbis.c_L25_52",
+        end_line = 52,
+        src = Label(":src/ogg_vorbis.c"),
+        start_line = 25,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/paf.c_L1_48",
+        end_line = 48,
+        src = Label(":src/paf.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/pvf.c_L1_36",
+        end_line = 36,
+        src = Label(":src/pvf.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/raw.c_L1_26",
+        end_line = 26,
+        src = Label(":src/raw.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/rf64.c_L1_38",
+        end_line = 38,
+        src = Label(":src/rf64.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/rx2.c_L1_39",
+        end_line = 39,
+        src = Label(":src/rx2.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/sd2.c_L1_41",
+        end_line = 41,
+        src = Label(":src/sd2.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/sds.c_L1_43",
+        end_line = 43,
+        src = Label(":src/sds.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/sf_unistd.h_L1_16",
+        end_line = 16,
+        src = Label(":src/sf_unistd.h"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/sfconfig.h_L1_16",
+        end_line = 16,
+        src = Label(":src/sfconfig.h"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/svx.c_L1_28",
+        end_line = 28,
+        src = Label(":src/svx.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/test_audio_detect.c_L1_76",
+        end_line = 76,
+        src = Label(":src/test_audio_detect.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/test_binheader_writef.c_L1_16",
+        end_line = 16,
+        src = Label(":src/test_binheader_writef.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/test_broadcast_var.c_L1_16",
+        end_line = 16,
+        src = Label(":src/test_broadcast_var.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/test_cart_var.c_L1_16",
+        end_line = 16,
+        src = Label(":src/test_cart_var.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/test_conversions.c_L1_16",
+        end_line = 16,
+        src = Label(":src/test_conversions.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/test_endswap.tpl_L1_43",
+        end_line = 43,
+        src = Label(":src/test_endswap.tpl"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/test_file_io.c_L1_16",
+        end_line = 16,
+        src = Label(":src/test_file_io.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/test_float.c_L1_16",
+        end_line = 16,
+        src = Label(":src/test_float.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/test_log_printf.c_L1_16",
+        end_line = 16,
+        src = Label(":src/test_log_printf.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/test_main.c_L1_16",
+        end_line = 16,
+        src = Label(":src/test_main.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/test_nms_adpcm.c_L1_17",
+        end_line = 17,
+        src = Label(":src/test_nms_adpcm.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/txw.c_L1_19",
+        end_line = 19,
+        src = Label(":src/txw.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/voc.c_L1_17",
+        end_line = 17,
+        src = Label(":src/voc.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/vox_adpcm.c_L1_16",
+        end_line = 16,
+        src = Label(":src/vox_adpcm.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/w64.c_L1_29",
+        end_line = 29,
+        src = Label(":src/w64.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/wav.c_L1_32",
+        end_line = 32,
+        src = Label(":src/wav.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/wavlike.c_L1_17",
+        end_line = 17,
+        src = Label(":src/wavlike.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/wavlike.h_L1_25",
+        end_line = 25,
+        src = Label(":src/wavlike.h"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/windows.c_L1_16",
+        end_line = 16,
+        src = Label(":src/windows.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/wve.c_L1_42",
+        end_line = 42,
+        src = Label(":src/wve.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    extract_lines(
+        name = name + "_extract/src/xi.c_L1_34",
+        end_line = 34,
+        src = Label(":src/xi.c"),
+        start_line = 1,
+        visibility = ["//visibility:private"],
+    )
+    concat_licenses(
+        name = name,
+        srcs = [
+            ":" + name + "_extract/COPYING_L1_456",
+            ":" + name + "_extract/COPYING_L458_502",
+            ":" + name + "_extract/Scripts/android-configure.sh_L1_27",
+            ":" + name + "_extract/docs/index.md_L9_C37_9",
+            ":" + name + "_extract/docs/index.md_L196_C36_322",
+            ":" + name + "_extract/examples/generate.c_L1_30",
+            ":" + name + "_extract/examples/list_formats.c_L1_30",
+            ":" + name + "_extract/examples/make_sine.c_L1_30",
+            ":" + name + "_extract/examples/sfprocess.c_L1_30",
+            ":" + name + "_extract/examples/sndfile-loopify.c_L1_30",
+            ":" + name + "_extract/examples/sndfile-to-text.c_L1_30",
+            ":" + name + "_extract/include/sndfile.h_L1_16",
+            ":" + name + "_extract/include/sndfile.hh_L1_30",
+            ":" + name + "_extract/programs/common.c_L1_31",
+            ":" + name + "_extract/programs/common.h_L1_30",
+            ":" + name + "_extract/programs/sndfile-cmp.c_L1_31",
+            ":" + name + "_extract/programs/sndfile-concat.c_L1_30",
+            ":" + name + "_extract/programs/sndfile-convert.c_L1_30",
+            ":" + name + "_extract/programs/sndfile-deinterleave.c_L1_30",
+            ":" + name + "_extract/programs/sndfile-interleave.c_L1_30",
+            ":" + name + "_extract/programs/sndfile-metadata-get.c_L1_31",
+            ":" + name + "_extract/programs/sndfile-metadata-set.c_L1_31",
+            ":" + name + "_extract/programs/sndfile-play.c_L1_30",
+            ":" + name + "_extract/programs/sndfile-salvage.c_L1_30",
+            ":" + name + "_extract/programs/test-sndfile-metadata-set.py_L1_31",
+            ":" + name + "_extract/src/ALAC/ALACAudioTypes.h_L1_16",
+            ":" + name + "_extract/src/ALAC/ALACBitUtilities.c_L1_24",
+            ":" + name + "_extract/src/ALAC/ALACBitUtilities.h_L1_24",
+            ":" + name + "_extract/src/ALAC/ALACDecoder.h_L1_16",
+            ":" + name + "_extract/src/ALAC/EndianPortable.h_L1_24",
+            ":" + name + "_extract/src/ALAC/LICENSE_L1_170",
+            ":" + name + "_extract/src/ALAC/ag_dec.c_L1_24",
+            ":" + name + "_extract/src/ALAC/ag_enc.c_L1_25",
+            ":" + name + "_extract/src/ALAC/aglib.h_L1_22",
+            ":" + name + "_extract/src/ALAC/alac_codec.h_L1_17",
+            ":" + name + "_extract/src/ALAC/alac_decoder.c_L1_57",
+            ":" + name + "_extract/src/ALAC/alac_encoder.c_L1_17",
+            ":" + name + "_extract/src/ALAC/dp_dec.c_L1_24",
+            ":" + name + "_extract/src/ALAC/dp_enc.c_L1_24",
+            ":" + name + "_extract/src/ALAC/dplib.h_L1_24",
+            ":" + name + "_extract/src/ALAC/matrix_dec.c_L1_25",
+            ":" + name + "_extract/src/ALAC/matrix_enc.c_L1_25",
+            ":" + name + "_extract/src/ALAC/matrixlib.h_L1_25",
+            ":" + name + "_extract/src/ALAC/shift.h_L1_16",
+            ":" + name + "_extract/src/G72x/g721.c_L1_24",
+            ":" + name + "_extract/src/G72x/g723_16.c_L1_26",
+            ":" + name + "_extract/src/G72x/g72x.h_L1_16",
+            ":" + name + "_extract/src/G72x/g72x_priv.h_L1_27_C13",
+            ":" + name + "_extract/src/GSM610/COPYRIGHT_L1_16",
+            ":" + name + "_extract/src/GSM610/README_L11_35",
+            ":" + name + "_extract/src/aiff.c_L1_32",
+            ":" + name + "_extract/src/alac.c_L1_16",
+            ":" + name + "_extract/src/alaw.c_L1_16",
+            ":" + name + "_extract/src/au.c_L1_39",
+            ":" + name + "_extract/src/audio_detect.c_L1_16",
+            ":" + name + "_extract/src/avr.c_L1_16",
+            ":" + name + "_extract/src/binheader_writef_check.py_L1_31",
+            ":" + name + "_extract/src/broadcast.c_L1_17",
+            ":" + name + "_extract/src/caf.c_L1_31",
+            ":" + name + "_extract/src/cart.c_L1_18",
+            ":" + name + "_extract/src/chanmap.c_L1_16",
+            ":" + name + "_extract/src/chanmap.h_L1_16",
+            ":" + name + "_extract/src/chunk.c_L1_17",
+            ":" + name + "_extract/src/command.c_L1_16",
+            ":" + name + "_extract/src/common.c_L1_16",
+            ":" + name + "_extract/src/common.h_L1_16",
+            ":" + name + "_extract/src/create_symbols_file.py_L1_33",
+            ":" + name + "_extract/src/dither.c_L1_37",
+            ":" + name + "_extract/src/double64.c_L1_55",
+            ":" + name + "_extract/src/dwd.c_L1_66",
+            ":" + name + "_extract/src/dwvw.c_L1_25",
+            ":" + name + "_extract/src/file_io.c_L1_17",
+            ":" + name + "_extract/src/flac.c_L1_38",
+            ":" + name + "_extract/src/float32.c_L1_52",
+            ":" + name + "_extract/src/g72x.c_L1_16",
+            ":" + name + "_extract/src/htk.c_L1_37",
+            ":" + name + "_extract/src/id3.c_L1_16",
+            ":" + name + "_extract/src/id3.h_L1_17",
+            ":" + name + "_extract/src/ima_adpcm.c_L1_16",
+            ":" + name + "_extract/src/ima_oki_adpcm.c_L1_17",
+            ":" + name + "_extract/src/ima_oki_adpcm.h_L1_17",
+            ":" + name + "_extract/src/interleave.c_L1_16",
+            ":" + name + "_extract/src/ircam.c_L1_28",
+            ":" + name + "_extract/src/macos.c_L1_16",
+            ":" + name + "_extract/src/mat4.c_L1_36",
+            ":" + name + "_extract/src/mat5.c_L1_72",
+            ":" + name + "_extract/src/mpc2k.c_L1_16",
+            ":" + name + "_extract/src/mpeg.c_L1_17",
+            ":" + name + "_extract/src/mpeg.h_L1_17",
+            ":" + name + "_extract/src/mpeg_decode.c_L1_17",
+            ":" + name + "_extract/src/mpeg_l3_encode.c_L1_17",
+            ":" + name + "_extract/src/ms_adpcm.c_L1_16",
+            ":" + name + "_extract/src/new.c_L1_55",
+            ":" + name + "_extract/src/nist.c_L1_42",
+            ":" + name + "_extract/src/nms_adpcm.c_L1_17",
+            ":" + name + "_extract/src/ogg.c_L1_24",
+            ":" + name + "_extract/src/ogg.c_L25_52",
+            ":" + name + "_extract/src/ogg.h_L1_17",
+            ":" + name + "_extract/src/ogg_opus.c_L1_23",
+            ":" + name + "_extract/src/ogg_pcm.c_L1_16",
+            ":" + name + "_extract/src/ogg_vcomment.c_L1_17",
+            ":" + name + "_extract/src/ogg_vcomment.h_L1_17",
+            ":" + name + "_extract/src/ogg_vorbis.c_L1_24",
+            ":" + name + "_extract/src/ogg_vorbis.c_L25_52",
+            ":" + name + "_extract/src/paf.c_L1_48",
+            ":" + name + "_extract/src/pvf.c_L1_36",
+            ":" + name + "_extract/src/raw.c_L1_26",
+            ":" + name + "_extract/src/rf64.c_L1_38",
+            ":" + name + "_extract/src/rx2.c_L1_39",
+            ":" + name + "_extract/src/sd2.c_L1_41",
+            ":" + name + "_extract/src/sds.c_L1_43",
+            ":" + name + "_extract/src/sf_unistd.h_L1_16",
+            ":" + name + "_extract/src/sfconfig.h_L1_16",
+            ":" + name + "_extract/src/svx.c_L1_28",
+            ":" + name + "_extract/src/test_audio_detect.c_L1_76",
+            ":" + name + "_extract/src/test_binheader_writef.c_L1_16",
+            ":" + name + "_extract/src/test_broadcast_var.c_L1_16",
+            ":" + name + "_extract/src/test_cart_var.c_L1_16",
+            ":" + name + "_extract/src/test_conversions.c_L1_16",
+            ":" + name + "_extract/src/test_endswap.tpl_L1_43",
+            ":" + name + "_extract/src/test_file_io.c_L1_16",
+            ":" + name + "_extract/src/test_float.c_L1_16",
+            ":" + name + "_extract/src/test_log_printf.c_L1_16",
+            ":" + name + "_extract/src/test_main.c_L1_16",
+            ":" + name + "_extract/src/test_nms_adpcm.c_L1_17",
+            ":" + name + "_extract/src/txw.c_L1_19",
+            ":" + name + "_extract/src/voc.c_L1_17",
+            ":" + name + "_extract/src/vox_adpcm.c_L1_16",
+            ":" + name + "_extract/src/w64.c_L1_29",
+            ":" + name + "_extract/src/wav.c_L1_32",
+            ":" + name + "_extract/src/wavlike.c_L1_17",
+            ":" + name + "_extract/src/wavlike.h_L1_25",
+            ":" + name + "_extract/src/windows.c_L1_16",
+            ":" + name + "_extract/src/wve.c_L1_42",
+            ":" + name + "_extract/src/xi.c_L1_34",
+        ],
+        visibility = visibility,
+    )
+
+concat_package_licenses = macro(
+    doc = "Extracts and concatenates all detected license texts in the package.",
+    implementation = _concat_package_licenses_impl,
+)
diff --git a/make_lite.py b/make_lite.py
new file mode 100644
index 0000000..eee69fc
--- /dev/null
+++ b/make_lite.py
@@ -0,0 +1,491 @@
+#!/usr/bin/python
+
+import commands, os, re, string, sys, time
+
+def count_enclosed_functions (source):
+	func_count = 0
+	open_brace = 0
+	close_brace = 0
+	for ch in source:
+		if ch == '{':
+			open_brace += 1
+		elif ch == '}':
+			close_brace += 1
+			if open_brace == close_brace:
+				func_count += 1
+		if open_brace < close_brace:
+			print "count_enclosed_functions : open_brace < close_brace"
+			return -1
+	return func_count
+
+def find_function_prototype (source, proto_name):
+	proto_re = "(^[a-zA-Z_ \t]+\s+%s[^a-zA-Z0-9_]\s*\([^\)]+\)\s+;\n)" % (proto_name)
+	proto_result = re.search (proto_re, source, re.MULTILINE | re.DOTALL)
+	if not proto_result:
+		return None
+	proto_text = proto_result.groups ()[0]
+	return proto_text
+
+def find_function_definition (source, func_name):
+	func_re = "(\n[a-zA-Z_ \t]+\n%s[^a-zA-Z0-9_].* /\* %s \*/\n)" % (func_name, func_name)
+	func_result = re.search (func_re, source, re.MULTILINE | re.DOTALL)
+	if not func_result:
+		sys.exit (1)
+		return None
+	func_text = func_result.groups ()[0]
+
+	# Now to check that we only have one enclosing function.
+	func_count = count_enclosed_functions (func_text)
+	if func_count != 1:
+		return None
+	return func_text
+
+def find_include (source, inc_name):
+	inc_re = "(^#include\s+[\<\"]%s[\"\>]\s*)" % inc_name
+	inc_result = re.search (inc_re, source, re.MULTILINE | re.DOTALL)
+	if not inc_result:
+		return None
+	inc_text = inc_result.groups ()[0]
+	return inc_text
+
+def find_assign_statement (source, var_name):
+	var_re = "(^\s+%s\s*=[^;]+;)" % var_name
+	var_result = re.search (var_re, source, re.MULTILINE | re.DOTALL)
+	if not var_result:
+		return None
+	assign_text = var_result.groups ()[0]
+	return assign_text
+
+#--------------------------------------------------------------------------------
+
+def remove_include (source, inc_name):
+	inc_text = find_include (source, inc_name)
+	if not inc_text:
+		print "remove_include : include '%s' not found. Exiting." % inc_name
+		sys.exit (1)
+
+	source = string.replace (source, inc_text, "")
+	return source
+
+def remove_assign (source, assign_name):
+	assign_text = find_assign (source, inc_name)
+	if not inc_text:
+		print "remove_include : include '%s' not found. Exiting." % inc_name
+		sys.exit (1)
+
+	source = string.replace (source, inc_text, "")
+	return source
+
+def remove_prototype (source, proto_name):
+	proto_text = find_function_prototype (source, proto_name)
+	if not proto_text:
+		print "remove_prototype : prototype '%s' not found. Exiting." % proto_name
+		sys.exit (1)
+
+	source = string.replace (source, proto_text, "")
+	return source
+
+def remove_function (source, func_name):
+	func_text = find_function_definition (source, func_name)
+	if not func_text:
+		print "remove_function : function '%s' not found. Exiting." % func_name
+		sys.exit (1)
+
+	source = string.replace (source, func_text, "/* Function %s() removed here. */\n" % func_name)
+	return source
+
+def remove_all_assignments (source, var):
+	count = 0
+	while 1:
+		assign_text = find_assign_statement (source, var)
+		if not assign_text:
+			if count != 0:
+				break
+			print "remove_all_assignments : variable '%s' not found. Exiting." % var
+			sys.exit (1)
+
+		source = string.replace (source, assign_text, "")
+		count += 1
+	return source
+
+
+
+#----------------------------------------------------------------
+
+def remove_funcs_and_protos_from_file (filename, func_list):
+	source_code = open (filename, 'r').read ()
+
+	for func in func_list:
+		source_code = remove_prototype (source_code, func) ;
+		source_code = remove_function (source_code, func) ;
+	open (filename, 'w').write (source_code)
+
+def remove_funcs_from_file (filename, func_list):
+	source_code = open (filename, 'r').read ()
+
+	for func in func_list:
+		source_code = remove_function (source_code, func) ;
+	open (filename, 'w').write (source_code)
+
+def remove_protos_from_file (filename, func_list):
+	source_code = open (filename, 'r').read ()
+
+	for func in func_list:
+		source_code = remove_prototype (source_code, func) ;
+	open (filename, 'w').write (source_code)
+
+def remove_includes_from_file (filename, inc_list):
+	source_code = open (filename, 'r').read ()
+
+	for inc in inc_list:
+		source_code = remove_include (source_code, inc) ;
+	open (filename, 'w').write (source_code)
+
+def remove_all_assignments_from_file (filename, var_list):
+	source_code = open (filename, 'r').read ()
+
+	for var in var_list:
+		source_code = remove_all_assignments (source_code, var) ;
+	open (filename, 'w').write (source_code)
+
+def remove_comment_start_end (filename, start_comment, end_comment):
+	source_code = open (filename, 'r').read ()
+
+	while 1:
+		start_index = string.find (source_code, start_comment)
+		end_index = string.find (source_code, end_comment)
+		if start_index < 0 or end_index < start_index:
+			break
+		end_index += len (end_comment)
+		source_code = source_code [:start_index-1] + source_code [end_index:] ;
+
+	open (filename, 'w').write (source_code)
+
+def remove_strings_from_file (filename, str_list):
+	file_text = open (filename, 'r').read ()
+	for current_str in str_list:
+		file_text = string.replace (file_text, current_str, '')
+	open (filename, 'w').write (file_text)
+
+def string_replace_in_file (filename, from_str, to_str):
+	file_text = open (filename, 'r').read ()
+	file_text = string.replace (file_text, from_str, to_str)
+	open (filename, 'w').write (file_text)
+
+def remove_regex_from_file (filename, regex_list):
+	file_text = open (filename, 'r').read ()
+	for regex in regex_list:
+		file_text = re.sub (regex, '', file_text, re.MULTILINE | re.DOTALL)
+	open (filename, 'w').write (file_text)
+
+#==========================================================================
+
+def find_configure_version (filename):
+	# AM_INIT_AUTOMAKE(libsndfile,0.0.21pre6)
+	file = open (filename)
+	while 1:
+		line = file.readline ()
+		if re.search ("AC_INIT", line):
+			x = re.sub ("[^\(]+\(", "", line)
+			x = re.sub ("\).*\n", "", x)
+			x = string.split (x, ",")
+			package = x [0]
+			version = x [1]
+			break
+	file.close ()
+	# version = re.escape (version)
+	return package, version
+
+def fix_configure_ac_file (filename):
+	data = open (filename, 'r').read ()
+	data = string.replace (data, "AM_INIT_AUTOMAKE(libsndfile,", "AM_INIT_AUTOMAKE(libsndfile_lite,", 1)
+
+	file = open (filename, 'w')
+	file.write (data)
+	file.close ()
+
+
+def make_dist_file (package, version):
+	print "Making dist file."
+	tar_gz_file = "%s-%s.tar.gz" % (package, version)
+	if os.path.exists (tar_gz_file):
+		return
+	if os.system ("make dist"):
+		sys.exit (1)
+	return
+
+def delete_files (file_list):
+	for file_name in file_list:
+		os.remove (file_name)
+
+#=======================================================================
+
+source_dir = os.getcwd ()
+
+conf_package, conf_version =  find_configure_version ('configure.ac')
+
+package_version = "%s-%s" % (conf_package, conf_version)
+lite_version = "%s_lite-%s" % (conf_package, conf_version)
+
+os.system ("rm -rf %s%s.tar.gz" % (source_dir, package_version))
+
+os.system ("make dist")
+
+make_dist_file (conf_package, conf_version)
+
+os.chdir ("/tmp")
+
+print "Uncompressing .tar.gz file."
+os.system ("rm -rf %s" % package_version)
+if os.system ("tar zxf %s/%s.tar.gz" % (source_dir, package_version)):
+	sys.exit (1)
+
+
+print "Renaming to libsndfile_lite."
+os.system ("rm -rf %s" % lite_version)
+os.rename (package_version, lite_version)
+
+print "Changing into libsndfile_lite directory."
+os.chdir (lite_version)
+
+print "Removing un-neeed directories."
+delete_dirs = [ 'src/G72x' ]
+
+for dir_name in delete_dirs:
+	os.system ("rm -rf %s" % dir_name)
+
+print "Removing un-needed files."
+delete_files ([ 'src/ircam.c', 'src/nist.c',
+	'src/ima_adpcm.c', 'src/ms_adpcm.c', 'src/au_g72x.c',
+	'src/mat4.c', 'src/mat5.c', 'src/dwvw.c', 'src/paf.c',
+	'src/ogg.c', 'src/pvf.c', 'src/xi.c', 'src/htk.c',
+	'src/sd2.c', 'src/rx2.c', 'src/txw.c', 'src/wve.c',
+	'src/dwd.c', 'src/svx.c', 'src/voc.c', 'src/vox_adpcm.c',
+	'src/sds.c'
+	])
+
+
+print "Hacking 'configure.ac' and 'src/Makefile.am'."
+remove_strings_from_file ('configure.ac', [ 'src/G72x/Makefile' ])
+remove_strings_from_file ('src/Makefile.am', [ 'G72x/libg72x.la', 'G72x',
+		'ircam.c', 'nist.c', 'ima_adpcm.c', 'ms_adpcm.c', 'au_g72x.c', 'mat4.c', 
+		'mat5.c', 'dwvw.c',  'paf.c', 'ogg.c', 'pvf.c', 'xi.c', 'htk.c', 
+		'sd2.c', 'rx2.c', 'txw.c', 'wve.c', 'dwd.c', 'svx.c', 'voc.c', 
+		'vox_adpcm.c', 'sds.c'
+		])
+
+#----------------------------------------------------------------------------
+
+print "Hacking header files."
+
+remove_protos_from_file ('src/common.h', [	'xi_open', 'sd2_open', 'ogg_open',
+	'dwvw_init', 'paf_open', 'svx_open', 'nist_open', 'rx2_open', 'mat4_open',
+	'voc_open', 'txw_open', 'dwd_open', 'htk_open', 'wve_open', 'mat5_open',
+	'pvf_open', 'ircam_open', 'sds_open',
+	'float32_init', 'double64_init', 'aiff_ima_init', 'vox_adpcm_init',
+	'wav_w64_ima_init', 'wav_w64_msadpcm_init'
+	])
+
+remove_protos_from_file ('src/au.h',
+		[ 'au_g72x_reader_init', 'au_g72x_writer_init' ])
+
+remove_protos_from_file ('src/wav_w64.h', [ 'msadpcm_write_adapt_coeffs' ])
+
+#----------------------------------------------------------------------------
+
+print "Hacking case statements."
+
+remove_comment_start_end ('src/sndfile.c', '/* Lite remove start */' , '/* Lite remove end */')
+remove_comment_start_end ('src/aiff.c', '/* Lite remove start */' , '/* Lite remove end */')
+remove_comment_start_end ('src/au.c', '/* Lite remove start */' , '/* Lite remove end */')
+remove_comment_start_end ('src/raw.c', '/* Lite remove start */' , '/* Lite remove end */')
+remove_comment_start_end ('src/w64.c', '/* Lite remove start */' , '/* Lite remove end */')
+remove_comment_start_end ('src/wav.c', '/* Lite remove start */' , '/* Lite remove end */')
+remove_comment_start_end ('src/double64.c', '/* Lite remove start */' , '/* Lite remove end */')
+remove_comment_start_end ('src/float32.c', '/* Lite remove start */' , '/* Lite remove end */')
+
+
+#----------------------------------------------------------------------------
+
+print "Hacking src/pcm.c."
+remove_funcs_from_file ('src/pcm.c', [
+	'f2sc_array', 'f2sc_clip_array', 'f2uc_array', 'f2uc_clip_array',
+	'f2bes_array', 'f2bes_clip_array', 'f2les_array', 'f2les_clip_array',
+	'f2let_array', 'f2let_clip_array', 'f2bet_array', 'f2bet_clip_array',
+	'f2bei_array', 'f2bei_clip_array', 'f2lei_array', 'f2lei_clip_array',
+	'd2sc_array', 'd2sc_clip_array', 'd2uc_array', 'd2uc_clip_array',
+	'd2bes_array', 'd2bes_clip_array', 'd2les_array', 'd2les_clip_array',
+	'd2let_array', 'd2let_clip_array', 'd2bet_array', 'd2bet_clip_array',
+	'd2bei_array', 'd2bei_clip_array', 'd2lei_array', 'd2lei_clip_array',
+	])
+
+remove_funcs_and_protos_from_file ('src/pcm.c', [
+	'pcm_read_sc2f', 'pcm_read_uc2f', 'pcm_read_les2f', 'pcm_read_bes2f',
+	'pcm_read_let2f', 'pcm_read_bet2f', 'pcm_read_lei2f', 'pcm_read_bei2f',
+	'pcm_read_sc2d', 'pcm_read_uc2d', 'pcm_read_les2d', 'pcm_read_bes2d',
+	'pcm_read_let2d', 'pcm_read_bet2d', 'pcm_read_lei2d', 'pcm_read_bei2d',
+	'pcm_write_f2sc', 'pcm_write_f2uc', 'pcm_write_f2bes', 'pcm_write_f2les',
+	'pcm_write_f2bet', 'pcm_write_f2let', 'pcm_write_f2bei', 'pcm_write_f2lei',
+	'pcm_write_d2sc', 'pcm_write_d2uc', 'pcm_write_d2bes', 'pcm_write_d2les',
+	'pcm_write_d2bet', 'pcm_write_d2let', 'pcm_write_d2bei', 'pcm_write_d2lei',
+
+	'sc2f_array', 'uc2f_array', 'bes2f_array', 'les2f_array',
+	'bet2f_array', 'let2f_array', 'bei2f_array', 'lei2f_array',
+	'sc2d_array', 'uc2d_array', 'bes2d_array', 'les2d_array',
+	'bet2d_array', 'let2d_array', 'bei2d_array', 'lei2d_array'
+	])
+
+remove_includes_from_file ('src/pcm.c', [ 'float_cast.h' ])
+remove_all_assignments_from_file ('src/pcm.c', [
+	'psf-\>write_float', 'psf\-\>write_double',
+	'psf-\>read_float', 'psf\-\>read_double' ])
+
+#----------------------------------------------------------------------------
+print "Hacking src/ulaw.c."
+remove_funcs_and_protos_from_file ('src/ulaw.c', [
+	'ulaw_read_ulaw2f', 'ulaw_read_ulaw2d',
+	'ulaw_write_f2ulaw', 'ulaw_write_d2ulaw',
+	'ulaw2f_array', 'ulaw2d_array', 'f2ulaw_array', 'd2ulaw_array'
+	])
+
+remove_includes_from_file ('src/ulaw.c', [ 'float_cast.h' ])
+remove_all_assignments_from_file ('src/ulaw.c', [
+	'psf-\>write_float', 'psf\-\>write_double',
+	'psf-\>read_float', 'psf\-\>read_double' ])
+
+#----------------------------------------------------------------------------
+
+print "Hacking src/alaw.c."
+remove_funcs_and_protos_from_file ('src/alaw.c', [
+	'alaw_read_alaw2f', 'alaw_read_alaw2d',
+	'alaw_write_f2alaw', 'alaw_write_d2alaw',
+	'alaw2f_array', 'alaw2d_array', 'f2alaw_array', 'd2alaw_array'
+	])
+
+remove_includes_from_file ('src/alaw.c', [ 'float_cast.h' ])
+remove_all_assignments_from_file ('src/alaw.c', [
+	'psf-\>write_float', 'psf\-\>write_double',
+	'psf-\>read_float', 'psf\-\>read_double' ])
+
+#----------------------------------------------------------------------------
+
+print "Hacking src/gsm610.c."
+remove_funcs_and_protos_from_file ('src/gsm610.c', [
+	'gsm610_read_f', 'gsm610_read_d', 'gsm610_write_f', 'gsm610_write_d'
+	])
+
+remove_includes_from_file ('src/gsm610.c', [ 'float_cast.h' ])
+remove_all_assignments_from_file ('src/gsm610.c', [
+	'psf-\>write_float', 'psf\-\>write_double',
+	'psf-\>read_float', 'psf\-\>read_double' ])
+
+#----------------------------------------------------------------------------
+
+print "Hacking src/float32.c."
+
+# string_replace_in_file ('src/float32.c', '"float_cast.h"', '<math.h>')
+remove_funcs_from_file ('src/float32.c', [ 'float32_init'	])
+
+remove_funcs_and_protos_from_file ('src/float32.c', [
+	'host_read_f2s', 'host_read_f2i', 'host_read_f', 'host_read_f2d', 
+	'host_write_s2f', 'host_write_i2f', 'host_write_f', 'host_write_d2f', 
+	'f2s_array', 'f2i_array', 'f2d_array', 's2f_array', 'i2f_array', 'd2f_array', 
+	'float32_peak_update', 
+	'replace_read_f2s', 'replace_read_f2i', 'replace_read_f', 'replace_read_f2d', 
+	'replace_write_s2f', 'replace_write_i2f', 'replace_write_f', 'replace_write_d2f', 
+	'bf2f_array', 'f2bf_array', 
+	'float32_get_capability', 
+	])
+
+#----------------------------------------------------------------------------
+
+print "Hacking src/double64.c."
+remove_funcs_from_file ('src/double64.c', [ 'double64_init'	])
+
+remove_funcs_and_protos_from_file ('src/double64.c', [
+	'host_read_d2s', 'host_read_d2i', 'host_read_d2f', 'host_read_d',
+	'host_write_s2d', 'host_write_i2d', 'host_write_f2d', 'host_write_d',
+	'd2s_array', 'd2i_array', 'd2f_array',
+	's2d_array', 'i2d_array', 'f2d_array',
+	'double64_peak_update', 'double64_get_capability',
+	'replace_read_d2s', 'replace_read_d2i', 'replace_read_d2f', 'replace_read_d',
+	'replace_write_s2d', 'replace_write_i2d', 'replace_write_f2d', 'replace_write_d',
+	'd2bd_read', 'bd2d_write'
+	])
+
+#----------------------------------------------------------------------------
+
+print "Hacking test programs."
+delete_files ([ 'tests/dwvw_test.c', 'tests/floating_point_test.c', 
+	'tests/dft_cmp.c', 'tests/peak_chunk_test.c',
+	'tests/scale_clip_test.tpl', 'tests/scale_clip_test.def'
+	])
+
+remove_comment_start_end ('tests/write_read_test.def', '/* Lite remove start */', '/* Lite remove end */')
+remove_comment_start_end ('tests/write_read_test.tpl', '/* Lite remove start */', '/* Lite remove end */')
+
+remove_comment_start_end ('tests/Makefile.am', '# Lite remove start', '# Lite remove end')
+
+remove_strings_from_file ('tests/Makefile.am', [ 
+	'scale_clip_test.tpl', 'scale_clip_test.def',
+	'\n\t./dwvw_test',
+	'\n\t./floating_point_test', '\n\t./scale_clip_test', 
+	'\n\t./peak_chunk_test aiff', '\n\t./peak_chunk_test wav',
+	'\n\t./command_test norm', '\n\t./command_test peak',
+	'\n\t./lossy_comp_test wav_ima', '\n\t./lossy_comp_test wav_msadpcm',
+	'\n\t./lossy_comp_test au_g721', '\n\t./lossy_comp_test au_g723',
+	'\n\t./lossy_comp_test vox_adpcm', 
+	'\n\t./lossy_comp_test w64_ima', '\n\t./lossy_comp_test w64_msadpcm',
+	'peak_chunk_test', 'dwvw_test', 'floating_point_test', 'scale_clip_test',
+	
+	'paf-tests', 'svx-tests', 'nist-tests', 'ircam-tests', 'voc-tests',
+	'mat4-tests', 'mat5-tests', 'pvf-tests', 'xi-tests', 'htk-tests',
+	'sds-tests'
+	])
+
+remove_comment_start_end ('tests/pcm_test.c', '/* Lite remove start */', '/* Lite remove end */')
+remove_funcs_and_protos_from_file ('tests/pcm_test.c', [
+	'pcm_test_float', 'pcm_test_double' 
+	])
+
+remove_comment_start_end ('tests/lossy_comp_test.c', '/* Lite remove start */', '/* Lite remove end */')
+remove_funcs_and_protos_from_file ('tests/lossy_comp_test.c', [
+	'lcomp_test_float', 'lcomp_test_double', 'sdlcomp_test_float', 'sdlcomp_test_double',
+	'smoothed_diff_float', 'smoothed_diff_double'
+	])
+
+remove_comment_start_end ('tests/multi_file_test.c', '/* Lite remove start */', '/* Lite remove end */')
+
+remove_strings_from_file ('tests/stdio_test.c', [ 
+	'"paf",', '"svx",', '"nist",', '"ircam",', '"voc",', '"mat4",', '"mat5",', '"pvf",'
+	])
+
+remove_comment_start_end ('tests/pipe_test.c', '/* Lite remove start */', '/* Lite remove end */')
+
+#----------------------------------------------------------------------------
+
+print "Fixing configure.ac file."
+fix_configure_ac_file ('configure.ac')
+
+print "Building and testing source."
+	# Try --disable-shared --disable-gcc-opt
+if os.system ("./reconfigure.mk && ./configure --disable-shared --disable-gcc-opt && make check"):
+	os.system ('PS1="FIX > " bash --norc')
+	sys.exit (1)
+
+print "Making distcheck"
+if os.system ("make distcheck"):
+	os.system ('PS1="FIX > " bash --norc')
+	sys.exit (1)
+
+print "Copying tarball"
+if os.system ("cp %s.tar.gz %s" % (lite_version, source_dir)):
+	print "??? %s.tar.gz ???" % lite_version
+	os.system ('PS1="FIX > " bash --norc')
+	sys.exit (1)
+
+os.chdir (source_dir)
+
+os.system ("rm -rf /tmp/%s" % lite_version)
+
+print "Done."
diff --git a/man/sndfile-cmp.1 b/man/sndfile-cmp.1
new file mode 100644
index 0000000..88aaafe
--- /dev/null
+++ b/man/sndfile-cmp.1
@@ -0,0 +1,29 @@
+.Dd November 2, 2014
+.Dt SNDFILE-CMP 1
+.Os
+.Sh NAME
+.Nm sndfile-cmp
+.Nd compare two audio files
+.Sh SYNOPSIS
+.Nm sndfile-cmp
+.Ar file1
+.Ar file2
+.Sh DESCRIPTION
+.Nm
+compares the audio data of two sound files.
+For two files to compare as being the same, their channel counts, sample rate,
+audio data lengths and actual audio data must match.
+Other differences such as string metadata like song title, artist etc and their
+presence or absence are ignored.
+.Sh EXIT STATUS
+.Bl -tag -width 1n -compact
+.It 0
+The audio data is the same.
+.It 1
+The audio data differs.
+.El
+.Sh SEE ALSO
+.Lk http://libsndfile.github.io/libsndfile/
+.Sh AUTHORS
+.An Conrad Parker Aq Mt conrad@metadecks.org
+.An Erik de Castro Lopo Aq Mt erikd@mega-nerd.com
diff --git a/man/sndfile-concat.1 b/man/sndfile-concat.1
new file mode 100644
index 0000000..3ba34c7
--- /dev/null
+++ b/man/sndfile-concat.1
@@ -0,0 +1,28 @@
+.Dd November 2, 2014
+.Dt SNDFILE-CONCAT 1
+.Os
+.Sh NAME
+.Nm sndfile-concat
+.Nd concatenate audio data from two or more audio files
+.Sh SYNOPSIS
+.Nm sndfile-concat
+.Ar infile1
+.Ar infile2
+.Ar ...
+.Ar outfile
+.Sh DESCRIPTION
+.Nm
+generates a new output file by concatenating the audio data
+of two or more input files. The encoding of the output file
+is the encoding used in
+.Ar infile1 .
+Audio data from the subsequent files are converted to this encoding.
+The only restriction is that the files must have
+the same number of channels.
+The output file is overwritten if it already exists.
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Lk http://libsndfile.github.io/libsndfile/
+.Sh AUTHORS
+.An Erik de Castro Lopo Aq Mt erikd@mega-nerd.com
diff --git a/man/sndfile-convert.1 b/man/sndfile-convert.1
new file mode 100644
index 0000000..e7156ee
--- /dev/null
+++ b/man/sndfile-convert.1
@@ -0,0 +1,160 @@
+.Dd November 2, 2014
+.Dt SNDFILE-CONVERT 1
+.Os
+.Sh NAME
+.Nm sndfile-convert
+.Nd convert sound files from one format to another
+.Sh SYNOPSIS
+.Nm sndfile-convert
+.Op Fl override-sample-rate Ns = Ns Ar rate
+.Op Fl endian Ns = Ns Cm little | big | cpu
+.Op Fl normalize
+.Op Ar encoding
+.Ar input
+.Ar output
+.Sh DESCRIPTION
+.Nm
+converts sound files from one audio format to another.
+The output file is overwritten it it already exists.
+.Ss Formats
+The format of the output file is determined by the filename extension.
+The following file formats are currently recognized:
+.Pp
+.Bl -tag -compact -width ircam
+.It wav
+WAV (Microsoft)
+.It aif
+AIFF (Apple/SGI)
+.It au
+AU (Sun/NeXT)
+.It snd
+AU (Sun/NeXT)
+.It raw
+RAW (header-less)
+.It gsm
+RAW (header-less)
+.It vox
+RAW (header-less)
+.It paf
+PAF (Ensoniq PARIS, big-endian)
+.It fap
+PAF (Ensoniq PARIS, little-endian)
+.It svx
+IFF (Amiga IFF/SVX8/SV16)
+.It nist
+SPHERE (NIST SPeech HEader Resources)
+.It sph
+SPHERE (NIST SPeech HEader Resources)
+.It voc
+VOC (Creative Labs)
+.It ircam
+SF (Berkeley/IRCAM/CARL)
+.It sf
+SF (Berkeley/IRCAM/CARL)
+.It w64
+W64 (SoundFoundry WAVE 64)
+.It mat
+MAT4 (GNU Octave 2.0 / Matlab 4.2)
+.It mat4
+MAT4 (GNU Octave 2.0 / Matlab 4.2)
+.It mat5
+MAT5 (GNU Octave 2.1 / Matlab 5.0)
+.It pvf
+PVF (Portable Voice Format)
+.It xi
+XI (FastTracker 2)
+.It htk
+HTK (HMM Tool Kit)
+.It sds
+SDS (Midi Sample Dump Standard)
+.It avr
+AVR (Audio Visual Research)
+.It wavex
+WAVEX (MS WAVE with WAVEFORMATEX)
+.It sd2
+SD2 (Sound Designer II)
+.It flac
+FLAC (FLAC Lossless Audio Codec)
+.It caf
+CAF (Apple Core Audio File)
+.It wve
+WVE (Psion Series 3)
+.It prc
+WVE (Psion Series 3)
+.It ogg
+OGG (OGG Container format)
+.It oga
+OGG (OGG Container format)
+.It mpc
+MPC (Akai MPC 2k)
+.It rf64
+RF64 (RIFF 64)
+.El
+.Ss Options
+The following options are recoginzed:
+.Pp
+.Bl -tag -compact -width "override-sample-rate=XXXXX"
+.It Fl override-sample-rate Ns = Ns Ar rate
+Make the input use sample rate of
+.Ar rate
+Hz.
+.It Fl endian Ns = Ns Cm little
+Make the output file use little endian data.
+.It Fl endian Ns = Ns Cm big
+Make the output file use big endian data.
+.It Fl endian Ns = Ns Cm cpu
+Make the output file use CPU endianness.
+.It Fl normalize
+Normalize the audio data in the output file.
+.El
+.Ss Encodings
+The optional
+.Ar encoding
+parameter allows setting of the data encoding for the output file.
+The following encodings are currently supported:
+.Pp
+.Bl -tag -compact -width ima-adpcmXX
+.It Fl pcms8
+signed 8 bit pcm
+.It Fl pcmu8
+unsigned 8 bit pcm
+.It Fl pcm16
+16 bit pcm
+.It Fl pcm24
+24 bit pcm
+.It Fl pcm32
+32 bit pcm
+.It Fl float32
+32 bit floating point
+.It Fl ulaw
+ULAW
+.It Fl alaw
+ALAW
+.It Fl ima-adpcm
+IMA ADPCM (WAV only)
+.It Fl ms-adpcm
+MS ADPCM (WAV only)
+.It Fl gsm610
+GSM6.10 (WAV only)
+.It Fl dwvw12
+12 bit DWVW (AIFF only)
+.It Fl dwvw16
+16 bit DWVW (AIFF only)
+.It Fl dwvw24
+24 bit DWVW (AIFF only)
+.It Fl vorbis
+Vorbis (OGG only)
+.El
+.Pp
+If no encoding is specified for the output file,
+.Nm
+will try to use the encoding of the input file.
+This will not always work as most container formats
+(e.g. WAV, AIFF etc) only support a small subset of encodings
+(e.g. 16 bit PCM, a-law, Vorbis etc).
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Lk http://libsndfile.github.io/libsndfile/
+.Sh AUTHORS
+.An Erik de Castro Lopo Aq Mt erikd@mega-nerd.com .
diff --git a/man/sndfile-info.1 b/man/sndfile-info.1
new file mode 100644
index 0000000..a8c3749
--- /dev/null
+++ b/man/sndfile-info.1
@@ -0,0 +1,34 @@
+.Dd November 2, 2014
+.Dt SNDFILE-INFO 1
+.Os
+.Sh NAME
+.Nm sndfile-info
+.Nd display information about sound files
+.Sh SYNOPSIS
+.Nm sndfile-info
+.Op Fl -broadcast
+.Op Fl -cart
+.Op Fl -channel-map
+.Op Fl -instrument
+.Ar
+.Sh DESCRIPTION
+.Nm
+displays basic information about sound files
+such as format, number of channels, samplerate, and length.
+The following options are recognized:
+.Pp
+.Bl -tag -compact -width channelmapXXXX
+.It Fl -broadcast
+Display broadcast (BWF) info.
+.It Fl -cart
+Display the cart chunk of a WAV (or related) file.
+.It Fl -channel-map
+Display channel map.
+.It Fl -instrument
+Display instrument info:
+a base note, gain, velocity, key, and loop points.
+.El
+.Sh SEE ALSO
+.Lk http://libsndfile.github.io/libsndfile/
+.Sh AUTHORS
+.An Erik de Castro Lopo Aq Mt erikd@mega-nerd.com .
diff --git a/man/sndfile-interleave.1 b/man/sndfile-interleave.1
new file mode 100644
index 0000000..0cf7076
--- /dev/null
+++ b/man/sndfile-interleave.1
@@ -0,0 +1,62 @@
+.Dd November 2, 2014
+.Dt SNDFILE-INTERLEAVE 1
+.Os
+.Sh NAME
+.Nm sndfile-interleave ,
+.Nm sndfile-deinterleave
+.Nd convert mono files into a multi-channel file and vice versa
+.Sh SYNOPSIS
+.Nm sndfile-interleave
+.Ar input1
+.Ar input2
+.Ar ...
+.Fl o Ar output
+.Nm sndfile-deinterleave
+.Ar file
+.Sh DESCRIPTION
+.Nm sndfile-interleave
+creates a multi-channel file taking audio data
+from two or more mono files as individual channels.
+The format of the output file is determined by its filename suffix.
+The audio parameters of the output file will be made so that
+the format can accommodate each of the mono inputs;
+for example, the samplerate will be the maximal samplerate
+occurring in the inputs.
+The output file will be overwritten if it already exists.
+.Pp
+.Nm sndfile-deinterleave
+creates two or more mono files from a multi-channel audio file,
+containing data from the individual channels. The names of the
+resulting mono files are of the form
+.Dq name_XY.suf
+where
+.Em name
+and
+.Em suf
+are the basename and suffix of the original file.
+If any file of such name already exists, it will be overwritten.
+Apart from the number of channels,
+the audio format of the resulting mono files
+is the same as that of the original file.
+.Sh EXIT STATUS
+.Ex -std
+.Sh EXAMPLES
+Merge a mono OGG file and a mono FLAC file into a stereo WAV file:
+.Bd -literal -offset indent
+$ sndfile-interleave left.ogg right.flac -o stereo.wav
+.Ed
+.Pp
+Split a multi-channel into individual mono files:
+.Bd -literal -offset indent
+$ sndfile-deinterleave multi.wav
+Input file : multi
+Output files :
+    multi_00.wav
+    multi_01.wav
+    multi_02.wav
+    multi_03.wav
+.Ed
+.Sh SEE ALSO
+.Lk http://libsndfile.github.io/libsndfile/
+.Sh AUTHORS
+.An Erik de Castro Lopo Aq Mt erikd@mega-nerd.com
diff --git a/man/sndfile-metadata-get.1 b/man/sndfile-metadata-get.1
new file mode 100644
index 0000000..61dfae7
--- /dev/null
+++ b/man/sndfile-metadata-get.1
@@ -0,0 +1,116 @@
+.Dd November 2, 2014
+.Dt SNDFILE-METADATA-GET 1
+.Os
+.Sh NAME
+.Nm sndfile-metadata-get ,
+.Nm sndfile-metadata-set
+.Nd get or set metadata in a sound file
+.Sh SYNOPSIS
+.Nm sndfile-metadata-get
+.Op Ar options
+.Ar file
+.Nm sndfile-metadata-set
+.Op Ar options
+.Ar file
+.Nm sndfile-metadata-set
+.Op Ar options
+.Ar input
+.Ar output
+.Sh DESCRIPTION
+.Nm sndfile-metadata-get
+displays bext and string metadata stored in an audio file.
+The following options specify what to print.
+.Pp
+.Bl -tag -width bext-descriptionXXXX -compact
+.It Fl -all
+all metadata
+.It Fl -bext-description
+description
+.It Fl -bext-originator
+originator info
+.It Fl -bext-orig-ref
+originator reference
+.It Fl -bext-umid
+Unique Material Identifier
+.It Fl -bext-orig-date
+origination date
+.It Fl -bext-orig-time
+origination time
+.It Fl -bext-coding-hist
+coding history
+.It Fl -str-title
+title
+.It Fl -str-copyright
+copyright
+.It Fl -str-artist
+artist
+.It Fl -str-comment
+comment
+.It Fl -str-date
+creation date
+.It Fl -str-album
+album
+.It Fl -str-license
+license
+.El
+.Pp
+.Nm sndfile-metadata-set
+sets bext and string metadata in an audio file if the format supports it.
+If the file does not contain a BEXT chunk to be modified,
+the second synopsis must be used, where another output file
+capable of storing the metadata is created.
+This file is overwritten if it already exists.
+The following options take an argument specifying the metadata:
+.Pp
+.Bl -tag -width bext-coding-histXXXXXXX -compact
+.It Fl -bext-description
+description
+.It Fl -bext-originator
+originator
+.It Fl -bext-orig-ref
+originator reference
+.It Fl -bext-umid
+Unique Material Identifier
+.It Fl -bext-orig-date
+origination date
+.It Fl -bext-orig-time
+origination time
+.It Fl -bext-coding-hist
+coding history
+.It Fl -bext-time-raf
+time reference
+.It Fl -str-comment
+comment
+.It Fl -str-title
+title
+.It Fl -str-copyright
+copyright
+.It Fl -str-artist
+artist
+.It Fl -str-date
+date
+.It Fl -str-album
+album
+.It Fl -str-license
+license
+.El
+.Pp
+The following options take no argument:
+.Pp
+.Bl -tag -width bext-coding-histXXXXXXX -compact
+.It Fl -bext-auto-time-date
+Set the BEXT time and date to current.
+.It Fl -bext-auto-time
+Set the BEXT time to current.
+.It Fl -bext-auto-date
+Set the BEXT date to current.
+.It Fl -str-auto-date
+Set the string date to current.
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Lk http://libsndfile.github.io/libsndfile/
+.Lk http://tech.ebu.ch/docs/tech/tech3285.pdf
+.Sh AUTHORS
+.An Erik de Castro Lopo Aq Mt erikd@mega-nerd.com
diff --git a/man/sndfile-play.1 b/man/sndfile-play.1
new file mode 100644
index 0000000..4d570ea
--- /dev/null
+++ b/man/sndfile-play.1
@@ -0,0 +1,36 @@
+.Dd September 10, 2021
+.Dt SNDFILE-PLAY 1
+.Os
+.Sh NAME
+.Nm sndfile-play
+.Nd play a sound file
+.Sh SYNOPSIS
+.Nm sndfile-play
+.Ar
+.Sh DESCRIPTION
+.Nm
+plays one or more sound files on various operating systems using standard audio
+output APIs. The following table summarizes which audio API is used where:
+.Pp
+.Bl -tag -width MacOSX10XXX -compact
+.It Linux
+ALSA or OSS
+.It OpenBSD
+sndio
+.It FreeBSD
+/dev/dsp (OSS)
+.It NetBSD
+/dev/audio
+.It Solaris
+/dev/audio
+.It MacOSX 10.6
+CoreAudio
+.It MacOSX 10.7
+AudioToolbox
+.It Win32
+waveOut
+.El
+.Sh SEE ALSO
+.Lk http://libsndfile.github.io/libsndfile/
+.Sh AUTHORS
+.An Erik de Castro Lopo Aq Mt erikd@mega-nerd.com
diff --git a/man/sndfile-salvage.1 b/man/sndfile-salvage.1
new file mode 100644
index 0000000..b2010f1
--- /dev/null
+++ b/man/sndfile-salvage.1
@@ -0,0 +1,25 @@
+.Dd November 2, 2014
+.Dt SNDFILE-SALVAGE 1
+.Os
+.Sh NAME
+.Nm sndfile-salvage
+.Nd salvage audio data from WAV files longer than 4G
+.Sh SYNOPSIS
+.Nm sndfile-salvage
+.Ar toolong.wav
+.Ar fixed64.wav
+.Sh DESCRIPTION
+Audio files using the WAV file container are inherently limited to 4G of data
+size fields in the WAV header being stored as unsigned 32bit integers.
+Many applications have trouble with these WAV files
+that are more the 4G in size.
+.Nm
+rewrites the WAV file into a W64 file with the same audio content.
+This file is overwritten if it already exists.
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Lk http://libsndfile.github.io/libsndfile/
+.\".Lk http://en.wikipedia.org/wiki/RF64
+.Sh AUTHORS
+.An Erik de Castro Lopo Aq Mt erikd@mega-nerd.com
diff --git a/ossfuzz/.gitignore b/ossfuzz/.gitignore
new file mode 100644
index 0000000..6e9ed16
--- /dev/null
+++ b/ossfuzz/.gitignore
@@ -0,0 +1 @@
+sndfile_fuzzer
diff --git a/ossfuzz/ci_oss.sh b/ossfuzz/ci_oss.sh
new file mode 100755
index 0000000..408b00d
--- /dev/null
+++ b/ossfuzz/ci_oss.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+set -ex
+
+PROJECT_NAME=libsndfile
+
+# Clone the oss-fuzz repository
+git clone https://github.com/google/oss-fuzz.git /tmp/ossfuzz
+
+if [[ ! -d /tmp/ossfuzz/projects/${PROJECT_NAME} ]]
+then
+    echo "Could not find the ${PROJECT_NAME} project in ossfuzz"
+
+    # Exit with a success code while the libsndfile project is not expected to exist
+    # on oss-fuzz.
+    exit 0
+fi
+
+# Work out which branch to clone from, inside Docker
+BRANCH=${GITHUB_REF}
+
+# Modify the oss-fuzz Dockerfile so that we're checking out the current reference on CI.
+sed -i "s@RUN.*@RUN git config --global remote.origin.fetch '+refs/pull/*:refs/remotes/origin/pull/*' \&\& git clone https://github.com/libsndfile/libsndfile.git /src/libsndfile \&\& cd /src/libsndfile \&\& git checkout -b ${BRANCH}@" /tmp/ossfuzz/projects/${PROJECT_NAME}/Dockerfile
+
+# Try and build the fuzzers
+pushd /tmp/ossfuzz
+python3 infra/helper.py build_image --pull ${PROJECT_NAME}
+python3 infra/helper.py build_fuzzers ${PROJECT_NAME}
+python3 infra/helper.py check_build ${PROJECT_NAME} --engine libfuzzer --sanitizer address --architecture x86_64
+popd
diff --git a/ossfuzz/ossfuzz.sh b/ossfuzz/ossfuzz.sh
new file mode 100755
index 0000000..7b3666f
--- /dev/null
+++ b/ossfuzz/ossfuzz.sh
@@ -0,0 +1,32 @@
+#!/bin/bash -eu
+
+# This script is called by the oss-fuzz main project when compiling the fuzz
+# targets. This script is regression tested by ci_oss.sh.
+
+# Save off the current folder as the build root.
+export BUILD_ROOT=$PWD
+
+echo "CC: ${CC:-}"
+echo "CXX: ${CXX:-}"
+echo "LIB_FUZZING_ENGINE: ${LIB_FUZZING_ENGINE:-}"
+echo "CFLAGS: ${CFLAGS:-}"
+echo "CXXFLAGS: ${CXXFLAGS:-}"
+echo "OUT: ${OUT:-}"
+
+export MAKEFLAGS+="-j$(nproc)"
+
+# Install dependencies
+apt-get -y install autoconf autogen automake libtool pkg-config python
+
+# For now, do not install the following libraries (as they won't be in the
+# final image):
+# libasound2-dev libflac-dev libogg-dev libopus-dev libvorbis-dev
+
+# Compile the fuzzer.
+autoreconf -vif
+./configure --disable-shared --enable-ossfuzzers
+make V=1
+
+# Copy the fuzzer to the output directory.
+cp -v ossfuzz/sndfile_fuzzer $OUT/
+cp -v ossfuzz/sndfile_alt_fuzzer $OUT/
diff --git a/ossfuzz/sndfile_alt_fuzzer.cc b/ossfuzz/sndfile_alt_fuzzer.cc
new file mode 100644
index 0000000..e69fcdf
--- /dev/null
+++ b/ossfuzz/sndfile_alt_fuzzer.cc
@@ -0,0 +1,79 @@
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sndfile.h>
+#include <inttypes.h>
+
+#include "sndfile_fuzz_header.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
+{   // One byte is needed for deciding which function to target.
+    if (size == 0)
+       return 0 ;
+
+    const uint8_t decider = *data ;
+    data += 1 ;
+    size -= 1 ;
+
+    SF_INFO sndfile_info ;
+    VIO_DATA vio_data ;
+    SF_VIRTUAL_IO vio ;
+    SNDFILE *sndfile = NULL ;
+    int err = sf_init_file(data, size, &sndfile, &vio_data, &vio, &sndfile_info) ;
+    if (err)
+      goto EXIT_LABEL ;
+
+    // Just the right number of channels. Create some buffer space for reading.
+    switch (decider % 3)
+    {   case 0  :
+                {
+                  short* read_buffer = NULL ;
+                  read_buffer = (short*)malloc(sizeof(short) * size);
+                  if (read_buffer == NULL)
+                    abort() ;
+
+                  while (sf_read_short(sndfile, read_buffer, size))
+                  {
+                    // Do nothing with the data.
+                  }
+                  free(read_buffer) ;
+                }
+                break ;
+        case 1  :
+                {
+                  int* read_buffer = NULL ;
+                  read_buffer = (int*)malloc(sizeof(int) * size) ;
+                  if (read_buffer == NULL)
+                    abort() ;
+
+                  while (sf_read_int(sndfile, read_buffer, size))
+                  {
+                    // Do nothing with the data.
+                  }
+                  free(read_buffer) ;
+                }
+                break ;
+        case 2  :
+                {
+                  double* read_buffer = NULL ;
+                  read_buffer = (double*)malloc(sizeof(double) * size) ;
+                  if (read_buffer == NULL)
+                    abort() ;
+
+                  while (sf_read_double(sndfile, read_buffer, size))
+                  {
+                    // Do nothing with the data.
+                  }
+                  free(read_buffer) ;
+                }
+                break ;
+        default :
+                break ;
+                } ;
+
+  EXIT_LABEL:
+    if (sndfile != NULL)
+      sf_close(sndfile);
+
+    return 0 ;
+}
diff --git a/ossfuzz/sndfile_fuzz_header.h b/ossfuzz/sndfile_fuzz_header.h
new file mode 100644
index 0000000..898aec4
--- /dev/null
+++ b/ossfuzz/sndfile_fuzz_header.h
@@ -0,0 +1,119 @@
+#ifndef SNDFILE_FUZZ_HEADER_H
+#define SNDFILE_FUZZ_HEADER_H
+
+typedef struct
+{
+  sf_count_t offset ;
+  sf_count_t length ;
+  const unsigned char *data ;
+} VIO_DATA ;
+
+static sf_count_t vfget_filelen (void *user_data)
+{  VIO_DATA *vf = (VIO_DATA *)user_data ;
+   return vf->length ;
+}
+
+static sf_count_t vfseek (sf_count_t offset, int whence, void *user_data)
+{
+  VIO_DATA *vf = (VIO_DATA *)user_data ;
+  sf_count_t new_offset ;
+
+  switch (whence)
+  {   case SEEK_SET :
+        new_offset = offset ;
+        break ;
+
+    case SEEK_CUR :
+        new_offset = vf->offset + offset ;
+        break ;
+
+    case SEEK_END :
+        new_offset = vf->length + offset ;
+        break ;
+
+    default :
+        break ;
+  }
+
+  /* Ensure you can't seek outside the data */
+  if (new_offset > vf->length)
+  {  /* Trying to seek past the end of the data */
+     printf("vf overseek: new_offset(%" PRId64 ") > vf->length(%" PRId64 ");"
+            "  whence(%d), vf->offset(%" PRId64 "), offset(%" PRId64 ")\n",
+            new_offset, vf->length, whence, vf->offset, offset) ;
+     new_offset = vf->length ;
+  }
+  else if (new_offset < 0)
+  {  /* Trying to seek before the start of the data */
+     printf("vf underseek: new_offset(%" PRId64 ") < 0;  whence(%d), vf->offset"
+            "(%" PRId64 "), vf->length(%" PRId64 "), offset(%" PRId64 ")\n",
+            new_offset, whence, vf->offset, vf->length, offset) ;
+     new_offset = 0 ;
+  }
+  vf->offset = new_offset ;
+
+  return vf->offset ;
+}
+
+static sf_count_t vfread (void *ptr, sf_count_t count, void *user_data)
+{  VIO_DATA *vf = (VIO_DATA *)user_data ;
+
+   if (vf->offset + count > vf->length)
+     count = vf->length - vf->offset ;
+
+   memcpy(ptr, vf->data + vf->offset, count) ;
+   vf->offset += count ;
+
+   return count ;
+}
+
+static sf_count_t vfwrite (const void *ptr, sf_count_t count, void *user_data)
+{
+  (void)ptr ;
+  (void)count ;
+  (void)user_data ;
+
+  // Cannot write to this virtual file.
+  return 0;
+}
+
+static sf_count_t vftell (void *user_data)
+{ VIO_DATA *vf = (VIO_DATA *)user_data ;
+
+  return vf->offset ;
+}
+
+int sf_init_file(const uint8_t *data, 
+                size_t size, 
+                SNDFILE **sndfile, 
+                VIO_DATA *vio_data, 
+                SF_VIRTUAL_IO *vio, SF_INFO *sndfile_info)
+{  float* read_buffer = NULL ;
+
+   // Initialize the virtual IO structure.
+   vio->get_filelen = vfget_filelen ;
+   vio->seek = vfseek ;
+   vio->read = vfread ;
+   vio->write = vfwrite ;
+   vio->tell = vftell ;
+
+   // Initialize the VIO user data.
+   vio_data->data = data ;
+   vio_data->length = size ;
+   vio_data->offset = 0 ;
+
+   memset(sndfile_info, 0, sizeof(SF_INFO)) ;
+
+   // Try and open the virtual file.
+   *sndfile = sf_open_virtual(vio, SFM_READ, sndfile_info, vio_data) ;
+
+   if (sndfile_info->channels == 0)
+		 return -1 ;
+
+   if (sndfile_info->channels > 1024 * 1024)
+		 return -1 ;
+
+   return 0;
+}
+
+#endif
diff --git a/ossfuzz/sndfile_fuzzer.cc b/ossfuzz/sndfile_fuzzer.cc
new file mode 100644
index 0000000..2a3d693
--- /dev/null
+++ b/ossfuzz/sndfile_fuzzer.cc
@@ -0,0 +1,39 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sndfile.h>
+#include <inttypes.h>
+
+#include "sndfile_fuzz_header.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
+{  VIO_DATA vio_data ;
+   SF_VIRTUAL_IO vio ;
+   SF_INFO sndfile_info ;
+   SNDFILE *sndfile = NULL ;
+   float* read_buffer = NULL ;
+
+   int err = sf_init_file(data, size, &sndfile, &vio_data, &vio, &sndfile_info) ;
+   if (err)
+     goto EXIT_LABEL ;
+
+   // Just the right number of channels. Create some buffer space for reading.
+   read_buffer = (float*)malloc(sizeof(float) * sndfile_info.channels);
+   if (read_buffer == NULL)
+     abort() ;
+
+   while (sf_readf_float(sndfile, read_buffer, 1))
+   {
+     // Do nothing with the data.
+   }
+
+EXIT_LABEL:
+
+   if (sndfile != NULL)
+     sf_close(sndfile) ;
+
+   free(read_buffer) ;
+
+   return 0 ;
+}
diff --git a/ossfuzz/standaloneengine.cc b/ossfuzz/standaloneengine.cc
new file mode 100644
index 0000000..ab6408d
--- /dev/null
+++ b/ossfuzz/standaloneengine.cc
@@ -0,0 +1,86 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "testinput.h"
+
+/**
+ * Main procedure for standalone fuzzing engine.
+ *
+ * Reads filenames from the argument array. For each filename, read the file
+ * into memory and then call the fuzzing interface with the data.
+ */
+int main(int argc, char **argv)
+{
+  int ii;
+  for(ii = 1; ii < argc; ii++)
+  {
+    FILE *infile;
+    printf("[%s] ", argv[ii]);
+
+    /* Try and open the file. */
+    infile = fopen(argv[ii], "rb");
+    if(infile)
+    {
+      uint8_t *buffer = NULL;
+      size_t buffer_len;
+
+      printf("Opened.. ");
+
+      /* Get the length of the file. */
+      fseek(infile, 0L, SEEK_END);
+      buffer_len = ftell(infile);
+
+      /* Reset the file indicator to the beginning of the file. */
+      fseek(infile, 0L, SEEK_SET);
+
+      /* Allocate a buffer for the file contents. */
+      buffer = (uint8_t *)calloc(buffer_len, sizeof(uint8_t));
+      if(buffer)
+      {
+        size_t result;
+
+        /* Read all the text from the file into the buffer. */
+        result = fread(buffer, sizeof(uint8_t), buffer_len, infile);
+
+        if (result == buffer_len)
+        {
+          printf("Read %zu bytes, fuzzing.. ", buffer_len);
+          /* Call the fuzzer with the data. */
+          LLVMFuzzerTestOneInput(buffer, buffer_len);
+
+          printf("complete !!");
+        }
+        else
+        {
+          fprintf(stderr,
+                  "Failed to read %zu bytes (result %zu)\n",
+                  buffer_len,
+                  result);
+        }
+
+        /* Free the buffer as it's no longer needed. */
+        free(buffer);
+        buffer = NULL;
+      }
+      else
+      {
+        fprintf(stderr,
+                "[%s] Failed to allocate %zu bytes \n",
+                argv[ii],
+                buffer_len);
+      }
+
+      /* Close the file as it's no longer needed. */
+      fclose(infile);
+      infile = NULL;
+    }
+    else
+    {
+      /* Failed to open the file. Maybe wrong name or wrong permissions? */
+      fprintf(stderr, "[%s] Open failed. \n", argv[ii]);
+    }
+
+    printf("\n");
+  }
+}
diff --git a/ossfuzz/testinput.h b/ossfuzz/testinput.h
new file mode 100644
index 0000000..6ab9b51
--- /dev/null
+++ b/ossfuzz/testinput.h
@@ -0,0 +1,3 @@
+#include <inttypes.h>
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
diff --git a/programs/common.c b/programs/common.c
new file mode 100644
index 0000000..7ff0d41
--- /dev/null
+++ b/programs/common.c
@@ -0,0 +1,503 @@
+/*
+** Copyright (C) 1999-2019 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2008 George Blood Audio
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in
+**       the documentation and/or other materials provided with the
+**       distribution.
+**     * Neither the author nor the names of any contributors may be used
+**       to endorse or promote products derived from this software without
+**       specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdint.h>
+#include <math.h>
+
+#include <sndfile.h>
+
+#include "common.h"
+
+#define	BUFFER_LEN	4096
+
+#define	MIN(x, y)	((x) < (y) ? (x) : (y))
+
+int
+sfe_copy_data_fp (SNDFILE *outfile, SNDFILE *infile, int channels, int normalize)
+{	static double	data [BUFFER_LEN], max ;
+	sf_count_t		frames, readcount, k ;
+
+	frames = BUFFER_LEN / channels ;
+	readcount = frames ;
+
+	sf_command (infile, SFC_CALC_SIGNAL_MAX, &max, sizeof (max)) ;
+	if (!isnormal (max)) /* neither zero, subnormal, infinite, nor NaN */
+		return 1 ;
+
+	if (!normalize && max < 1.0)
+	{	while (readcount > 0)
+		{	readcount = sf_readf_double (infile, data, frames) ;
+			sf_writef_double (outfile, data, readcount) ;
+			} ;
+		}
+	else
+	{	sf_command (infile, SFC_SET_NORM_DOUBLE, NULL, SF_FALSE) ;
+
+		while (readcount > 0)
+		{	readcount = sf_readf_double (infile, data, frames) ;
+			for (k = 0 ; k < readcount * channels ; k++)
+			{	data [k] /= max ;
+
+				if (!isfinite (data [k])) /* infinite or NaN */
+					return 1 ;
+				}
+			sf_writef_double (outfile, data, readcount) ;
+			} ;
+		} ;
+
+	return 0 ;
+} /* sfe_copy_data_fp */
+
+void
+sfe_copy_data_int (SNDFILE *outfile, SNDFILE *infile, int channels)
+{	static int	data [BUFFER_LEN] ;
+	int		frames, readcount ;
+
+	frames = BUFFER_LEN / channels ;
+	readcount = frames ;
+
+	while (readcount > 0)
+	{	readcount = (int) sf_readf_int (infile, data, frames) ;
+		sf_writef_int (outfile, data, readcount) ;
+		} ;
+
+	return ;
+} /* sfe_copy_data_int */
+
+/*==============================================================================
+*/
+
+static int
+merge_broadcast_info (SNDFILE * infile, SNDFILE * outfile, int format, const METADATA_INFO * info)
+{	SF_BROADCAST_INFO_2K binfo ;
+	int infileminor ;
+
+	memset (&binfo, 0, sizeof (binfo)) ;
+
+	if ((SF_FORMAT_TYPEMASK & format) != SF_FORMAT_WAV)
+	{	printf ("Error : This is not a WAV file and hence broadcast info cannot be added to it.\n\n") ;
+		return 1 ;
+		} ;
+
+	infileminor = SF_FORMAT_SUBMASK & format ;
+
+	switch (infileminor)
+	{	case SF_FORMAT_PCM_16 :
+		case SF_FORMAT_PCM_24 :
+		case SF_FORMAT_PCM_32 :
+		case SF_FORMAT_MPEG_LAYER_III :
+			break ;
+
+		default :
+			printf (
+				"Warning : The EBU Technical Recommendation R68-2000 states that the only\n"
+				"          allowed encodings are Linear PCM and MPEG3. This file is not in\n"
+				"          the right format.\n\n"
+				) ;
+			break ;
+		} ;
+
+	if (sf_command (infile, SFC_GET_BROADCAST_INFO, &binfo, sizeof (binfo)) == 0)
+	{	if (infile == outfile)
+		{	printf (
+				"Error : Attempting in-place broadcast info update, but file does not\n"
+				"        have a 'bext' chunk to modify. The solution is to specify both\n"
+				"        input and output files on the command line.\n\n"
+				) ;
+			return 1 ;
+			} ;
+		} ;
+
+#define REPLACE_IF_NEW(x) \
+		if (info->x != NULL) \
+		{	memset (binfo.x, 0, sizeof (binfo.x)) ; \
+			memcpy (binfo.x, info->x, MIN (strlen (info->x), sizeof (binfo.x))) ; \
+			} ;
+
+	REPLACE_IF_NEW (description) ;
+	REPLACE_IF_NEW (originator) ;
+	REPLACE_IF_NEW (originator_reference) ;
+	REPLACE_IF_NEW (origination_date) ;
+	REPLACE_IF_NEW (origination_time) ;
+	REPLACE_IF_NEW (umid) ;
+
+	/* Special case loudness values */
+#define REPLACE_IF_NEW_INT(x) \
+		if (info->x != NULL) \
+		{	binfo.x = round (atof (info->x) * 100.0) ; \
+			} ;
+
+	REPLACE_IF_NEW_INT (loudness_value) ;
+	REPLACE_IF_NEW_INT (loudness_range) ;
+	REPLACE_IF_NEW_INT (max_true_peak_level) ;
+	REPLACE_IF_NEW_INT (max_momentary_loudness) ;
+	REPLACE_IF_NEW_INT (max_shortterm_loudness) ;
+
+	/* Special case for Time Ref. */
+	if (info->time_ref != NULL)
+	{	uint64_t ts = atoll (info->time_ref) ;
+
+		binfo.time_reference_high = (ts >> 32) ;
+		binfo.time_reference_low = (ts & 0xffffffff) ;
+		} ;
+
+	/* Special case for coding_history because we may want to append. */
+	if (info->coding_history != NULL)
+	{	if (info->coding_hist_append)
+		{	int slen = (int) strlen (binfo.coding_history) ;
+
+			while (slen > 1 && isspace (binfo.coding_history [slen - 1]))
+				slen -- ;
+
+			memcpy (binfo.coding_history + slen, info->coding_history, sizeof (binfo.coding_history) - slen) ;
+			}
+		else
+		{	size_t slen = MIN (strlen (info->coding_history), sizeof (binfo.coding_history)) ;
+
+			memset (binfo.coding_history, 0, sizeof (binfo.coding_history)) ;
+			memcpy (binfo.coding_history, info->coding_history, slen) ;
+			binfo.coding_history_size = (uint32_t) slen ;
+			} ;
+		} ;
+
+	if (sf_command (outfile, SFC_SET_BROADCAST_INFO, &binfo, sizeof (binfo)) == 0)
+	{	printf ("Error : Setting of broadcast info chunks failed.\n\n") ;
+		return 1 ;
+		} ;
+
+	return 0 ;
+} /* merge_broadcast_info*/
+
+static void
+update_strings (SNDFILE * outfile, const METADATA_INFO * info)
+{
+	if (info->title != NULL)
+		sf_set_string (outfile, SF_STR_TITLE, info->title) ;
+
+	if (info->copyright != NULL)
+		sf_set_string (outfile, SF_STR_COPYRIGHT, info->copyright) ;
+
+	if (info->artist != NULL)
+		sf_set_string (outfile, SF_STR_ARTIST, info->artist) ;
+
+	if (info->comment != NULL)
+		sf_set_string (outfile, SF_STR_COMMENT, info->comment) ;
+
+	if (info->date != NULL)
+		sf_set_string (outfile, SF_STR_DATE, info->date) ;
+
+	if (info->album != NULL)
+		sf_set_string (outfile, SF_STR_ALBUM, info->album) ;
+
+	if (info->license != NULL)
+		sf_set_string (outfile, SF_STR_LICENSE, info->license) ;
+
+} /* update_strings */
+
+
+
+void
+sfe_apply_metadata_changes (const char * filenames [2], const METADATA_INFO * info)
+{	SNDFILE *infile = NULL, *outfile = NULL ;
+	SF_INFO sfinfo ;
+	METADATA_INFO tmpinfo ;
+	int error_code = 0 ;
+
+	memset (&sfinfo, 0, sizeof (sfinfo)) ;
+	memset (&tmpinfo, 0, sizeof (tmpinfo)) ;
+
+	if (filenames [1] == NULL)
+		infile = outfile = sf_open (filenames [0], SFM_RDWR, &sfinfo) ;
+	else
+	{	infile = sf_open (filenames [0], SFM_READ, &sfinfo) ;
+
+		/* Output must be WAV. */
+		sfinfo.format = SF_FORMAT_WAV | (SF_FORMAT_SUBMASK & sfinfo.format) ;
+		outfile = sf_open (filenames [1], SFM_WRITE, &sfinfo) ;
+		} ;
+
+	if (infile == NULL)
+	{	printf ("Error : Not able to open input file '%s' : %s\n", filenames [0], sf_strerror (infile)) ;
+		error_code = 1 ;
+		goto cleanup_exit ;
+		} ;
+
+	if (outfile == NULL)
+	{	printf ("Error : Not able to open output file '%s' : %s\n", filenames [1], sf_strerror (outfile)) ;
+		error_code = 1 ;
+		goto cleanup_exit ;
+		} ;
+
+	if (info->has_bext_fields && merge_broadcast_info (infile, outfile, sfinfo.format, info))
+	{	error_code = 1 ;
+		goto cleanup_exit ;
+		} ;
+
+	if (infile != outfile)
+	{	int infileminor = SF_FORMAT_SUBMASK & sfinfo.format ;
+
+		/* If the input file is not the same as the output file, copy the data. */
+		if ((infileminor == SF_FORMAT_DOUBLE) || (infileminor == SF_FORMAT_FLOAT))
+		{	if (sfe_copy_data_fp (outfile, infile, sfinfo.channels, SF_FALSE) != 0)
+			{	printf ("Error : Not able to decode input file '%s'\n", filenames [0]) ;
+				error_code = 1 ;
+				goto cleanup_exit ;
+				} ;
+			}
+		else
+			sfe_copy_data_int (outfile, infile, sfinfo.channels) ;
+		} ;
+
+	update_strings (outfile, info) ;
+
+cleanup_exit :
+
+	if (outfile != NULL && outfile != infile)
+		sf_close (outfile) ;
+
+	if (infile != NULL)
+		sf_close (infile) ;
+
+	if (error_code)
+		exit (error_code) ;
+
+	return ;
+} /* sfe_apply_metadata_changes */
+
+/*==============================================================================
+*/
+
+typedef struct
+{	const char	*ext ;
+	int			len ;
+	int			format ;
+} OUTPUT_FORMAT_MAP ;
+
+/* Map a file name extension to a container format. */
+static OUTPUT_FORMAT_MAP format_map [] =
+{
+	{	"wav", 		0,	SF_FORMAT_WAV	},
+	{	"aif",		3,	SF_FORMAT_AIFF	},
+	{	"au",		0,	SF_FORMAT_AU	},
+	{	"snd",		0,	SF_FORMAT_AU	},
+	{	"raw",		0,	SF_FORMAT_RAW	},
+	{	"gsm",		0,	SF_FORMAT_RAW | SF_FORMAT_GSM610 },
+	{	"vox",		0, 	SF_FORMAT_RAW | SF_FORMAT_VOX_ADPCM },
+	{	"paf",		0,	SF_FORMAT_PAF | SF_ENDIAN_BIG },
+	{	"fap",		0,	SF_FORMAT_PAF | SF_ENDIAN_LITTLE },
+	{	"svx",		0,	SF_FORMAT_SVX	},
+	{	"nist", 	0,	SF_FORMAT_NIST	},
+	{	"sph",		0,	SF_FORMAT_NIST	},
+	{	"voc",		0, 	SF_FORMAT_VOC	},
+	{	"ircam",	0,	SF_FORMAT_IRCAM	},
+	{	"sf",		0, 	SF_FORMAT_IRCAM	},
+	{	"w64", 		0, 	SF_FORMAT_W64	},
+	{	"mat",		0, 	SF_FORMAT_MAT4 	},
+	{	"mat4", 	0,	SF_FORMAT_MAT4	},
+	{	"mat5", 	0, 	SF_FORMAT_MAT5 	},
+	{	"pvf",		0, 	SF_FORMAT_PVF 	},
+	{	"xi",		0, 	SF_FORMAT_XI 	},
+	{	"htk",		0,	SF_FORMAT_HTK	},
+	{	"sds",		0, 	SF_FORMAT_SDS 	},
+	{	"avr",		0, 	SF_FORMAT_AVR 	},
+	{	"wavex",	0, 	SF_FORMAT_WAVEX },
+	{	"sd2",		0, 	SF_FORMAT_SD2 	},
+	{	"flac",		0,	SF_FORMAT_FLAC	},
+	{	"caf",		0,	SF_FORMAT_CAF	},
+	{	"wve",		0,	SF_FORMAT_WVE	},
+	{	"prc",		0,	SF_FORMAT_WVE	},
+	{	"oga",		0,	SF_FORMAT_OGG	},
+	{	"ogg",		0,	SF_FORMAT_OGG | SF_FORMAT_VORBIS },
+	{	"opus",		0,	SF_FORMAT_OGG | SF_FORMAT_OPUS },
+	{	"mpc",		0,	SF_FORMAT_MPC2K	},
+	{	"rf64",		0,	SF_FORMAT_RF64	},
+	{	"mp3",		0,	SF_FORMAT_MPEG | SF_FORMAT_MPEG_LAYER_III },
+} ; /* format_map */
+
+int
+sfe_file_type_of_ext (const char *str, int format)
+{	char	buffer [16], *cptr ;
+	int		k ;
+
+	format &= SF_FORMAT_SUBMASK ;
+
+	if ((cptr = strrchr (str, '.')) == NULL)
+		return 0 ;
+
+	strncpy (buffer, cptr + 1, 15) ;
+	buffer [15] = 0 ;
+
+	for (k = 0 ; buffer [k] ; k++)
+		buffer [k] = tolower ((buffer [k])) ;
+
+	for (k = 0 ; k < (int) (sizeof (format_map) / sizeof (format_map [0])) ; k++)
+	{	if ((format_map [k].len > 0 && strncmp (buffer, format_map [k].ext, format_map [k].len) == 0) ||
+			(strcmp (buffer, format_map [k].ext) == 0))
+		{	if (format_map [k].format & SF_FORMAT_SUBMASK)
+				return format_map [k].format ;
+			else
+				return format_map [k].format | format ;
+			} ;
+		} ;
+
+	/* Default if all the above fails. */
+	return (SF_FORMAT_WAV | SF_FORMAT_PCM_24) ;
+} /* sfe_file_type_of_ext */
+
+void
+sfe_dump_format_map (void)
+{	SF_FORMAT_INFO	info ;
+	int k ;
+
+	for (k = 0 ; k < ARRAY_LEN (format_map) ; k++)
+	{	info.format = format_map [k].format ;
+		sf_command (NULL, SFC_GET_FORMAT_INFO, &info, sizeof (info)) ;
+		printf ("        %-10s : %s", format_map [k].ext, info.name == NULL ? "????" : info.name) ;
+		if (format_map [k].format & SF_FORMAT_SUBMASK)
+		{	info.format = format_map [k].format & SF_FORMAT_SUBMASK ;
+			sf_command (NULL, SFC_GET_FORMAT_INFO, &info, sizeof (info)) ;
+			printf (" %s", info.name == NULL ? "????" : info.name) ;
+			} ;
+		putchar ('\n') ;
+
+		} ;
+
+} /* sfe_dump_format_map */
+
+const char *
+program_name (const char * argv0)
+{	const char * tmp ;
+
+	tmp = strrchr (argv0, '/') ;
+	argv0 = tmp ? tmp + 1 : argv0 ;
+
+	/* Remove leading libtool name mangling. */
+	if (strstr (argv0, "lt-") == argv0)
+		return argv0 + 3 ;
+
+	return argv0 ;
+} /* program_name */
+
+const char *
+sfe_endian_name (int format)
+{
+	switch (format & SF_FORMAT_ENDMASK)
+	{	case SF_ENDIAN_FILE : return "file" ;
+		case SF_ENDIAN_LITTLE : return "little" ;
+		case SF_ENDIAN_BIG : return "big" ;
+		case SF_ENDIAN_CPU : return "cpu" ;
+		default : break ;
+		} ;
+
+	return "unknown" ;
+} /* sfe_endian_name */
+
+const char *
+sfe_container_name (int format)
+{
+	switch (format & SF_FORMAT_TYPEMASK)
+	{	case SF_FORMAT_WAV : return "WAV" ;
+		case SF_FORMAT_AIFF : return "AIFF" ;
+		case SF_FORMAT_AU : return "AU" ;
+		case SF_FORMAT_RAW : return "RAW" ;
+		case SF_FORMAT_PAF : return "PAF" ;
+		case SF_FORMAT_SVX : return "SVX" ;
+		case SF_FORMAT_NIST : return "NIST" ;
+		case SF_FORMAT_VOC : return "VOC" ;
+		case SF_FORMAT_IRCAM : return "IRCAM" ;
+		case SF_FORMAT_W64 : return "W64" ;
+		case SF_FORMAT_MAT4 : return "MAT4" ;
+		case SF_FORMAT_MAT5 : return "MAT5" ;
+		case SF_FORMAT_PVF : return "PVF" ;
+		case SF_FORMAT_XI : return "XI" ;
+		case SF_FORMAT_HTK : return "HTK" ;
+		case SF_FORMAT_SDS : return "SDS" ;
+		case SF_FORMAT_AVR : return "AVR" ;
+		case SF_FORMAT_WAVEX : return "WAVEX" ;
+		case SF_FORMAT_SD2 : return "SD2" ;
+		case SF_FORMAT_FLAC : return "FLAC" ;
+		case SF_FORMAT_CAF : return "CAF" ;
+		case SF_FORMAT_WVE : return "WVE" ;
+		case SF_FORMAT_OGG : return "OGG" ;
+		case SF_FORMAT_MPC2K : return "MPC2K" ;
+		case SF_FORMAT_RF64 : return "RF64" ;
+		case SF_FORMAT_MPEG : return "MPEG" ;
+		default : break ;
+		} ;
+
+	return "unknown" ;
+} /* sfe_container_name */
+
+const char *
+sfe_codec_name (int format)
+{
+	switch (format & SF_FORMAT_SUBMASK)
+	{	case SF_FORMAT_PCM_S8 : return "signed 8 bit PCM" ;
+		case SF_FORMAT_PCM_16 : return "16 bit PCM" ;
+		case SF_FORMAT_PCM_24 : return "24 bit PCM" ;
+		case SF_FORMAT_PCM_32 : return "32 bit PCM" ;
+		case SF_FORMAT_PCM_U8 : return "unsigned 8 bit PCM" ;
+		case SF_FORMAT_FLOAT : return "32 bit float" ;
+		case SF_FORMAT_DOUBLE : return "64 bit double" ;
+		case SF_FORMAT_ULAW : return "u-law" ;
+		case SF_FORMAT_ALAW : return "a-law" ;
+		case SF_FORMAT_IMA_ADPCM : return "IMA ADPCM" ;
+		case SF_FORMAT_MS_ADPCM : return "MS ADPCM" ;
+		case SF_FORMAT_GSM610 : return "gsm610" ;
+		case SF_FORMAT_VOX_ADPCM : return "Vox ADPCM" ;
+		case SF_FORMAT_G721_32 : return "g721 32kbps" ;
+		case SF_FORMAT_G723_24 : return "g723 24kbps" ;
+		case SF_FORMAT_G723_40 : return "g723 40kbps" ;
+		case SF_FORMAT_DWVW_12 : return "12 bit DWVW" ;
+		case SF_FORMAT_DWVW_16 : return "16 bit DWVW" ;
+		case SF_FORMAT_DWVW_24 : return "14 bit DWVW" ;
+		case SF_FORMAT_DWVW_N : return "DWVW" ;
+		case SF_FORMAT_DPCM_8 : return "8 bit DPCM" ;
+		case SF_FORMAT_DPCM_16 : return "16 bit DPCM" ;
+		case SF_FORMAT_VORBIS : return "Vorbis" ;
+		case SF_FORMAT_ALAC_16 : return "16 bit ALAC" ;
+		case SF_FORMAT_ALAC_20 : return "20 bit ALAC" ;
+		case SF_FORMAT_ALAC_24 : return "24 bit ALAC" ;
+		case SF_FORMAT_ALAC_32 : return "32 bit ALAC" ;
+		case SF_FORMAT_OPUS : return "Opus" ;
+		case SF_FORMAT_MPEG_LAYER_I : return "MPEG layer 1" ;
+		case SF_FORMAT_MPEG_LAYER_II : return "MPEG layer 2" ;
+		case SF_FORMAT_MPEG_LAYER_III : return "MPEG layer 3" ;
+		default : break ;
+		} ;
+	return "unknown" ;
+} /* sfe_codec_name */
diff --git a/programs/common.h b/programs/common.h
new file mode 100644
index 0000000..5ed36db
--- /dev/null
+++ b/programs/common.h
@@ -0,0 +1,82 @@
+/*
+** Copyright (C) 1999-2013 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in
+**       the documentation and/or other materials provided with the
+**       distribution.
+**     * Neither the author nor the names of any contributors may be used
+**       to endorse or promote products derived from this software without
+**       specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#define	ARRAY_LEN(x)	((int) (sizeof (x) / sizeof (x [0])))
+#define	MAX(a, b)		((a) > (b) ? (a) : (b))
+
+typedef struct
+{	const char * title ;
+	const char * copyright ;
+	const char * artist ;
+	const char * comment ;
+	const char * date ;
+	const char * album ;
+	const char * license ;
+
+
+	/* Stuff to go in the 'bext' chunk of WAV files. */
+	int has_bext_fields ;
+	int coding_hist_append ;
+
+	const char * description ;
+	const char * originator ;
+	const char * originator_reference ;
+	const char * origination_date ;
+	const char * origination_time ;
+	const char * umid ;
+	const char * loudness_value ;
+	const char * loudness_range ;
+	const char * max_true_peak_level ;
+	const char * max_momentary_loudness ;
+	const char * max_shortterm_loudness ;
+	const char * coding_history ;
+	const char * time_ref ;
+} METADATA_INFO ;
+
+typedef SF_BROADCAST_INFO_VAR (2048) SF_BROADCAST_INFO_2K ;
+
+void sfe_apply_metadata_changes (const char * filenames [2], const METADATA_INFO * info) ;
+
+int sfe_copy_data_fp (SNDFILE *outfile, SNDFILE *infile, int channels, int normalize) ;
+
+void sfe_copy_data_int (SNDFILE *outfile, SNDFILE *infile, int channels) ;
+
+int sfe_file_type_of_ext (const char *filename, int format) ;
+
+void sfe_dump_format_map (void) ;
+
+const char * program_name (const char * argv0) ;
+
+const char * sfe_endian_name (int format) ;
+const char * sfe_container_name (int format) ;
+const char * sfe_codec_name (int format) ;
diff --git a/programs/sndfile-cmp.c b/programs/sndfile-cmp.c
new file mode 100644
index 0000000..3dd992d
--- /dev/null
+++ b/programs/sndfile-cmp.c
@@ -0,0 +1,155 @@
+/*
+** Copyright (C) 2008-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2008 Conrad Parker <conrad@metadecks.org>
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in
+**       the documentation and/or other materials provided with the
+**       distribution.
+**     * Neither the author nor the names of any contributors may be used
+**       to endorse or promote products derived from this software without
+**       specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include <sndfile.h>
+
+#include "common.h"
+
+/* Length of comparison data buffers in units of items */
+#define BUFLEN 65536
+
+static const char * progname = NULL ;
+static char * filename1 = NULL, * filename2 = NULL ;
+
+static int
+comparison_error (const char * what, sf_count_t frame_offset)
+{	char buffer [128] ;
+
+	if (frame_offset >= 0)
+		snprintf (buffer, sizeof (buffer), " (at frame offset %" PRId64 ")", frame_offset) ;
+	else
+		buffer [0] = 0 ;
+
+	printf ("%s: %s of files %s and %s differ%s.\n", progname, what, filename1, filename2, buffer) ;
+	return 1 ;
+} /* comparison_error */
+
+static int
+compare (void)
+{
+	double buf1 [BUFLEN], buf2 [BUFLEN] ;
+	SF_INFO sfinfo1, sfinfo2 ;
+	SNDFILE * sf1 = NULL, * sf2 = NULL ;
+	sf_count_t items, i, nread1, nread2, offset = 0 ;
+	int retval = 0 ;
+
+	memset (&sfinfo1, 0, sizeof (SF_INFO)) ;
+	sf1 = sf_open (filename1, SFM_READ, &sfinfo1) ;
+	if (sf1 == NULL)
+	{	printf ("Error opening %s.\n", filename1) ;
+		retval = 1 ;
+		goto out ;
+		} ;
+
+	memset (&sfinfo2, 0, sizeof (SF_INFO)) ;
+	sf2 = sf_open (filename2, SFM_READ, &sfinfo2) ;
+	if (sf2 == NULL)
+	{	printf ("Error opening %s.\n", filename2) ;
+		retval = 1 ;
+		goto out ;
+		} ;
+
+	if (sfinfo1.samplerate != sfinfo2.samplerate)
+	{	retval = comparison_error ("Samplerates", -1) ;
+		goto out ;
+		} ;
+
+	if (sfinfo1.channels != sfinfo2.channels)
+	{	retval = comparison_error ("Number of channels", -1) ;
+		goto out ;
+		} ;
+
+	/* Calculate the framecount that will fit in our data buffers */
+	items = BUFLEN / sfinfo1.channels ;
+
+	while ((nread1 = sf_readf_double (sf1, buf1, items)) > 0)
+	{	nread2 = sf_readf_double (sf2, buf2, nread1) ;
+		if (nread2 != nread1)
+		{	retval = comparison_error ("PCM data lengths", -1) ;
+			goto out ;
+			} ;
+		for (i = 0 ; i < nread1 * sfinfo1.channels ; i++)
+		{	if (buf1 [i] != buf2 [i])
+			{	retval = comparison_error ("PCM data", offset + i / sfinfo1.channels) ;
+				goto out ;
+				} ;
+			} ;
+		offset += nread1 ;
+		} ;
+
+	if ((nread2 = sf_readf_double (sf2, buf2, items)) != 0)
+	{	retval = comparison_error ("PCM data lengths", -1) ;
+		goto out ;
+		} ;
+
+out :
+	sf_close (sf1) ;
+	sf_close (sf2) ;
+
+	return retval ;
+} /* compare */
+
+static void
+usage_exit (void)
+{
+	printf ("Usage : %s <filename> <filename>\n", progname) ;
+	printf ("	Compare the PCM data of two sound files.\n\n") ;
+	printf ("Using %s.\n\n", sf_version_string ()) ;
+	exit (1) ;
+} /* usage_exit */
+
+int
+main (int argc, char *argv [])
+{
+	progname = program_name (argv [0]) ;
+
+	if (argc != 3)
+		usage_exit () ;
+
+	filename1 = argv [argc - 2] ;
+	filename2 = argv [argc - 1] ;
+
+	if (strcmp (filename1, filename2) == 0)
+	{	printf ("Error : Input filenames are the same.\n\n") ;
+		usage_exit () ;
+		} ;
+
+	return compare () ;
+} /* main */
diff --git a/programs/sndfile-concat.c b/programs/sndfile-concat.c
new file mode 100644
index 0000000..49380fd
--- /dev/null
+++ b/programs/sndfile-concat.c
@@ -0,0 +1,170 @@
+/*
+** Copyright (C) 1999-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in
+**       the documentation and/or other materials provided with the
+**       distribution.
+**     * Neither the author nor the names of any contributors may be used
+**       to endorse or promote products derived from this software without
+**       specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include	<stdio.h>
+#include	<stdlib.h>
+#include	<string.h>
+#include	<ctype.h>
+
+#include	<sndfile.h>
+
+#include	"common.h"
+
+#define		BUFFER_LEN	(1 << 16)
+
+
+static void concat_data_fp (SNDFILE *wfile, SNDFILE *rofile, int channels) ;
+static void concat_data_int (SNDFILE *wfile, SNDFILE *rofile, int channels) ;
+
+static void
+usage_exit (const char *progname)
+{
+	printf ("\nUsage : %s <infile1> <infile2>  ... <outfile>\n\n", progname) ;
+	puts (
+		"    Create a new output file <outfile> containing the concatenated\n"
+		"    audio data from froms <infile1> <infile2> ....\n"
+		"\n"
+		"    The joined file will be encoded in the same format as the data\n"
+		"    in infile1, with all the data in subsequent files automatically\n"
+		"    converted to the correct encoding.\n"
+		"\n"
+		"    The only restriction is that the two files must have the same\n"
+		"    number of channels.\n"
+		) ;
+
+	exit (1) ;
+} /* usage_exit */
+
+int
+main (int argc, char *argv [])
+{	const char	*progname, *outfilename ;
+	SNDFILE		*outfile, **infiles ;
+	SF_INFO		sfinfo_out, sfinfo_in ;
+	void 		(*func) (SNDFILE*, SNDFILE*, int) ;
+	int			k ;
+
+	progname = program_name (argv [0]) ;
+
+	if (argc < 4)
+		usage_exit (progname) ;
+
+	argv ++ ;
+	argc -- ;
+
+	argc -- ;
+	outfilename = argv [argc] ;
+
+	if ((infiles = calloc (argc, sizeof (SNDFILE*))) == NULL)
+	{	printf ("\nError : Malloc failed.\n\n") ;
+		exit (1) ;
+		} ;
+
+	memset (&sfinfo_in, 0, sizeof (sfinfo_in)) ;
+
+	if ((infiles [0] = sf_open (argv [0], SFM_READ, &sfinfo_in)) == NULL)
+	{	printf ("\nError : failed to open file '%s'.\n\n", argv [0]) ;
+		exit (1) ;
+		} ;
+
+	sfinfo_out = sfinfo_in ;
+
+	for (k = 1 ; k < argc ; k++)
+	{	if ((infiles [k] = sf_open (argv [k], SFM_READ, &sfinfo_in)) == NULL)
+		{	printf ("\nError : failed to open file '%s'.\n\n", argv [k]) ;
+			exit (1) ;
+			} ;
+
+		if (sfinfo_in.channels != sfinfo_out.channels)
+		{	printf ("\nError : File '%s' has %d channels (should have %d).\n\n", argv [k], sfinfo_in.channels, sfinfo_out.channels) ;
+			exit (1) ;
+			} ;
+		} ;
+
+	if ((outfile = sf_open (outfilename, SFM_WRITE, &sfinfo_out)) == NULL)
+	{	printf ("\nError : Not able to open input file %s.\n", outfilename) ;
+		puts (sf_strerror (NULL)) ;
+		exit (1) ;
+		} ;
+
+	if ((sfinfo_out.format & SF_FORMAT_SUBMASK) == SF_FORMAT_DOUBLE ||
+			(sfinfo_out.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT)
+		func = concat_data_fp ;
+	else
+		func = concat_data_int ;
+
+	for (k = 0 ; k < argc ; k++)
+	{	func (outfile, infiles [k], sfinfo_out.channels) ;
+		sf_close (infiles [k]) ;
+		} ;
+
+	sf_close (outfile) ;
+	free (infiles) ;
+
+	return 0 ;
+} /* main */
+
+static void
+concat_data_fp (SNDFILE *wfile, SNDFILE *rofile, int channels)
+{	static double	data [BUFFER_LEN] ;
+	int		frames, readcount ;
+
+	frames = BUFFER_LEN / channels ;
+	readcount = frames ;
+
+	sf_seek (wfile, 0, SEEK_END) ;
+
+	while (readcount > 0)
+	{	readcount = (int) sf_readf_double (rofile, data, frames) ;
+		sf_writef_double (wfile, data, readcount) ;
+		} ;
+
+	return ;
+} /* concat_data_fp */
+
+static void
+concat_data_int (SNDFILE *wfile, SNDFILE *rofile, int channels)
+{	static int	data [BUFFER_LEN] ;
+	int		frames, readcount ;
+
+	frames = BUFFER_LEN / channels ;
+	readcount = frames ;
+
+	sf_seek (wfile, 0, SEEK_END) ;
+
+	while (readcount > 0)
+	{	readcount = (int) sf_readf_int (rofile, data, frames) ;
+		sf_writef_int (wfile, data, readcount) ;
+		} ;
+
+	return ;
+} /* concat_data_int */
+
diff --git a/programs/sndfile-convert.c b/programs/sndfile-convert.c
new file mode 100644
index 0000000..b946862
--- /dev/null
+++ b/programs/sndfile-convert.c
@@ -0,0 +1,410 @@
+/*
+** Copyright (C) 1999-2019 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in
+**       the documentation and/or other materials provided with the
+**       distribution.
+**     * Neither the author nor the names of any contributors may be used
+**       to endorse or promote products derived from this software without
+**       specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <sndfile.h>
+
+#include "common.h"
+
+
+typedef	struct
+{	char	*infilename, *outfilename ;
+	SF_INFO	infileinfo, outfileinfo ;
+} OptionData ;
+
+static void copy_metadata (SNDFILE *outfile, SNDFILE *infile, int channels) ;
+
+static void
+usage_exit (const char *progname)
+{
+	printf ("\nUsage : %s [options] [encoding] <input file> <output file>\n", progname) ;
+	puts ("\n"
+		"    where [option] may be:\n\n"
+		"        -override-sample-rate=X  : force sample rate of input to X\n"
+		"        -endian=little           : force output file to little endian data\n"
+		"        -endian=big              : force output file to big endian data\n"
+		"        -endian=cpu              : force output file same endian-ness as the CPU\n"
+		"        -normalize               : normalize the data in the output file\n"
+		) ;
+
+	puts (
+		"    where [encoding] may be one of the following:\n\n"
+		"        -pcms8     : signed 8 bit pcm\n"
+		"        -pcmu8     : unsigned 8 bit pcm\n"
+		"        -pcm16     : 16 bit pcm\n"
+		"        -pcm24     : 24 bit pcm\n"
+		"        -pcm32     : 32 bit pcm\n"
+		"        -float32   : 32 bit floating point\n"
+		"        -float64   : 64 bit floating point\n"
+		"        -ulaw      : ULAW\n"
+		"        -alaw      : ALAW\n"
+		"        -alac16    : 16 bit ALAC (CAF only)\n"
+		"        -alac20    : 20 bit ALAC (CAF only)\n"
+		"        -alac24    : 24 bit ALAC (CAF only)\n"
+		"        -alac32    : 32 bit ALAC (CAF only)\n"
+		"        -ima-adpcm : IMA ADPCM (WAV only)\n"
+		"        -ms-adpcm  : MS ADPCM (WAV only)\n"
+		"        -gsm610    : GSM6.10 (WAV only)\n"
+		"        -dwvw12    : 12 bit DWVW (AIFF only)\n"
+		"        -dwvw16    : 16 bit DWVW (AIFF only)\n"
+		"        -dwvw24    : 24 bit DWVW (AIFF only)\n"
+		"        -vorbis    : Vorbis (OGG only)\n"
+		"        -opus      : Opus (OGG only)\n"
+		) ;
+
+	puts (
+		"    If no encoding is specified, the program will try to use the encoding\n"
+		"    of the input file in the output file. This will not always work as\n"
+		"    most container formats (eg WAV, AIFF etc) only support a small subset\n"
+		"    of codec formats (eg 16 bit PCM, a-law, Vorbis etc).\n"
+		) ;
+
+	puts (
+		"    The format of the output file is determined by the file extension of the\n"
+		"    output file name. The following extensions are currently understood:\n"
+		) ;
+
+	sfe_dump_format_map () ;
+
+	puts ("") ;
+	exit (1) ;
+} /* usage_exit */
+
+static void
+report_format_error_exit (const char * argv0, SF_INFO * sfinfo)
+{	int old_format = sfinfo->format ;
+	int endian = sfinfo->format & SF_FORMAT_ENDMASK ;
+	int channels = sfinfo->channels ;
+
+	sfinfo->format = old_format & (SF_FORMAT_TYPEMASK | SF_FORMAT_SUBMASK) ;
+
+	if (endian && sf_format_check (sfinfo))
+	{	printf ("Error : output file format does not support %s endian-ness.\n", sfe_endian_name (endian)) ;
+		exit (1) ;
+		} ;
+
+	sfinfo->channels = 1 ;
+	if (sf_format_check (sfinfo))
+	{	printf ("Error : output file format does not support %d channels.\n", channels) ;
+		exit (1) ;
+		} ;
+
+	printf ("\n"
+			"Error : output file format is invalid.\n"
+			"The '%s' container does not support '%s' codec data.\n"
+			"Run '%s --help' for clues.\n\n",
+			sfe_container_name (sfinfo->format), sfe_codec_name (sfinfo->format), program_name (argv0)) ;
+	exit (1) ;
+} /* report_format_error_exit */
+
+int
+main (int argc, char * argv [])
+{	const char	*progname, *infilename, *outfilename ;
+	SNDFILE		*infile = NULL, *outfile = NULL ;
+	SF_INFO		sfinfo ;
+	int			k, outfilemajor, outfileminor = 0, infileminor ;
+	int			override_sample_rate = 0 ; /* assume no sample rate override. */
+	int			endian = SF_ENDIAN_FILE, normalize = SF_FALSE ;
+
+	progname = program_name (argv [0]) ;
+
+	if (argc < 3 || argc > 5)
+		usage_exit (progname) ;
+
+	infilename = argv [argc-2] ;
+	outfilename = argv [argc-1] ;
+
+	if (strcmp (infilename, outfilename) == 0)
+	{	printf ("Error : Input and output filenames are the same.\n\n") ;
+		usage_exit (progname) ;
+		} ;
+
+	if (strlen (infilename) > 1 && infilename [0] == '-')
+	{	printf ("Error : Input filename (%s) looks like an option.\n\n", infilename) ;
+		usage_exit (progname) ;
+		} ;
+
+	if (outfilename [0] == '-')
+	{	printf ("Error : Output filename (%s) looks like an option.\n\n", outfilename) ;
+		usage_exit (progname) ;
+		} ;
+
+	for (k = 1 ; k < argc - 2 ; k++)
+	{	if (! strcmp (argv [k], "-pcms8"))
+		{	outfileminor = SF_FORMAT_PCM_S8 ;
+			continue ;
+			} ;
+		if (! strcmp (argv [k], "-pcmu8"))
+		{	outfileminor = SF_FORMAT_PCM_U8 ;
+			continue ;
+			} ;
+		if (! strcmp (argv [k], "-pcm16"))
+		{	outfileminor = SF_FORMAT_PCM_16 ;
+			continue ;
+			} ;
+		if (! strcmp (argv [k], "-pcm24"))
+		{	outfileminor = SF_FORMAT_PCM_24 ;
+			continue ;
+			} ;
+		if (! strcmp (argv [k], "-pcm32"))
+		{	outfileminor = SF_FORMAT_PCM_32 ;
+			continue ;
+			} ;
+		if (! strcmp (argv [k], "-float32"))
+		{	outfileminor = SF_FORMAT_FLOAT ;
+			continue ;
+			} ;
+		if (! strcmp (argv [k], "-float64"))
+		{	outfileminor = SF_FORMAT_DOUBLE ;
+			continue ;
+			} ;
+		if (! strcmp (argv [k], "-ulaw"))
+		{	outfileminor = SF_FORMAT_ULAW ;
+			continue ;
+			} ;
+		if (! strcmp (argv [k], "-alaw"))
+		{	outfileminor = SF_FORMAT_ALAW ;
+			continue ;
+			} ;
+		if (! strcmp (argv [k], "-alac16"))
+		{	outfileminor = SF_FORMAT_ALAC_16 ;
+			continue ;
+			} ;
+		if (! strcmp (argv [k], "-alac20"))
+		{	outfileminor = SF_FORMAT_ALAC_20 ;
+			continue ;
+			} ;
+		if (! strcmp (argv [k], "-alac24"))
+		{	outfileminor = SF_FORMAT_ALAC_24 ;
+			continue ;
+			} ;
+		if (! strcmp (argv [k], "-alac32"))
+		{	outfileminor = SF_FORMAT_ALAC_32 ;
+			continue ;
+			} ;
+		if (! strcmp (argv [k], "-ima-adpcm"))
+		{	outfileminor = SF_FORMAT_IMA_ADPCM ;
+			continue ;
+			} ;
+		if (! strcmp (argv [k], "-ms-adpcm"))
+		{	outfileminor = SF_FORMAT_MS_ADPCM ;
+			continue ;
+			} ;
+		if (! strcmp (argv [k], "-gsm610"))
+		{	outfileminor = SF_FORMAT_GSM610 ;
+			continue ;
+			} ;
+		if (! strcmp (argv [k], "-dwvw12"))
+		{	outfileminor = SF_FORMAT_DWVW_12 ;
+			continue ;
+			} ;
+		if (! strcmp (argv [k], "-dwvw16"))
+		{	outfileminor = SF_FORMAT_DWVW_16 ;
+			continue ;
+			} ;
+		if (! strcmp (argv [k], "-dwvw24"))
+		{	outfileminor = SF_FORMAT_DWVW_24 ;
+			continue ;
+			} ;
+		if (! strcmp (argv [k], "-vorbis"))
+		{	outfileminor = SF_FORMAT_VORBIS ;
+			continue ;
+			} ;
+		if (! strcmp (argv [k], "-opus"))
+		{	outfileminor = SF_FORMAT_OPUS ;
+			continue ;
+			} ;
+
+		if (strstr (argv [k], "-override-sample-rate=") == argv [k])
+		{	const char *ptr ;
+
+			ptr = argv [k] + strlen ("-override-sample-rate=") ;
+			override_sample_rate = atoi (ptr) ;
+			continue ;
+			} ;
+
+		if (! strcmp (argv [k], "-endian=little"))
+		{	endian = SF_ENDIAN_LITTLE ;
+			continue ;
+			} ;
+
+		if (! strcmp (argv [k], "-endian=big"))
+		{	endian = SF_ENDIAN_BIG ;
+			continue ;
+			} ;
+
+		if (! strcmp (argv [k], "-endian=cpu"))
+		{	endian = SF_ENDIAN_CPU ;
+			continue ;
+			} ;
+
+		if (! strcmp (argv [k], "-endian=file"))
+		{	endian = SF_ENDIAN_FILE ;
+			continue ;
+			} ;
+
+		if (! strcmp (argv [k], "-normalize"))
+		{	normalize = SF_TRUE ;
+			continue ;
+			} ;
+
+		printf ("Error : Not able to decode argument '%s'.\n", argv [k]) ;
+		exit (1) ;
+		} ;
+
+	memset (&sfinfo, 0, sizeof (sfinfo)) ;
+
+	if ((infile = sf_open (infilename, SFM_READ, &sfinfo)) == NULL)
+	{	printf ("Not able to open input file %s.\n", infilename) ;
+		puts (sf_strerror (NULL)) ;
+		return 1 ;
+		} ;
+
+	/* Update sample rate if forced to something else. */
+	if (override_sample_rate)
+		sfinfo.samplerate = override_sample_rate ;
+
+	infileminor = sfinfo.format & SF_FORMAT_SUBMASK ;
+
+	if ((sfinfo.format = sfe_file_type_of_ext (outfilename, sfinfo.format)) == 0)
+	{	printf ("Error : Not able to determine output file type for %s.\n", outfilename) ;
+		return 1 ;
+		} ;
+
+	outfilemajor = sfinfo.format & (SF_FORMAT_TYPEMASK | SF_FORMAT_ENDMASK) ;
+
+	if (outfileminor == 0)
+		outfileminor = sfinfo.format & SF_FORMAT_SUBMASK ;
+
+	if (outfileminor != 0)
+		sfinfo.format = outfilemajor | outfileminor ;
+	else
+		sfinfo.format = outfilemajor | (sfinfo.format & SF_FORMAT_SUBMASK) ;
+
+	sfinfo.format |= endian ;
+
+	if ((sfinfo.format & SF_FORMAT_TYPEMASK) == SF_FORMAT_XI)
+		switch (sfinfo.format & SF_FORMAT_SUBMASK)
+		{	case SF_FORMAT_PCM_16 :
+					sfinfo.format = outfilemajor | SF_FORMAT_DPCM_16 ;
+					break ;
+
+			case SF_FORMAT_PCM_S8 :
+			case SF_FORMAT_PCM_U8 :
+					sfinfo.format = outfilemajor | SF_FORMAT_DPCM_8 ;
+					break ;
+			} ;
+
+	if (sf_format_check (&sfinfo) == 0)
+	{	sf_close (infile) ;
+		report_format_error_exit (argv [0], &sfinfo) ;
+		} ;
+
+	if ((sfinfo.format & SF_FORMAT_SUBMASK) == SF_FORMAT_GSM610 && sfinfo.samplerate != 8000)
+	{	printf (
+			"WARNING: GSM 6.10 data format only supports 8kHz sample rate. The converted\n"
+			"ouput file will contain the input data converted to the GSM 6.10 data format\n"
+			"but not re-sampled.\n"
+			) ;
+		} ;
+
+	/* Open the output file. */
+	if ((outfile = sf_open (outfilename, SFM_WRITE, &sfinfo)) == NULL)
+	{	printf ("Not able to open output file %s : %s\n", outfilename, sf_strerror (NULL)) ;
+		return 1 ;
+		} ;
+
+	/* Copy the metadata */
+	copy_metadata (outfile, infile, sfinfo.channels) ;
+
+	if (normalize
+			|| (outfileminor == SF_FORMAT_DOUBLE) || (outfileminor == SF_FORMAT_FLOAT)
+			|| (infileminor == SF_FORMAT_DOUBLE) || (infileminor == SF_FORMAT_FLOAT)
+			|| (infileminor == SF_FORMAT_OPUS) || (outfileminor == SF_FORMAT_OPUS)
+			|| (infileminor == SF_FORMAT_VORBIS) || (outfileminor == SF_FORMAT_VORBIS)
+			|| (infileminor == SF_FORMAT_MPEG_LAYER_I)
+			|| (infileminor == SF_FORMAT_MPEG_LAYER_II)
+			|| (infileminor == SF_FORMAT_MPEG_LAYER_III) || (outfileminor == SF_FORMAT_MPEG_LAYER_III))
+	{	if (sfe_copy_data_fp (outfile, infile, sfinfo.channels, normalize) != 0)
+		{	printf ("Error : Not able to decode input file %s.\n", infilename) ;
+			return 1 ;
+			} ;
+		}
+	else
+		sfe_copy_data_int (outfile, infile, sfinfo.channels) ;
+
+	sf_close (infile) ;
+	sf_close (outfile) ;
+
+	return 0 ;
+} /* main */
+
+static void
+copy_metadata (SNDFILE *outfile, SNDFILE *infile, int channels)
+{	SF_INSTRUMENT inst ;
+	SF_CUES cues ;
+	SF_BROADCAST_INFO_2K binfo ;
+	const char *str ;
+	int k, chanmap [256] ;
+
+	for (k = SF_STR_FIRST ; k <= SF_STR_LAST ; k++)
+	{	str = sf_get_string (infile, k) ;
+		if (str != NULL)
+			sf_set_string (outfile, k, str) ;
+		} ;
+
+	memset (&inst, 0, sizeof (inst)) ;
+	memset (&cues, 0, sizeof (cues)) ;
+	memset (&binfo, 0, sizeof (binfo)) ;
+
+	if (channels < ARRAY_LEN (chanmap))
+	{	int size = channels * sizeof (chanmap [0]) ;
+
+		if (sf_command (infile, SFC_GET_CHANNEL_MAP_INFO, chanmap, size) == SF_TRUE)
+			sf_command (outfile, SFC_SET_CHANNEL_MAP_INFO, chanmap, size) ;
+		} ;
+
+	if (sf_command (infile, SFC_GET_CUE, &cues, sizeof (cues)) == SF_TRUE)
+		sf_command (outfile, SFC_SET_CUE, &cues, sizeof (cues)) ;
+
+	if (sf_command (infile, SFC_GET_INSTRUMENT, &inst, sizeof (inst)) == SF_TRUE)
+		sf_command (outfile, SFC_SET_INSTRUMENT, &inst, sizeof (inst)) ;
+
+	if (sf_command (infile, SFC_GET_BROADCAST_INFO, &binfo, sizeof (binfo)) == SF_TRUE)
+		sf_command (outfile, SFC_SET_BROADCAST_INFO, &binfo, sizeof (binfo)) ;
+
+} /* copy_metadata */
+
diff --git a/programs/sndfile-deinterleave.c b/programs/sndfile-deinterleave.c
new file mode 100644
index 0000000..42d4750
--- /dev/null
+++ b/programs/sndfile-deinterleave.c
@@ -0,0 +1,222 @@
+/*
+** Copyright (C) 2009-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in
+**       the documentation and/or other materials provided with the
+**       distribution.
+**     * Neither the author nor the names of any contributors may be used
+**       to endorse or promote products derived from this software without
+**       specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sndfile.h>
+
+#include "common.h"
+
+#define	BUFFER_LEN	4096
+#define	MAX_CHANNELS	16
+
+
+typedef struct
+{	SNDFILE * infile ;
+	SNDFILE * outfile [MAX_CHANNELS] ;
+
+	union
+	{	double	d [MAX_CHANNELS * BUFFER_LEN] ;
+		int		i [MAX_CHANNELS * BUFFER_LEN] ;
+	} din ;
+
+	union
+	{	double	d [BUFFER_LEN] ;
+		int		i [BUFFER_LEN] ;
+	} dout ;
+
+	int channels ;
+} STATE ;
+
+static void usage_exit (void) ;
+
+static void deinterleave_int (STATE * state) ;
+static void deinterleave_double (STATE * state) ;
+
+int
+main (int argc, char **argv)
+{	STATE *state = NULL ;
+	SF_INFO sfinfo ;
+	char pathname [512], ext [32], *cptr ;
+	int ch, double_split, ret = 1 ;
+
+	if (argc != 2)
+	{	if (argc != 1)
+			puts ("\nError : need a single input file.\n") ;
+		usage_exit () ;
+		goto cleanup ;
+		} ;
+
+	state = calloc (1, sizeof (*state)) ;
+	if (!state)
+	{	printf ("\nError : Out of memory.\n") ;
+		goto cleanup ;
+		} ;
+	memset (&sfinfo, 0, sizeof (sfinfo)) ;
+
+	if ((state->infile = sf_open (argv [1], SFM_READ, &sfinfo)) == NULL)
+	{	printf ("\nError : Not able to open input file '%s'\n%s\n", argv [1], sf_strerror (NULL)) ;
+		goto cleanup ;
+		} ;
+
+	if (sfinfo.channels < 2)
+	{	printf ("\nError : Input file '%s' only has one channel.\n", argv [1]) ;
+		goto cleanup ;
+		} ;
+
+	if (sfinfo.channels > MAX_CHANNELS)
+	{	printf ("\nError : Input file '%s' has too many (%d) channels. Limit is %d.\n",
+			argv [1], sfinfo.channels, MAX_CHANNELS) ;
+		goto cleanup ;
+		} ;
+
+
+	state->channels = sfinfo.channels ;
+	sfinfo.channels = 1 ;
+
+	if (snprintf (pathname, sizeof (pathname), "%s", argv [1]) > (int) sizeof (pathname))
+	{	printf ("\nError : Length of provided filename '%s' exceeds MAX_PATH (%d).\n", argv [1], (int) sizeof (pathname)) ;
+		goto cleanup ;
+		} ;
+
+	if ((cptr = strrchr (pathname, '.')) == NULL)
+		ext [0] = 0 ;
+	else
+	{	snprintf (ext, sizeof (ext), "%s", cptr) ;
+		cptr [0] = 0 ;
+		} ;
+
+	printf ("Input file : %s\n", pathname) ;
+	puts ("Output files :") ;
+
+	for (ch = 0 ; ch < state->channels ; ch++)
+	{	char filename [520] ;
+		size_t count ;
+
+		count = snprintf (filename, sizeof (filename), "%s_%02d%s", pathname, ch, ext) ;
+
+		if (count >= sizeof (filename))
+		{	printf ("File name truncated to %s\n", filename) ;
+			} ;
+
+		if ((state->outfile [ch] = sf_open (filename, SFM_WRITE, &sfinfo)) == NULL)
+		{	printf ("Not able to open output file '%s'\n%s\n", filename, sf_strerror (NULL)) ;
+			goto cleanup ;
+			} ;
+
+		printf ("    %s\n", filename) ;
+		} ;
+
+	switch (sfinfo.format & SF_FORMAT_SUBMASK)
+	{	case SF_FORMAT_FLOAT :
+		case SF_FORMAT_DOUBLE :
+		case SF_FORMAT_VORBIS :
+			double_split = 1 ;
+			break ;
+
+		default :
+			double_split = 0 ;
+			break ;
+		} ;
+
+	if (double_split)
+		deinterleave_double (state) ;
+	else
+		deinterleave_int (state) ;
+
+	ret = 0 ;
+
+cleanup :
+
+	if (state != NULL)
+	{	sf_close (state->infile) ;
+		for (ch = 0 ; ch < MAX_CHANNELS ; ch++)
+			if (state->outfile [ch] != NULL)
+				sf_close (state->outfile [ch]) ;
+		} ;
+
+	free (state) ;
+
+	return ret ;
+} /* main */
+
+/*------------------------------------------------------------------------------
+*/
+
+static void
+usage_exit (void)
+{	puts ("\nUsage : sndfile-deinterleave <filename>\n") ;
+	puts (
+		"Split a mutli-channel file into a set of mono files.\n"
+		"\n"
+		"If the input file is named 'a.wav', the output files will be named\n"
+		"a_00.wav, a_01.wav and so on.\n"
+		) ;
+	printf ("Using %s.\n\n", sf_version_string ()) ;
+} /* usage_exit */
+
+static void
+deinterleave_int (STATE * state)
+{	int read_len ;
+	int ch, k ;
+
+	do
+	{	read_len = (int) sf_readf_int (state->infile, state->din.i, BUFFER_LEN) ;
+
+		for (ch = 0 ; ch < state->channels ; ch ++)
+		{	for (k = 0 ; k < read_len ; k++)
+				state->dout.i [k] = state->din.i [k * state->channels + ch] ;
+			sf_write_int (state->outfile [ch], state->dout.i, read_len) ;
+			} ;
+		}
+	while (read_len > 0) ;
+
+} /* deinterleave_int */
+
+static void
+deinterleave_double (STATE * state)
+{	int read_len ;
+	int ch, k ;
+
+	do
+	{	read_len = (int) sf_readf_double (state->infile, state->din.d, BUFFER_LEN) ;
+
+		for (ch = 0 ; ch < state->channels ; ch ++)
+		{	for (k = 0 ; k < read_len ; k++)
+				state->dout.d [k] = state->din.d [k * state->channels + ch] ;
+			sf_write_double (state->outfile [ch], state->dout.d, read_len) ;
+			} ;
+		}
+	while (read_len > 0) ;
+
+} /* deinterleave_double */
diff --git a/programs/sndfile-info.c b/programs/sndfile-info.c
new file mode 100644
index 0000000..ae44654
--- /dev/null
+++ b/programs/sndfile-info.c
@@ -0,0 +1,529 @@
+/*
+** Copyright (C) 1999-2019 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in
+**       the documentation and/or other materials provided with the
+**       distribution.
+**     * Neither the author nor the names of any contributors may be used
+**       to endorse or promote products derived from this software without
+**       specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include	<stdio.h>
+#include	<stdlib.h>
+#include	<string.h>
+#include	<inttypes.h>
+#include	<ctype.h>
+#include	<math.h>
+
+#include	<sndfile.h>
+
+#include "common.h"
+
+#define	BUFFER_LEN		(1 << 16)
+
+#if (defined (WIN32) || defined (_WIN32))
+#include <windows.h>
+#endif
+
+static void usage_exit (const char *progname) ;
+
+static void info_dump (const char *filename) ;
+static int	instrument_dump (const char *filename) ;
+static int	broadcast_dump (const char *filename) ;
+static int	chanmap_dump (const char *filename) ;
+static int	cart_dump (const char *filename) ;
+static void total_dump (void) ;
+
+static double total_seconds = 0.0 ;
+
+int
+main (int argc, char *argv [])
+{	int	k ;
+
+	if (argc < 2 || strcmp (argv [1], "--help") == 0 || strcmp (argv [1], "-h") == 0)
+		usage_exit (program_name (argv [0])) ;
+
+	if (strcmp (argv [1], "--instrument") == 0)
+	{	int error = 0 ;
+
+		for (k = 2 ; k < argc ; k++)
+			error += instrument_dump (argv [k]) ;
+		return error ;
+		} ;
+
+	if (strcmp (argv [1], "--broadcast") == 0)
+	{	int error = 0 ;
+
+		for (k = 2 ; k < argc ; k++)
+			error += broadcast_dump (argv [k]) ;
+		return error ;
+		} ;
+
+	if (strcmp (argv [1], "--channel-map") == 0)
+	{	int error = 0 ;
+
+		for (k = 2 ; k < argc ; k++)
+			error += chanmap_dump (argv [k]) ;
+		return error ;
+		} ;
+
+	if (strcmp (argv [1], "--cart") == 0)
+	{	int error = 0 ;
+
+		for (k = 2 ; k < argc ; k++)
+			error += cart_dump (argv [k]) ;
+		return error ;
+		} ;
+
+	for (k = 1 ; k < argc ; k++)
+		info_dump (argv [k]) ;
+
+	if (argc > 2)
+		total_dump () ;
+
+	return 0 ;
+} /* main */
+
+/*==============================================================================
+**	Print version and usage.
+*/
+
+static void
+usage_exit (const char *progname)
+{	printf ("Usage :\n  %s <file> ...\n", progname) ;
+	printf ("    Prints out information about one or more sound files.\n\n") ;
+	printf ("  %s --instrument <file>\n", progname) ;
+	printf ("    Prints out the instrument data for the given file.\n\n") ;
+	printf ("  %s --broadcast <file>\n", progname) ;
+	printf ("    Prints out the broadcast WAV info for the given file.\n\n") ;
+	printf ("  %s --channel-map <file>\n", progname) ;
+	printf ("    Prints out the channel map for the given file.\n\n") ;
+	printf ("  %s --cart <file>\n", progname) ;
+	printf ("    Prints out the cart chunk WAV info for the given file.\n\n") ;
+
+	printf ("Using %s.\n\n", sf_version_string ()) ;
+#if (defined (_WIN32) || defined (WIN32))
+		printf ("This is a Unix style command line application which\n"
+				"should be run in a MSDOS box or Command Shell window.\n\n") ;
+		printf ("Sleeping for 5 seconds before exiting.\n\n") ;
+		fflush (stdout) ;
+
+		Sleep (5 * 1000) ;
+#endif
+	exit (1) ;
+} /* usage_exit */
+
+/*==============================================================================
+**	Dumping of sndfile info.
+*/
+
+static double	data [BUFFER_LEN] ;
+
+static double
+calc_decibels (SF_INFO * sfinfo, double max)
+{	double decibels ;
+
+	switch (sfinfo->format & SF_FORMAT_SUBMASK)
+	{	case SF_FORMAT_PCM_U8 :
+		case SF_FORMAT_PCM_S8 :
+			decibels = max / 0x80 ;
+			break ;
+
+		case SF_FORMAT_PCM_16 :
+			decibels = max / 0x8000 ;
+			break ;
+
+		case SF_FORMAT_PCM_24 :
+			decibels = max / 0x800000 ;
+			break ;
+
+		case SF_FORMAT_PCM_32 :
+			decibels = max / 0x80000000 ;
+			break ;
+
+		case SF_FORMAT_FLOAT :
+		case SF_FORMAT_DOUBLE :
+		case SF_FORMAT_VORBIS :
+		case SF_FORMAT_OPUS :
+			decibels = max / 1.0 ;
+			break ;
+
+		default :
+			decibels = max / 0x8000 ;
+			break ;
+		} ;
+
+	return 20.0 * log10 (decibels) ;
+} /* calc_decibels */
+
+static const char *
+format_duration_str (double seconds)
+{	static char str [128] ;
+	int hrs, min ;
+	double sec ;
+
+	memset (str, 0, sizeof (str)) ;
+
+	hrs = (int) (seconds / 3600.0) ;
+	min = (int) ((seconds - (hrs * 3600.0)) / 60.0) ;
+	sec = seconds - (hrs * 3600.0) - (min * 60.0) ;
+
+	snprintf (str, sizeof (str) - 1, "%02d:%02d:%06.3f", hrs, min, sec) ;
+
+	return str ;
+} /* format_duration_str */
+
+static const char *
+generate_duration_str (SF_INFO *sfinfo)
+{
+	double seconds ;
+
+	if (sfinfo->samplerate < 1)
+		return NULL ;
+
+	if (sfinfo->frames / sfinfo->samplerate > 0x7FFFFFFF)
+		return "unknown" ;
+
+	seconds = (1.0 * sfinfo->frames) / sfinfo->samplerate ;
+
+	/* Accumulate the total of all known file durations */
+	total_seconds += seconds ;
+
+	return format_duration_str (seconds) ;
+} /* generate_duration_str */
+
+static void
+info_dump (const char *filename)
+{	static	char	strbuffer [BUFFER_LEN] ;
+	SNDFILE		*file ;
+	SF_INFO		sfinfo ;
+	double		signal_max, decibels ;
+
+	memset (&sfinfo, 0, sizeof (sfinfo)) ;
+
+	if ((file = sf_open (filename, SFM_READ, &sfinfo)) == NULL)
+	{	printf ("Error : Not able to open input file %s.\n", filename) ;
+		fflush (stdout) ;
+		memset (data, 0, sizeof (data)) ;
+		sf_command (file, SFC_GET_LOG_INFO, strbuffer, BUFFER_LEN) ;
+		puts (strbuffer) ;
+		puts (sf_strerror (NULL)) ;
+		return ;
+		} ;
+
+	printf ("========================================\n") ;
+	sf_command (file, SFC_GET_LOG_INFO, strbuffer, BUFFER_LEN) ;
+	puts (strbuffer) ;
+	printf ("----------------------------------------\n") ;
+
+	printf ("Sample Rate : %d\n", sfinfo.samplerate) ;
+
+	if (sfinfo.frames == SF_COUNT_MAX)
+		printf ("Frames      : unknown\n") ;
+	else
+		printf ("Frames      : %" PRId64 "\n", sfinfo.frames) ;
+
+	printf ("Channels    : %d\n", sfinfo.channels) ;
+	printf ("Format      : 0x%08X\n", sfinfo.format) ;
+	printf ("Sections    : %d\n", sfinfo.sections) ;
+	printf ("Seekable    : %s\n", (sfinfo.seekable ? "TRUE" : "FALSE")) ;
+	printf ("Duration    : %s\n", generate_duration_str (&sfinfo)) ;
+
+	if (sfinfo.frames < 100 * 1024 * 1024)
+	{	/* Do not use sf_signal_max because it doesn't work for non-seekable files . */
+		sf_command (file, SFC_CALC_SIGNAL_MAX, &signal_max, sizeof (signal_max)) ;
+		decibels = calc_decibels (&sfinfo, signal_max) ;
+		printf ("Signal Max  : %g (%4.2f dB)\n", signal_max, decibels) ;
+		} ;
+	putchar ('\n') ;
+
+	sf_close (file) ;
+
+} /* info_dump */
+
+/*==============================================================================
+**	Dumping of SF_INSTRUMENT data.
+*/
+
+static const char *
+str_of_type (int mode)
+{	switch (mode)
+	{	case SF_LOOP_NONE : return "none" ;
+		case SF_LOOP_FORWARD : return "fwd " ;
+		case SF_LOOP_BACKWARD : return "back" ;
+		case SF_LOOP_ALTERNATING : return "alt " ;
+		default : break ;
+		} ;
+
+	return "????" ;
+} /* str_of_mode */
+
+static int
+instrument_dump (const char *filename)
+{	SNDFILE	*file ;
+	SF_INFO	sfinfo ;
+	SF_INSTRUMENT inst ;
+	int got_inst, k ;
+
+	memset (&sfinfo, 0, sizeof (sfinfo)) ;
+
+	if ((file = sf_open (filename, SFM_READ, &sfinfo)) == NULL)
+	{	printf ("Error : Not able to open input file %s.\n", filename) ;
+		fflush (stdout) ;
+		memset (data, 0, sizeof (data)) ;
+		puts (sf_strerror (NULL)) ;
+		return 1 ;
+		} ;
+
+	got_inst = sf_command (file, SFC_GET_INSTRUMENT, &inst, sizeof (inst)) ;
+	sf_close (file) ;
+
+	if (got_inst == SF_FALSE)
+	{	printf ("Error : File '%s' does not contain instrument data.\n\n", filename) ;
+		return 1 ;
+		} ;
+
+	printf ("Instrument : %s\n\n", filename) ;
+	printf ("  Gain        : %d\n", inst.gain) ;
+	printf ("  Base note   : %d\n", inst.basenote) ;
+	printf ("  Velocity    : %d - %d\n", (int) inst.velocity_lo, (int) inst.velocity_hi) ;
+	printf ("  Key         : %d - %d\n", (int) inst.key_lo, (int) inst.key_hi) ;
+	printf ("  Loop points : %d\n", inst.loop_count) ;
+
+	for (k = 0 ; k < inst.loop_count ; k++)
+		printf ("  %-2d    Mode : %s    Start : %6" PRIu32 "   End : %6" PRIu32
+			"   Count : %6" PRIu32 "\n", k, str_of_type (inst.loops [k].mode),
+			inst.loops [k].start, inst.loops [k].end, inst.loops [k].count) ;
+
+	putchar ('\n') ;
+	return 0 ;
+} /* instrument_dump */
+
+static int
+broadcast_dump (const char *filename)
+{	SNDFILE	*file ;
+	SF_INFO	sfinfo ;
+	SF_BROADCAST_INFO_2K bext ;
+	double time_ref_sec ;
+	int got_bext ;
+
+	memset (&sfinfo, 0, sizeof (sfinfo)) ;
+
+	if ((file = sf_open (filename, SFM_READ, &sfinfo)) == NULL)
+	{	printf ("Error : Not able to open input file %s.\n", filename) ;
+		fflush (stdout) ;
+		memset (data, 0, sizeof (data)) ;
+		puts (sf_strerror (NULL)) ;
+		return 1 ;
+		} ;
+
+	memset (&bext, 0, sizeof (SF_BROADCAST_INFO_2K)) ;
+
+	got_bext = sf_command (file, SFC_GET_BROADCAST_INFO, &bext, sizeof (bext)) ;
+	sf_close (file) ;
+
+	if (got_bext == SF_FALSE)
+	{	printf ("Error : File '%s' does not contain broadcast information.\n\n", filename) ;
+		return 1 ;
+		} ;
+
+	/*
+	**	From : http://www.ebu.ch/en/technical/publications/userguides/bwf_user_guide.php
+	**
+	**	Time Reference:
+	**		This field is a count from midnight in samples to the first sample
+	**		of the audio sequence.
+	*/
+
+	time_ref_sec = ((pow (2.0, 32) * bext.time_reference_high) + (1.0 * bext.time_reference_low)) / sfinfo.samplerate ;
+
+	printf ("Description              : %.*s\n", (int) sizeof (bext.description), bext.description) ;
+	printf ("Originator               : %.*s\n", (int) sizeof (bext.originator), bext.originator) ;
+	printf ("Origination ref          : %.*s\n", (int) sizeof (bext.originator_reference), bext.originator_reference) ;
+	printf ("Origination date         : %.*s\n", (int) sizeof (bext.origination_date), bext.origination_date) ;
+	printf ("Origination time         : %.*s\n", (int) sizeof (bext.origination_time), bext.origination_time) ;
+
+	if (bext.time_reference_high == 0 && bext.time_reference_low == 0)
+		printf ("Time ref                 : 0\n") ;
+	else
+		printf ("Time ref                 : 0x%x%08x (%.6f seconds)\n", bext.time_reference_high, bext.time_reference_low, time_ref_sec) ;
+
+	printf ("BWF version              : %d\n", bext.version) ;
+
+	if (bext.version >= 1)
+		printf ("UMID                     : %.*s\n", (int) sizeof (bext.umid), bext.umid) ;
+
+	if (bext.version >= 2)
+	{	/* 0x7fff shall be used to designate an unused value */
+		/* valid range: -99.99 .. 99.99 */
+		printf ("Loudness value           : %6.2f LUFS\n", bext.loudness_value / 100.0) ;
+		/* valid range: 0.00 .. 99.99 */
+		printf ("Loudness range           : %6.2f LU\n", bext.loudness_range / 100.0) ;
+		/* valid range: -99.99 .. 99.99 */
+		printf ("Max. true peak level     : %6.2f dBTP\n", bext.max_true_peak_level / 100.0) ;
+		printf ("Max. momentary loudness  : %6.2f LUFS\n", bext.max_momentary_loudness / 100.0) ;
+		printf ("Max. short term loudness : %6.2f LUFS\n", bext.max_shortterm_loudness / 100.0) ;
+		} ;
+
+	printf ("Coding history           : %.*s\n", bext.coding_history_size, bext.coding_history) ;
+
+	return 0 ;
+} /* broadcast_dump */
+
+static int
+chanmap_dump (const char *filename)
+{	SNDFILE	*file ;
+	SF_INFO	sfinfo ;
+	int * channel_map ;
+	int got_chanmap, k ;
+
+	memset (&sfinfo, 0, sizeof (sfinfo)) ;
+
+	if ((file = sf_open (filename, SFM_READ, &sfinfo)) == NULL)
+	{	printf ("Error : Not able to open input file %s.\n", filename) ;
+		fflush (stdout) ;
+		memset (data, 0, sizeof (data)) ;
+		puts (sf_strerror (NULL)) ;
+		return 1 ;
+		} ;
+
+	if ((channel_map = calloc (sfinfo.channels, sizeof (int))) == NULL)
+	{	printf ("Error : malloc failed.\n\n") ;
+		return 1 ;
+		} ;
+
+	got_chanmap = sf_command (file, SFC_GET_CHANNEL_MAP_INFO, channel_map, sfinfo.channels * sizeof (int)) ;
+	sf_close (file) ;
+
+	if (got_chanmap == SF_FALSE)
+	{	printf ("Error : File '%s' does not contain channel map information.\n\n", filename) ;
+		free (channel_map) ;
+		return 1 ;
+		} ;
+
+	printf ("File : %s\n\n", filename) ;
+
+	puts ("    Chan    Position") ;
+	for (k = 0 ; k < sfinfo.channels ; k ++)
+	{	const char * name ;
+
+#define CASE_NAME(x)	case x : name = #x ; break ;
+		switch (channel_map [k])
+		{	CASE_NAME (SF_CHANNEL_MAP_INVALID) ;
+			CASE_NAME (SF_CHANNEL_MAP_MONO) ;
+			CASE_NAME (SF_CHANNEL_MAP_LEFT) ;
+			CASE_NAME (SF_CHANNEL_MAP_RIGHT) ;
+			CASE_NAME (SF_CHANNEL_MAP_CENTER) ;
+			CASE_NAME (SF_CHANNEL_MAP_FRONT_LEFT) ;
+			CASE_NAME (SF_CHANNEL_MAP_FRONT_RIGHT) ;
+			CASE_NAME (SF_CHANNEL_MAP_FRONT_CENTER) ;
+			CASE_NAME (SF_CHANNEL_MAP_REAR_CENTER) ;
+			CASE_NAME (SF_CHANNEL_MAP_REAR_LEFT) ;
+			CASE_NAME (SF_CHANNEL_MAP_REAR_RIGHT) ;
+			CASE_NAME (SF_CHANNEL_MAP_LFE) ;
+			CASE_NAME (SF_CHANNEL_MAP_FRONT_LEFT_OF_CENTER) ;
+			CASE_NAME (SF_CHANNEL_MAP_FRONT_RIGHT_OF_CENTER) ;
+			CASE_NAME (SF_CHANNEL_MAP_SIDE_LEFT) ;
+			CASE_NAME (SF_CHANNEL_MAP_SIDE_RIGHT) ;
+			CASE_NAME (SF_CHANNEL_MAP_TOP_CENTER) ;
+			CASE_NAME (SF_CHANNEL_MAP_TOP_FRONT_LEFT) ;
+			CASE_NAME (SF_CHANNEL_MAP_TOP_FRONT_RIGHT) ;
+			CASE_NAME (SF_CHANNEL_MAP_TOP_FRONT_CENTER) ;
+			CASE_NAME (SF_CHANNEL_MAP_TOP_REAR_LEFT) ;
+			CASE_NAME (SF_CHANNEL_MAP_TOP_REAR_RIGHT) ;
+			CASE_NAME (SF_CHANNEL_MAP_TOP_REAR_CENTER) ;
+			CASE_NAME (SF_CHANNEL_MAP_MAX) ;
+			default : name = "default" ;
+				break ;
+			} ;
+
+		printf ("    %3d     %s\n", k, name) ;
+		} ;
+
+	putchar ('\n') ;
+	free (channel_map) ;
+
+	return 0 ;
+} /* chanmap_dump */
+
+static int
+cart_dump (const char *filename)
+{	SNDFILE	*file ;
+	SF_INFO	sfinfo ;
+	SF_CART_INFO_VAR (1024) cart ;
+	int got_cart, k ;
+
+	memset (&sfinfo, 0, sizeof (sfinfo)) ;
+	memset (&cart, 0, sizeof (cart)) ;
+
+	if ((file = sf_open (filename, SFM_READ, &sfinfo)) == NULL)
+	{	printf ("Error : Not able to open input file %s.\n", filename) ;
+		fflush (stdout) ;
+		memset (data, 0, sizeof (data)) ;
+		puts (sf_strerror (NULL)) ;
+		return 1 ;
+		} ;
+
+	got_cart = sf_command (file, SFC_GET_CART_INFO, &cart, sizeof (cart)) ;
+	sf_close (file) ;
+
+	if (got_cart == SF_FALSE)
+	{	printf ("Error : File '%s' does not contain cart information.\n\n", filename) ;
+		return 1 ;
+		} ;
+
+	printf ("Version        : %.*s\n", (int) sizeof (cart.version), cart.version) ;
+	printf ("Title          : %.*s\n", (int) sizeof (cart.title), cart.title) ;
+	printf ("Artist         : %.*s\n", (int) sizeof (cart.artist), cart.artist) ;
+	printf ("Cut id         : %.*s\n", (int) sizeof (cart.cut_id), cart.cut_id) ;
+	printf ("Category       : %.*s\n", (int) sizeof (cart.category), cart.category) ;
+	printf ("Classification : %.*s\n", (int) sizeof (cart.classification), cart.classification) ;
+	printf ("Out cue        : %.*s\n", (int) sizeof (cart.out_cue), cart.out_cue) ;
+	printf ("Start date     : %.*s\n", (int) sizeof (cart.start_date), cart.start_date) ;
+	printf ("Start time     : %.*s\n", (int) sizeof (cart.start_time), cart.start_time) ;
+	printf ("End date       : %.*s\n", (int) sizeof (cart.end_date), cart.end_date) ;
+	printf ("End time       : %.*s\n", (int) sizeof (cart.end_time), cart.end_time) ;
+	printf ("App id         : %.*s\n", (int) sizeof (cart.producer_app_id), cart.producer_app_id) ;
+	printf ("App version    : %.*s\n", (int) sizeof (cart.producer_app_version), cart.producer_app_version) ;
+	printf ("User defined   : %.*s\n", (int) sizeof (cart.user_def), cart.user_def) ;
+	printf ("Level ref.     : %d\n", cart.level_reference) ;
+	printf ("Post timers    :\n") ;
+
+	for (k = 0 ; k < ARRAY_LEN (cart.post_timers) ; k++)
+		if (cart.post_timers [k].usage [0])
+			printf ("  %d   %.*s    %d\n", k, (int) sizeof (cart.post_timers [k].usage), cart.post_timers [k].usage, cart.post_timers [k].value) ;
+
+	printf ("Reserved       : %.*s\n", (int) sizeof (cart.reserved), cart.reserved) ;
+	printf ("Url            : %.*s\n", (int) sizeof (cart.url), cart.url) ;
+	printf ("Tag text       : %.*s\n", cart.tag_text_size, cart.tag_text) ;
+
+	return 0 ;
+} /* cart_dump */
+
+static void
+total_dump (void)
+{	printf ("========================================\n") ;
+	printf ("Total Duration : %s\n", format_duration_str (total_seconds)) ;
+} /* total_dump */
diff --git a/programs/sndfile-interleave.c b/programs/sndfile-interleave.c
new file mode 100644
index 0000000..2b44efb
--- /dev/null
+++ b/programs/sndfile-interleave.c
@@ -0,0 +1,217 @@
+/*
+** Copyright (C) 2009-2015 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in
+**       the documentation and/or other materials provided with the
+**       distribution.
+**     * Neither the author nor the names of any contributors may be used
+**       to endorse or promote products derived from this software without
+**       specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sndfile.h>
+
+#include "common.h"
+
+#define	BUFFER_LEN	4096
+#define	MAX_INPUTS	16
+
+
+typedef struct
+{	SNDFILE * infile [MAX_INPUTS] ;
+	SNDFILE * outfile ;
+
+	union
+	{	double	d [BUFFER_LEN] ;
+		int		i [BUFFER_LEN] ;
+	} din ;
+
+	union
+
+	{	double	d [MAX_INPUTS * BUFFER_LEN] ;
+		int		i [MAX_INPUTS * BUFFER_LEN] ;
+	} dout ;
+
+	int channels ;
+} STATE ;
+
+
+static void print_usage (void) ;
+static void interleave_int (STATE * state) ;
+static void interleave_double (STATE * state) ;
+
+
+int
+main (int argc, char **argv)
+{	STATE *state = NULL ;
+	SF_INFO sfinfo ;
+	int k, double_merge = 0 ;
+	int ret = 1 ;
+
+	if (argc < 5)
+	{	if (argc > 1)
+			puts ("\nError : need at least 2 input files.") ;
+		print_usage () ;
+		goto cleanup ;
+		} ;
+
+	if (strcmp (argv [argc - 2], "-o") != 0)
+	{	puts ("\nError : second last command line parameter should be '-o'.\n") ;
+		print_usage () ;
+		goto cleanup ;
+		} ;
+
+	if (argc - 3 > MAX_INPUTS)
+	{	printf ("\nError : Cannot handle more than %d input channels.\n\n", MAX_INPUTS) ;
+		goto cleanup ;
+		} ;
+
+	state = calloc (1, sizeof (STATE)) ;
+	if (state == NULL)
+	{	puts ("\nError : out of memory.\n") ;
+		goto cleanup ;
+		} ;
+
+	memset (&sfinfo, 0, sizeof (sfinfo)) ;
+
+	for (k = 1 ; k < argc - 2 ; k++)
+	{
+		if ((state->infile [k - 1] = sf_open (argv [k], SFM_READ, &sfinfo)) == NULL)
+		{	printf ("\nError : Not able to open input file '%s'\n%s\n", argv [k], sf_strerror (NULL)) ;
+			goto cleanup ;
+			} ;
+
+		if (sfinfo.channels != 1)
+		{	printf ("\bError : Input file '%s' should be mono (has %d channels).\n", argv [k], sfinfo.channels) ;
+			goto cleanup ;
+			} ;
+
+		switch (sfinfo.format & SF_FORMAT_SUBMASK)
+		{	case SF_FORMAT_FLOAT :
+			case SF_FORMAT_DOUBLE :
+			case SF_FORMAT_VORBIS :
+				double_merge = 1 ;
+				break ;
+
+			default :
+				break ;
+			} ;
+
+		state->channels ++ ;
+		} ;
+
+	sfinfo.channels = state->channels ;
+	sfinfo.format = sfe_file_type_of_ext (argv [argc - 1], sfinfo.format) ;
+
+	if ((state->outfile = sf_open (argv [argc - 1], SFM_WRITE, &sfinfo)) == NULL)
+	{	printf ("Not able to open output file '%s'\n%s\n", argv [argc - 1], sf_strerror (NULL)) ;
+		goto cleanup ;
+		} ;
+
+	if (double_merge)
+		interleave_double (state) ;
+	else
+		interleave_int (state) ;
+
+	ret = 0 ;
+
+cleanup :
+
+	if (state != NULL)
+	{	for (k = 0 ; k < MAX_INPUTS ; k++)
+			if (state->infile [k] != NULL)
+				sf_close (state->infile [k]) ;
+		sf_close (state->outfile) ;
+		}
+
+	free (state) ;
+
+	return ret ;
+} /* main */
+
+/*------------------------------------------------------------------------------
+*/
+
+
+static void
+print_usage (void)
+{	puts ("\nUsage : sndfile-interleave <input 1> <input 2> ... -o <output file>\n") ;
+	puts ("Merge two or more mono files into a single multi-channel file.\n") ;
+	printf ("Using %s.\n\n", sf_version_string ()) ;
+} /* print_usage */
+
+
+static void
+interleave_int (STATE * state)
+{	int max_read_len, read_len ;
+	int ch, k ;
+
+	do
+	{	max_read_len = 0 ;
+
+		for (ch = 0 ; ch < state->channels ; ch ++)
+		{	read_len = (int) sf_read_int (state->infile [ch], state->din.i, BUFFER_LEN) ;
+			if (read_len < BUFFER_LEN)
+				memset (state->din.i + read_len, 0, sizeof (state->din.i [0]) * (BUFFER_LEN - read_len)) ;
+
+			for (k = 0 ; k < BUFFER_LEN ; k++)
+				state->dout.i [k * state->channels + ch] = state->din.i [k] ;
+
+			max_read_len = MAX (max_read_len, read_len) ;
+			} ;
+
+		sf_writef_int (state->outfile, state->dout.i, max_read_len) ;
+		}
+	while (max_read_len > 0) ;
+
+} /* interleave_int */
+
+
+static void
+interleave_double (STATE * state)
+{	int max_read_len, read_len ;
+	int ch, k ;
+
+	do
+	{	max_read_len = 0 ;
+
+		for (ch = 0 ; ch < state->channels ; ch ++)
+		{	read_len = (int) sf_read_double (state->infile [ch], state->din.d, BUFFER_LEN) ;
+			if (read_len < BUFFER_LEN)
+				memset (state->din.d + read_len, 0, sizeof (state->din.d [0]) * (BUFFER_LEN - read_len)) ;
+
+			for (k = 0 ; k < BUFFER_LEN ; k++)
+				state->dout.d [k * state->channels + ch] = state->din.d [k] ;
+
+			max_read_len = MAX (max_read_len, read_len) ;
+			} ;
+
+		sf_writef_double (state->outfile, state->dout.d, max_read_len) ;
+		}
+	while (max_read_len > 0) ;
+
+} /* interleave_double */
diff --git a/programs/sndfile-metadata-get.c b/programs/sndfile-metadata-get.c
new file mode 100644
index 0000000..9a09638
--- /dev/null
+++ b/programs/sndfile-metadata-get.c
@@ -0,0 +1,197 @@
+/*
+** Copyright (C) 2008-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2008-2010 George Blood Audio
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in
+**       the documentation and/or other materials provided with the
+**       distribution.
+**     * Neither the author nor the names of any contributors may be used
+**       to endorse or promote products derived from this software without
+**       specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+
+#include <sndfile.h>
+
+#include "common.h"
+
+#define	BUFFER_LEN		(1 << 16)
+
+static void usage_exit (const char *progname, int exit_code) ;
+static void process_args (SNDFILE * file, const SF_BROADCAST_INFO_2K * binfo, int argc, char * argv []) ;
+
+int
+main (int argc, char *argv [])
+{	SNDFILE *file ;
+	SF_INFO sfinfo ;
+	SF_BROADCAST_INFO_2K binfo ;
+	const char *progname ;
+	const char * filename = NULL ;
+	int	start ;
+
+	/* Store the program name. */
+	progname = program_name (argv [0]) ;
+
+	/* Check if we've been asked for help. */
+	if (argc < 2 || strcmp (argv [1], "--help") == 0 || strcmp (argv [1], "-h") == 0)
+		usage_exit (progname, 0) ;
+
+	if (argv [argc - 1][0] != '-')
+	{	filename = argv [argc - 1] ;
+		start = 1 ;
+		}
+	else if (argv [1][0] != '-')
+	{	filename = argv [1] ;
+		start = 2 ;
+		}
+	else
+	{	printf ("Error : Either the first or the last command line parameter should be a filename.\n\n") ;
+		exit (1) ;
+		} ;
+
+	memset (&sfinfo, 0, sizeof (sfinfo)) ;
+	if ((file = sf_open (filename, SFM_READ, &sfinfo)) == NULL)
+	{	printf ("Error : Open of file '%s' failed : %s\n\n", filename, sf_strerror (file)) ;
+		exit (1) ;
+		} ;
+
+	memset (&binfo, 0, sizeof (binfo)) ;
+	if (sf_command (file, SFC_GET_BROADCAST_INFO, &binfo, sizeof (binfo)) == 0)
+		memset (&binfo, 0, sizeof (binfo)) ;
+
+	process_args (file, &binfo, argc - 2, argv + start) ;
+
+	sf_close (file) ;
+	return 0 ;
+} /* main */
+
+/*==============================================================================
+**	Print version and usage.
+*/
+
+static void
+usage_exit (const char *progname, int exit_code)
+{	printf ("\nUsage :\n  %s [options] <file>\n\nOptions:\n", progname) ;
+
+	puts (
+		"    --bext-description     Print the 'bext' description.\n"
+		"    --bext-originator      Print the 'bext' originator info.\n"
+		"    --bext-orig-ref        Print the 'bext' origination reference.\n"
+		"    --bext-umid            Print the 'bext' UMID.\n"
+		"    --bext-orig-date       Print the 'bext' origination date.\n"
+		"    --bext-orig-time       Print the 'bext' origination time.\n"
+		"    --bext-loudness-value  Print the 'bext' loudness value.\n"
+		"    --bext-loudness-range  Print the 'bext' loudness range.\n"
+		"    --bext-max-truepeak    Print the 'bext' max. true peak level\n"
+		"    --bext-max-momentary   Print the 'bext' max. momentary loudness\n"
+		"    --bext-max-shortterm   Print the 'bext' max. short term loudness\n"
+		"    --bext-coding-hist     Print the 'bext' coding history.\n"
+		) ;
+
+	puts (
+		"    --str-title            Print the title metadata.\n"
+		"    --str-copyright        Print the copyright metadata.\n"
+		"    --str-artist           Print the artist metadata.\n"
+		"    --str-comment          Print the comment metadata.\n"
+		"    --str-date             Print the creation date metadata.\n"
+		"    --str-album            Print the album metadata.\n"
+		"    --str-license          Print the license metadata.\n"
+		) ;
+
+	printf ("Using %s.\n\n", sf_version_string ()) ;
+	exit (exit_code) ;
+} /* usage_exit */
+
+static void
+process_args (SNDFILE * file, const SF_BROADCAST_INFO_2K * binfo, int argc, char * argv [])
+{	const char * str ;
+	int k, do_all = 0 ;
+
+#define HANDLE_BEXT_ARG(cmd, name, field) \
+		if (do_all || strcmp (argv [k], cmd) == 0) \
+		{	printf ("%-22s : %.*s\n", name, (int) sizeof (binfo->field), binfo->field) ; \
+			if (! do_all) \
+				continue ; \
+			} ;
+
+#define HANDLE_BEXT_ARG_INT(cmd, name, field) \
+		if (do_all || strcmp (argv [k], cmd) == 0) \
+		{	printf ("%-22s : %6.2f\n", name, binfo->field / 100.0) ; \
+			if (! do_all) \
+				continue ; \
+			} ;
+
+#define HANDLE_STR_ARG(cmd, name, id) \
+		if (do_all || strcmp (argv [k], cmd) == 0) \
+		{	str = sf_get_string (file, id) ; \
+			printf ("%-22s : %s\n", name, str ? str : "") ; \
+			if (! do_all) continue ; \
+			} ;
+
+	if (argc == 0)
+	{	do_all = 1 ;
+		argc = 1 ;
+		} ;
+
+	for (k = 0 ; k < argc ; k++)
+	{	if (do_all || strcmp (argv [k], "--all") == 0)
+			do_all = 1 ;
+
+		HANDLE_BEXT_ARG ("--bext-description", "Description", description) ;
+		HANDLE_BEXT_ARG ("--bext-originator", "Originator", originator) ;
+		HANDLE_BEXT_ARG ("--bext-orig-ref", "Origination ref", originator_reference) ;
+		HANDLE_BEXT_ARG ("--bext-umid", "UMID", umid) ;
+		HANDLE_BEXT_ARG ("--bext-orig-date", "Origination date", origination_date) ;
+		HANDLE_BEXT_ARG ("--bext-orig-time", "Origination time", origination_time) ;
+		HANDLE_BEXT_ARG_INT ("--bext-loudness-value", "Loudness value", loudness_value) ;
+		HANDLE_BEXT_ARG_INT ("--bext-loudness-range", "Loudness range", loudness_range) ;
+		HANDLE_BEXT_ARG_INT ("--bext-max-truepeak", "Max. true peak level", max_true_peak_level) ;
+		HANDLE_BEXT_ARG_INT ("--bext-max-momentary", "Max. momentary level", max_momentary_loudness) ;
+		HANDLE_BEXT_ARG_INT ("--bext-max-shortterm", "Max. short term level", max_shortterm_loudness) ;
+		HANDLE_BEXT_ARG ("--bext-coding-hist", "Coding history", coding_history) ;
+
+		HANDLE_STR_ARG ("--str-title", "Name", SF_STR_TITLE) ;
+		HANDLE_STR_ARG ("--str-copyright", "Copyright", SF_STR_COPYRIGHT) ;
+		HANDLE_STR_ARG ("--str-artist", "Artist", SF_STR_ARTIST) ;
+		HANDLE_STR_ARG ("--str-comment", "Comment", SF_STR_COMMENT) ;
+		HANDLE_STR_ARG ("--str-date", "Create date", SF_STR_DATE) ;
+		HANDLE_STR_ARG ("--str-album", "Album", SF_STR_ALBUM) ;
+		HANDLE_STR_ARG ("--str-license", "License", SF_STR_LICENSE) ;
+
+		if (! do_all)
+		{	printf ("Error : Don't know what to do with command line arg '%s'.\n\n", argv [k]) ;
+			exit (1) ;
+			} ;
+		break ;
+		} ;
+
+	return ;
+} /* process_args */
diff --git a/programs/sndfile-metadata-set.c b/programs/sndfile-metadata-set.c
new file mode 100644
index 0000000..33c7939
--- /dev/null
+++ b/programs/sndfile-metadata-set.c
@@ -0,0 +1,298 @@
+/*
+** Copyright (C) 2008-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2008-2010 George Blood Audio
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in
+**       the documentation and/or other materials provided with the
+**       distribution.
+**     * Neither the author nor the names of any contributors may be used
+**       to endorse or promote products derived from this software without
+**       specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+
+#include <sndfile.h>
+
+#include "common.h"
+
+#define	BUFFER_LEN		(1 << 16)
+
+
+static void usage_exit (const char *progname, int exit_code) ;
+static void missing_param (const char * option) ;
+static void read_localtime (struct tm * timedata) ;
+static int has_bext_fields_set (const METADATA_INFO * info) ;
+
+int
+main (int argc, char *argv [])
+{	METADATA_INFO info ;
+	struct tm timedata ;
+	const char *progname ;
+	const char * filenames [2] = { NULL, NULL } ;
+	char date [128], time [128] ;
+	int	k ;
+
+	/* Store the program name. */
+	progname = program_name (argv [0]) ;
+
+	/* Check if we've been asked for help. */
+	if (argc < 3 || strcmp (argv [1], "--help") == 0 || strcmp (argv [1], "-h") == 0)
+		usage_exit (progname, 0) ;
+
+	/* Set all fields of the struct to zero bytes. */
+	memset (&info, 0, sizeof (info)) ;
+
+	/* Get the time in case we need it later. */
+	read_localtime (&timedata) ;
+
+	for (k = 1 ; k < argc ; k++)
+	{	if (argv [k][0] != '-')
+		{	if (filenames [0] == NULL)
+				filenames [0] = argv [k] ;
+			else if (filenames [1] == NULL)
+				filenames [1] = argv [k] ;
+			else
+			{	printf ("Error : Already have two file names on the command line and then found '%s'.\n\n", argv [k]) ;
+				usage_exit (progname, 1) ;
+				} ;
+			continue ;
+			} ;
+
+#define HANDLE_BEXT_ARG(cmd, field) \
+		if (strcmp (argv [k], cmd) == 0) \
+		{	k ++ ; \
+			if (k == argc) missing_param (argv [k - 1]) ; \
+			info.field = argv [k] ; \
+			continue ; \
+			} ;
+
+		HANDLE_BEXT_ARG ("--bext-description", description) ;
+		HANDLE_BEXT_ARG ("--bext-originator", originator) ;
+		HANDLE_BEXT_ARG ("--bext-orig-ref", originator_reference) ;
+		HANDLE_BEXT_ARG ("--bext-umid", umid) ;
+		HANDLE_BEXT_ARG ("--bext-orig-date", origination_date) ;
+		HANDLE_BEXT_ARG ("--bext-orig-time", origination_time) ;
+		HANDLE_BEXT_ARG ("--bext-loudness-value", loudness_value) ;
+		HANDLE_BEXT_ARG ("--bext-loudness-range", loudness_range) ;
+		HANDLE_BEXT_ARG ("--bext-max-truepeak", max_true_peak_level) ;
+		HANDLE_BEXT_ARG ("--bext-max-momentary", max_momentary_loudness) ;
+		HANDLE_BEXT_ARG ("--bext-max-shortterm", max_shortterm_loudness) ;
+		HANDLE_BEXT_ARG ("--bext-coding-hist", coding_history) ;
+		HANDLE_BEXT_ARG ("--bext-time-ref", time_ref) ;
+
+#define HANDLE_STR_ARG(cmd, field) \
+	if (strcmp (argv [k], cmd) == 0) \
+	{	k ++ ; \
+		if (k == argc) missing_param (argv [k - 1]) ; \
+		info.field = argv [k] ; \
+		continue ; \
+		} ;
+
+		HANDLE_STR_ARG ("--str-comment", comment) ;
+		HANDLE_STR_ARG ("--str-title", title) ;
+		HANDLE_STR_ARG ("--str-copyright", copyright) ;
+		HANDLE_STR_ARG ("--str-artist", artist) ;
+		HANDLE_STR_ARG ("--str-date", date) ;
+		HANDLE_STR_ARG ("--str-album", album) ;
+		HANDLE_STR_ARG ("--str-license", license) ;
+
+		/* Following options do not take an argument. */
+		if (strcmp (argv [k], "--bext-auto-time-date") == 0)
+		{	snprintf (time, sizeof (time), "%02d:%02d:%02d", timedata.tm_hour, timedata.tm_min, timedata.tm_sec) ;
+			info.origination_time = time ;
+
+			snprintf (date, sizeof (date), "%04d-%02d-%02d", timedata.tm_year + 1900, timedata.tm_mon + 1, timedata.tm_mday) ;
+			info.origination_date = date ;
+			continue ;
+			} ;
+
+		if (strcmp (argv [k], "--bext-auto-time") == 0)
+		{	snprintf (time, sizeof (time), "%02d:%02d:%02d", timedata.tm_hour, timedata.tm_min, timedata.tm_sec) ;
+			info.origination_time = time ;
+			continue ;
+			} ;
+
+		if (strcmp (argv [k], "--bext-auto-date") == 0)
+		{	snprintf (date, sizeof (date), "%04d-%02d-%02d", timedata.tm_year + 1900, timedata.tm_mon + 1, timedata.tm_mday) ;
+			info.origination_date = strdup (date) ;
+			continue ;
+			} ;
+
+		if (strcmp (argv [k], "--str-auto-date") == 0)
+		{	snprintf (date, sizeof (date), "%04d-%02d-%02d", timedata.tm_year + 1900, timedata.tm_mon + 1, timedata.tm_mday) ;
+
+			info.date = strdup (date) ;
+			continue ;
+			} ;
+
+		printf ("Error : Don't know what to do with command line arg '%s'.\n\n", argv [k]) ;
+		usage_exit (progname, 1) ;
+		} ;
+
+	/* Find out if any of the 'bext' fields are set. */
+	info.has_bext_fields = has_bext_fields_set (&info) ;
+
+	if (filenames [0] == NULL)
+	{	printf ("Error : No input file specificed.\n\n") ;
+		exit (1) ;
+		} ;
+
+	if (filenames [1] != NULL && strcmp (filenames [0], filenames [1]) == 0)
+	{	printf ("Error : Input and output files are the same.\n\n") ;
+		exit (1) ;
+		} ;
+
+	if (info.coding_history != NULL && filenames [1] == NULL)
+	{	printf ("\n"
+			"Error : Trying to update coding history of an existing file which unfortunately\n"
+			"        is not supported. Instead, create a new file using :\n"
+			"\n"
+			"        %s --bext-coding-hist \"Coding history\" old_file.wav new_file.wav\n"
+			"\n",
+			progname) ;
+		exit (1) ;
+		} ;
+
+	sfe_apply_metadata_changes (filenames, &info) ;
+
+	return 0 ;
+} /* main */
+
+/*==============================================================================
+**	Print version and usage.
+*/
+
+static void
+usage_exit (const char *progname, int exit_code)
+{	printf ("\nUsage :\n\n"
+		"  %s [options] <file>\n"
+		"  %s [options] <input file> <output file>\n"
+		"\n",
+		progname, progname) ;
+
+	puts (
+		"Where an option is made up of a pair of a field to set (one of\n"
+		"the 'bext' or metadata fields below) and a string. Fields are\n"
+		"as follows :\n"
+		) ;
+
+	puts (
+		"    --bext-description       Set the 'bext' description.\n"
+		"    --bext-originator        Set the 'bext' originator.\n"
+		"    --bext-orig-ref          Set the 'bext' originator reference.\n"
+		"    --bext-umid              Set the 'bext' UMID.\n"
+		"    --bext-orig-date         Set the 'bext' origination date.\n"
+		"    --bext-orig-time         Set the 'bext' origination time.\n"
+		"    --bext-loudness-value    Set the 'bext' loudness value.\n"
+		"    --bext-loudness-range    Set the 'bext' loudness range.\n"
+		"    --bext-max-truepeak      Set the 'bext' max. true peak level\n"
+		"    --bext-max-momentary     Set the 'bext' max. momentary loudness\n"
+		"    --bext-max-shortterm     Set the 'bext' max. short term loudness\n"
+		"    --bext-coding-hist       Set the 'bext' coding history.\n"
+		"    --bext-time-ref          Set the 'bext' Time ref.\n"
+		"\n"
+		"    --str-comment            Set the metadata comment.\n"
+		"    --str-title              Set the metadata title.\n"
+		"    --str-copyright          Set the metadata copyright.\n"
+		"    --str-artist             Set the metadata artist.\n"
+		"    --str-date               Set the metadata date.\n"
+		"    --str-album              Set the metadata album.\n"
+		"    --str-license            Set the metadata license.\n"
+		) ;
+
+	puts (
+		"There are also the following arguments which do not take a\n"
+		"parameter :\n\n"
+		"    --bext-auto-time-date    Set the 'bext' time and date to current time/date.\n"
+		"    --bext-auto-time         Set the 'bext' time to current time.\n"
+		"    --bext-auto-date         Set the 'bext' date to current date.\n"
+		"    --str-auto-date          Set the metadata date to current date.\n"
+		) ;
+
+	puts (
+		"Most of the above operations can be done in-place on an existing\n"
+		"file. If any operation cannot be performed, the application will\n"
+		"exit with an appropriate error message.\n"
+		) ;
+
+	printf ("Using %s.\n\n", sf_version_string ()) ;
+	exit (exit_code) ;
+} /* usage_exit */
+
+static void
+missing_param (const char * option)
+{
+	printf ("Error : Option '%s' needs a parameter but doesn't seem to have one.\n\n", option) ;
+	exit (1) ;
+} /* missing_param */
+
+/*==============================================================================
+*/
+
+static int
+has_bext_fields_set (const METADATA_INFO * info)
+{
+	if (info->description || info->originator || info->originator_reference)
+		return 1 ;
+
+	if (info->origination_date || info->origination_time || info->umid || info->coding_history || info->time_ref)
+		return 1 ;
+
+	if (info->loudness_value || info->loudness_range || info->max_true_peak_level || info->max_momentary_loudness || info->max_shortterm_loudness)
+		return 1 ;
+
+	return 0 ;
+} /* has_bext_fields_set */
+
+static void
+read_localtime (struct tm * timedata)
+{	time_t		current ;
+
+	time (&current) ;
+	memset (timedata, 0, sizeof (struct tm)) ;
+
+#if defined (_WIN32)
+	/* If the re-entrant version is available, use it. */
+	localtime_s (timedata, &current) ;
+#elif defined (HAVE_LOCALTIME_R)
+	/* If the re-entrant version is available, use it. */
+	localtime_r (&current, timedata) ;
+#elif defined (HAVE_LOCALTIME)
+	{
+		struct tm	*tmptr ;
+		/* Otherwise use the standard one and copy the data to local storage. */
+		if ((tmptr = localtime (&current)) != NULL)
+			memcpy (timedata, tmptr, sizeof (struct tm)) ;
+	}
+#endif
+
+	return ;
+} /* read_localtime */
diff --git a/programs/sndfile-play.c b/programs/sndfile-play.c
new file mode 100644
index 0000000..48b95c1
--- /dev/null
+++ b/programs/sndfile-play.c
@@ -0,0 +1,860 @@
+/*
+** Copyright (C) 1999-2018 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in
+**       the documentation and/or other materials provided with the
+**       distribution.
+**     * Neither the author nor the names of any contributors may be used
+**       to endorse or promote products derived from this software without
+**       specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#else
+#include "sf_unistd.h"
+#endif
+
+#include <sndfile.h>
+
+#include "common.h"
+
+#if HAVE_ALSA_ASOUNDLIB_H
+	#define ALSA_PCM_NEW_HW_PARAMS_API
+	#define ALSA_PCM_NEW_SW_PARAMS_API
+	#include <alsa/asoundlib.h>
+	#include <sys/time.h>
+#endif
+
+#if defined (__ANDROID__)
+
+#elif defined (__linux__) || defined (__FreeBSD_kernel__) || defined (__FreeBSD__) || defined (__riscos__)
+	#include 	<fcntl.h>
+	#include 	<sys/ioctl.h>
+	#include 	<sys/soundcard.h>
+
+#elif HAVE_SNDIO_H
+	#include <sndio.h>
+
+#elif (defined (sun) && defined (unix)) || defined(__NetBSD__)
+	#include <fcntl.h>
+	#include <sys/ioctl.h>
+	#include <sys/audioio.h>
+
+#elif (OS_IS_WIN32 == 1)
+	#include <windows.h>
+	#include <mmsystem.h>
+
+#endif
+
+#define	SIGNED_SIZEOF(x)	((int) sizeof (x))
+#define	BUFFER_LEN			(2048)
+
+/*------------------------------------------------------------------------------
+**	Linux/OSS functions for playing a sound.
+*/
+
+#if HAVE_ALSA_ASOUNDLIB_H
+
+static snd_pcm_t * alsa_open (int channels, unsigned srate, int realtime) ;
+static int alsa_write_float (snd_pcm_t *alsa_dev, float *data, int frames, int channels) ;
+
+static void
+alsa_play (int argc, char *argv [])
+{	static float buffer [BUFFER_LEN] ;
+	SNDFILE *sndfile ;
+	SF_INFO sfinfo ;
+	snd_pcm_t * alsa_dev ;
+	int		k, readcount, subformat ;
+
+	for (k = 1 ; k < argc ; k++)
+	{	memset (&sfinfo, 0, sizeof (sfinfo)) ;
+
+		printf ("Playing %s\n", argv [k]) ;
+		if (! (sndfile = sf_open (argv [k], SFM_READ, &sfinfo)))
+		{	puts (sf_strerror (NULL)) ;
+			continue ;
+			} ;
+
+		if (sfinfo.channels < 1 || sfinfo.channels > 2)
+		{	printf ("Error : channels = %d.\n", sfinfo.channels) ;
+			continue ;
+			} ;
+
+		if ((alsa_dev = alsa_open (sfinfo.channels, (unsigned) sfinfo.samplerate, SF_FALSE)) == NULL)
+			continue ;
+
+		subformat = sfinfo.format & SF_FORMAT_SUBMASK ;
+
+		if (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE)
+		{	double	scale ;
+			int 	m ;
+
+			sf_command (sndfile, SFC_CALC_SIGNAL_MAX, &scale, sizeof (scale)) ;
+			if (scale > 1.0)
+				scale = 1.0 / scale ;
+			else
+				scale = 1.0 ;
+
+			while ((readcount = sf_read_float (sndfile, buffer, BUFFER_LEN)))
+			{	for (m = 0 ; m < readcount ; m++)
+					buffer [m] *= scale ;
+				alsa_write_float (alsa_dev, buffer, BUFFER_LEN / sfinfo.channels, sfinfo.channels) ;
+				} ;
+			}
+		else
+		{	while ((readcount = sf_read_float (sndfile, buffer, BUFFER_LEN)))
+				alsa_write_float (alsa_dev, buffer, BUFFER_LEN / sfinfo.channels, sfinfo.channels) ;
+			} ;
+
+		snd_pcm_drain (alsa_dev) ;
+		snd_pcm_close (alsa_dev) ;
+
+		sf_close (sndfile) ;
+		} ;
+
+	return ;
+} /* alsa_play */
+
+static snd_pcm_t *
+alsa_open (int channels, unsigned samplerate, int realtime)
+{	const char * device = "default" ;
+	snd_pcm_t *alsa_dev = NULL ;
+	snd_pcm_hw_params_t *hw_params ;
+	snd_pcm_uframes_t buffer_size ;
+	snd_pcm_uframes_t alsa_period_size, alsa_buffer_frames ;
+	snd_pcm_sw_params_t *sw_params ;
+
+	int err ;
+
+	if (realtime)
+	{	alsa_period_size = 256 ;
+		alsa_buffer_frames = 3 * alsa_period_size ;
+		}
+	else
+	{	alsa_period_size = 1024 ;
+		alsa_buffer_frames = 4 * alsa_period_size ;
+		} ;
+
+	if ((err = snd_pcm_open (&alsa_dev, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0)
+	{	fprintf (stderr, "cannot open audio device \"%s\" (%s)\n", device, snd_strerror (err)) ;
+		goto catch_error ;
+		} ;
+
+	snd_pcm_nonblock (alsa_dev, 0) ;
+
+	if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0)
+	{	fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n", snd_strerror (err)) ;
+		goto catch_error ;
+		} ;
+
+	if ((err = snd_pcm_hw_params_any (alsa_dev, hw_params)) < 0)
+	{	fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n", snd_strerror (err)) ;
+		goto catch_error ;
+		} ;
+
+	if ((err = snd_pcm_hw_params_set_access (alsa_dev, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
+	{	fprintf (stderr, "cannot set access type (%s)\n", snd_strerror (err)) ;
+		goto catch_error ;
+		} ;
+
+	if ((err = snd_pcm_hw_params_set_format (alsa_dev, hw_params, SND_PCM_FORMAT_FLOAT)) < 0)
+	{	fprintf (stderr, "cannot set sample format (%s)\n", snd_strerror (err)) ;
+		goto catch_error ;
+		} ;
+
+	if ((err = snd_pcm_hw_params_set_rate_near (alsa_dev, hw_params, &samplerate, 0)) < 0)
+	{	fprintf (stderr, "cannot set sample rate (%s)\n", snd_strerror (err)) ;
+		goto catch_error ;
+		} ;
+
+	if ((err = snd_pcm_hw_params_set_channels (alsa_dev, hw_params, channels)) < 0)
+	{	fprintf (stderr, "cannot set channel count (%s)\n", snd_strerror (err)) ;
+		goto catch_error ;
+		} ;
+
+	if ((err = snd_pcm_hw_params_set_buffer_size_near (alsa_dev, hw_params, &alsa_buffer_frames)) < 0)
+	{	fprintf (stderr, "cannot set buffer size (%s)\n", snd_strerror (err)) ;
+		goto catch_error ;
+		} ;
+
+	if ((err = snd_pcm_hw_params_set_period_size_near (alsa_dev, hw_params, &alsa_period_size, 0)) < 0)
+	{	fprintf (stderr, "cannot set period size (%s)\n", snd_strerror (err)) ;
+		goto catch_error ;
+		} ;
+
+	if ((err = snd_pcm_hw_params (alsa_dev, hw_params)) < 0)
+	{	fprintf (stderr, "cannot set parameters (%s)\n", snd_strerror (err)) ;
+		goto catch_error ;
+		} ;
+
+	/* extra check: if we have only one period, this code won't work */
+	snd_pcm_hw_params_get_period_size (hw_params, &alsa_period_size, 0) ;
+	snd_pcm_hw_params_get_buffer_size (hw_params, &buffer_size) ;
+	if (alsa_period_size == buffer_size)
+	{	fprintf (stderr, "Can't use period equal to buffer size (%lu == %lu)", alsa_period_size, buffer_size) ;
+		goto catch_error ;
+		} ;
+
+	snd_pcm_hw_params_free (hw_params) ;
+
+	if ((err = snd_pcm_sw_params_malloc (&sw_params)) != 0)
+	{	fprintf (stderr, "%s: snd_pcm_sw_params_malloc: %s", __func__, snd_strerror (err)) ;
+		goto catch_error ;
+		} ;
+
+	if ((err = snd_pcm_sw_params_current (alsa_dev, sw_params)) != 0)
+	{	fprintf (stderr, "%s: snd_pcm_sw_params_current: %s", __func__, snd_strerror (err)) ;
+		goto catch_error ;
+		} ;
+
+	/* note: set start threshold to delay start until the ring buffer is full */
+	snd_pcm_sw_params_current (alsa_dev, sw_params) ;
+
+	if ((err = snd_pcm_sw_params_set_start_threshold (alsa_dev, sw_params, buffer_size)) < 0)
+	{	fprintf (stderr, "cannot set start threshold (%s)\n", snd_strerror (err)) ;
+		goto catch_error ;
+		} ;
+
+	if ((err = snd_pcm_sw_params (alsa_dev, sw_params)) != 0)
+	{	fprintf (stderr, "%s: snd_pcm_sw_params: %s", __func__, snd_strerror (err)) ;
+		goto catch_error ;
+		} ;
+
+	snd_pcm_sw_params_free (sw_params) ;
+
+	snd_pcm_reset (alsa_dev) ;
+
+catch_error :
+
+	if (err < 0 && alsa_dev != NULL)
+	{	snd_pcm_close (alsa_dev) ;
+		return NULL ;
+		} ;
+
+	return alsa_dev ;
+} /* alsa_open */
+
+static int
+alsa_write_float (snd_pcm_t *alsa_dev, float *data, int frames, int channels)
+{	static	int epipe_count = 0 ;
+
+	int total = 0 ;
+	int retval ;
+
+	if (epipe_count > 0)
+		epipe_count -- ;
+
+	while (total < frames)
+	{	retval = snd_pcm_writei (alsa_dev, data + total * channels, frames - total) ;
+
+		if (retval >= 0)
+		{	total += retval ;
+			if (total == frames)
+				return total ;
+
+			continue ;
+			} ;
+
+		switch (retval)
+		{	case -EAGAIN :
+					puts ("alsa_write_float: EAGAIN") ;
+					continue ;
+					break ;
+
+			case -EPIPE :
+					if (epipe_count > 0)
+					{	printf ("alsa_write_float: EPIPE %d\n", epipe_count) ;
+						if (epipe_count > 140)
+							return retval ;
+						} ;
+					epipe_count += 100 ;
+
+#if 0
+					if (0)
+					{	snd_pcm_status_t *status ;
+
+						snd_pcm_status_alloca (&status) ;
+						if ((retval = snd_pcm_status (alsa_dev, status)) < 0)
+							fprintf (stderr, "alsa_out: xrun. can't determine length\n") ;
+						else if (snd_pcm_status_get_state (status) == SND_PCM_STATE_XRUN)
+						{	struct timeval now, diff, tstamp ;
+
+							gettimeofday (&now, 0) ;
+							snd_pcm_status_get_trigger_tstamp (status, &tstamp) ;
+							timersub (&now, &tstamp, &diff) ;
+
+							fprintf (stderr, "alsa_write_float xrun: of at least %.3f msecs. resetting stream\n",
+									diff.tv_sec * 1000 + diff.tv_usec / 1000.0) ;
+							}
+						else
+							fprintf (stderr, "alsa_write_float: xrun. can't determine length\n") ;
+						} ;
+#endif
+
+					snd_pcm_prepare (alsa_dev) ;
+					break ;
+
+			case -EBADFD :
+					fprintf (stderr, "alsa_write_float: Bad PCM state.n") ;
+					return 0 ;
+					break ;
+
+#if defined ESTRPIPE && ESTRPIPE != EPIPE
+			case -ESTRPIPE :
+					fprintf (stderr, "alsa_write_float: Suspend event.n") ;
+					return 0 ;
+					break ;
+#endif
+
+			case -EIO :
+					puts ("alsa_write_float: EIO") ;
+					return 0 ;
+
+			default :
+					fprintf (stderr, "alsa_write_float: retval = %d\n", retval) ;
+					return 0 ;
+					break ;
+			} ; /* switch */
+		} ; /* while */
+
+	return total ;
+} /* alsa_write_float */
+
+#endif /* HAVE_ALSA_ASOUNDLIB_H */
+
+/*------------------------------------------------------------------------------
+**	Linux/OSS functions for playing a sound.
+*/
+
+#if !defined (__ANDROID__) && (defined (__linux__) || defined (__FreeBSD_kernel__) || defined (__FreeBSD__) || defined (__riscos__))
+
+static	int	opensoundsys_open_device (int channels, int srate) ;
+
+static int
+opensoundsys_play (int argc, char *argv [])
+{	static short buffer [BUFFER_LEN] ;
+	SNDFILE *sndfile ;
+	SF_INFO sfinfo ;
+	int		k, audio_device, readcount, writecount, subformat ;
+
+	for (k = 1 ; k < argc ; k++)
+	{	memset (&sfinfo, 0, sizeof (sfinfo)) ;
+
+		printf ("Playing %s\n", argv [k]) ;
+		if (! (sndfile = sf_open (argv [k], SFM_READ, &sfinfo)))
+		{	puts (sf_strerror (NULL)) ;
+			continue ;
+			} ;
+
+		if (sfinfo.channels < 1 || sfinfo.channels > 2)
+		{	printf ("Error : channels = %d.\n", sfinfo.channels) ;
+			continue ;
+			} ;
+
+		audio_device = opensoundsys_open_device (sfinfo.channels, sfinfo.samplerate) ;
+
+		subformat = sfinfo.format & SF_FORMAT_SUBMASK ;
+
+		if (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE)
+		{	static float float_buffer [BUFFER_LEN] ;
+			double	scale ;
+			int 	m ;
+
+			sf_command (sndfile, SFC_CALC_SIGNAL_MAX, &scale, sizeof (scale)) ;
+			if (scale < 1e-10)
+				scale = 1.0 ;
+			else
+				scale = 32700.0 / scale ;
+
+			while ((readcount = sf_read_float (sndfile, float_buffer, BUFFER_LEN)))
+			{	for (m = 0 ; m < readcount ; m++)
+					buffer [m] = scale * float_buffer [m] ;
+				writecount = write (audio_device, buffer, readcount * sizeof (short)) ;
+				} ;
+			}
+		else
+		{	while ((readcount = sf_read_short (sndfile, buffer, BUFFER_LEN)))
+				writecount = write (audio_device, buffer, readcount * sizeof (short)) ;
+			} ;
+
+		if (ioctl (audio_device, SNDCTL_DSP_POST, 0) == -1)
+			perror ("ioctl (SNDCTL_DSP_POST) ") ;
+
+		if (ioctl (audio_device, SNDCTL_DSP_SYNC, 0) == -1)
+			perror ("ioctl (SNDCTL_DSP_SYNC) ") ;
+
+		close (audio_device) ;
+
+		sf_close (sndfile) ;
+		} ;
+
+	return writecount ;
+} /* opensoundsys_play */
+
+static int
+opensoundsys_open_device (int channels, int srate)
+{	int fd, stereo, fmt ;
+
+	if ((fd = open ("/dev/dsp", O_WRONLY, 0)) == -1 &&
+		(fd = open ("/dev/sound/dsp", O_WRONLY, 0)) == -1)
+	{	perror ("opensoundsys_open_device : open ") ;
+		exit (1) ;
+		} ;
+
+	stereo = 0 ;
+	if (ioctl (fd, SNDCTL_DSP_STEREO, &stereo) == -1)
+	{ 	/* Fatal error */
+		perror ("opensoundsys_open_device : stereo ") ;
+		exit (1) ;
+		} ;
+
+	if (ioctl (fd, SNDCTL_DSP_RESET, 0))
+	{	perror ("opensoundsys_open_device : reset ") ;
+		exit (1) ;
+		} ;
+
+	fmt = CPU_IS_BIG_ENDIAN ? AFMT_S16_BE : AFMT_S16_LE ;
+	if (ioctl (fd, SNDCTL_DSP_SETFMT, &fmt) != 0)
+	{	perror ("opensoundsys_open_device : set format ") ;
+		exit (1) ;
+		} ;
+
+	if (ioctl (fd, SNDCTL_DSP_CHANNELS, &channels) != 0)
+	{	perror ("opensoundsys_open_device : channels ") ;
+		exit (1) ;
+		} ;
+
+	if (ioctl (fd, SNDCTL_DSP_SPEED, &srate) != 0)
+	{	perror ("opensoundsys_open_device : sample rate ") ;
+		exit (1) ;
+		} ;
+
+	if (ioctl (fd, SNDCTL_DSP_SYNC, 0) != 0)
+	{	perror ("opensoundsys_open_device : sync ") ;
+		exit (1) ;
+		} ;
+
+	return 	fd ;
+} /* opensoundsys_open_device */
+
+#endif /* __linux__ */
+
+/*------------------------------------------------------------------------------
+**	Mac OS X functions for playing a sound.
+*/
+
+/* MacOSX 10.8 use a new Audio API. Someone needs to write some code for it. */
+
+/*------------------------------------------------------------------------------
+**	Win32 functions for playing a sound.
+**
+**	This API sucks. Its needlessly complicated and is *WAY* too loose with
+**	passing pointers around in integers and using char* pointers to
+**  point to data instead of short*. It plain sucks!
+*/
+
+#if (OS_IS_WIN32 == 1)
+
+#define	WIN32_BUFFER_LEN	(1 << 15)
+
+typedef struct
+{	HWAVEOUT	hwave ;
+	WAVEHDR		whdr [2] ;
+
+	CRITICAL_SECTION	mutex ;		/* to control access to BuffersInUSe */
+	HANDLE		Event ;			/* signal that a buffer is free */
+
+	short		buffer [WIN32_BUFFER_LEN / sizeof (short)] ;
+	int			current, bufferlen ;
+	int			BuffersInUse ;
+
+	SNDFILE 	*sndfile ;
+	SF_INFO 	sfinfo ;
+
+	sf_count_t	remaining ;
+} Win32_Audio_Data ;
+
+
+static void
+win32_play_data (Win32_Audio_Data *audio_data)
+{	int thisread, readcount ;
+
+	/* fill a buffer if there is more data and we can read it sucessfully */
+	readcount = (audio_data->remaining > audio_data->bufferlen) ? audio_data->bufferlen : (int) audio_data->remaining ;
+
+	short *lpData = (short *) (void *) audio_data->whdr [audio_data->current].lpData ;
+	thisread = (int) sf_read_short (audio_data->sndfile, lpData, readcount) ;
+
+	audio_data->remaining -= thisread ;
+
+	if (thisread > 0)
+	{	/* Fix buffer length if this is only a partial block. */
+		if (thisread < audio_data->bufferlen)
+			audio_data->whdr [audio_data->current].dwBufferLength = thisread * sizeof (short) ;
+
+		/* Queue the WAVEHDR */
+		waveOutWrite (audio_data->hwave, (LPWAVEHDR) &(audio_data->whdr [audio_data->current]), sizeof (WAVEHDR)) ;
+
+		/* count another buffer in use */
+		EnterCriticalSection (&audio_data->mutex) ;
+		audio_data->BuffersInUse ++ ;
+		LeaveCriticalSection (&audio_data->mutex) ;
+
+		/* use the other buffer next time */
+		audio_data->current = (audio_data->current + 1) % 2 ;
+		} ;
+
+	return ;
+} /* win32_play_data */
+
+static void CALLBACK
+win32_audio_out_callback (HWAVEOUT hwave, UINT msg, DWORD_PTR data, DWORD param1, DWORD param2)
+{	Win32_Audio_Data	*audio_data ;
+
+	/* Prevent compiler warnings. */
+	(void) hwave ;
+	(void) param1 ;
+	(void) param2 ;
+
+	if (data == 0)
+		return ;
+
+	/*
+	** I consider this technique of passing a pointer via an integer as
+	** fundamentally broken but thats the way microsoft has defined the
+	** interface.
+	*/
+	audio_data = (Win32_Audio_Data*) data ;
+
+	/* let main loop know a buffer is free */
+	if (msg == MM_WOM_DONE)
+	{	EnterCriticalSection (&audio_data->mutex) ;
+		audio_data->BuffersInUse -- ;
+		LeaveCriticalSection (&audio_data->mutex) ;
+		SetEvent (audio_data->Event) ;
+		} ;
+
+	return ;
+} /* win32_audio_out_callback */
+
+static void
+win32_play (int argc, char *argv [])
+{	Win32_Audio_Data	audio_data ;
+
+	WAVEFORMATEX wf ;
+	int	k, error ;
+
+	audio_data.sndfile = NULL ;
+	audio_data.hwave = 0 ;
+
+	for (k = 1 ; k < argc ; k++)
+	{	printf ("Playing %s\n", argv [k]) ;
+
+		if (! (audio_data.sndfile = sf_open (argv [k], SFM_READ, &(audio_data.sfinfo))))
+		{	puts (sf_strerror (NULL)) ;
+			continue ;
+			} ;
+
+		audio_data.remaining = audio_data.sfinfo.frames * audio_data.sfinfo.channels ;
+		audio_data.current = 0 ;
+
+		InitializeCriticalSection (&audio_data.mutex) ;
+		audio_data.Event = CreateEvent (0, FALSE, FALSE, 0) ;
+
+		wf.nChannels = audio_data.sfinfo.channels ;
+		wf.wFormatTag = WAVE_FORMAT_PCM ;
+		wf.cbSize = 0 ;
+		wf.wBitsPerSample = 16 ;
+
+		wf.nSamplesPerSec = audio_data.sfinfo.samplerate ;
+
+		wf.nBlockAlign = audio_data.sfinfo.channels * sizeof (short) ;
+
+		wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec ;
+
+		error = waveOutOpen (&(audio_data.hwave), WAVE_MAPPER, &wf, (DWORD_PTR) win32_audio_out_callback,
+							(DWORD_PTR) &audio_data, CALLBACK_FUNCTION) ;
+		if (error)
+		{	puts ("waveOutOpen failed.") ;
+			audio_data.hwave = 0 ;
+			continue ;
+			} ;
+
+		audio_data.whdr [0].lpData = (char*) audio_data.buffer ;
+		audio_data.whdr [1].lpData = ((char*) audio_data.buffer) + sizeof (audio_data.buffer) / 2 ;
+
+		audio_data.whdr [0].dwBufferLength = sizeof (audio_data.buffer) / 2 ;
+		audio_data.whdr [1].dwBufferLength = sizeof (audio_data.buffer) / 2 ;
+
+		audio_data.whdr [0].dwFlags = 0 ;
+		audio_data.whdr [1].dwFlags = 0 ;
+
+		/* length of each audio buffer in samples */
+		audio_data.bufferlen = sizeof (audio_data.buffer) / 2 / sizeof (short) ;
+
+		/* Prepare the WAVEHDRs */
+		if ((error = waveOutPrepareHeader (audio_data.hwave, &(audio_data.whdr [0]), sizeof (WAVEHDR))))
+		{	printf ("waveOutPrepareHeader [0] failed : %08X\n", error) ;
+			waveOutClose (audio_data.hwave) ;
+			continue ;
+			} ;
+
+		if ((error = waveOutPrepareHeader (audio_data.hwave, &(audio_data.whdr [1]), sizeof (WAVEHDR))))
+		{	printf ("waveOutPrepareHeader [1] failed : %08X\n", error) ;
+			waveOutUnprepareHeader (audio_data.hwave, &(audio_data.whdr [0]), sizeof (WAVEHDR)) ;
+			waveOutClose (audio_data.hwave) ;
+			continue ;
+			} ;
+
+		/* Fill up both buffers with audio data */
+		audio_data.BuffersInUse = 0 ;
+		win32_play_data (&audio_data) ;
+		win32_play_data (&audio_data) ;
+
+		/* loop until both buffers are released */
+		while (audio_data.BuffersInUse > 0)
+		{
+			/* wait for buffer to be released */
+			WaitForSingleObject (audio_data.Event, INFINITE) ;
+
+			/* refill the buffer if there is more data to play */
+			win32_play_data (&audio_data) ;
+			} ;
+
+		waveOutUnprepareHeader (audio_data.hwave, &(audio_data.whdr [0]), sizeof (WAVEHDR)) ;
+		waveOutUnprepareHeader (audio_data.hwave, &(audio_data.whdr [1]), sizeof (WAVEHDR)) ;
+
+		waveOutClose (audio_data.hwave) ;
+		audio_data.hwave = 0 ;
+
+		DeleteCriticalSection (&audio_data.mutex) ;
+
+		sf_close (audio_data.sndfile) ;
+		} ;
+
+} /* win32_play */
+
+#endif /* Win32 */
+
+/*------------------------------------------------------------------------------
+**	OpenBSD's sndio.
+*/
+
+#if HAVE_SNDIO_H
+
+static void
+sndio_play (int argc, char *argv [])
+{	struct sio_hdl	*hdl ;
+	struct sio_par	par ;
+	short buffer [BUFFER_LEN] ;
+	SNDFILE	*sndfile ;
+	SF_INFO	sfinfo ;
+	int		k, readcount ;
+
+	for (k = 1 ; k < argc ; k++)
+	{	printf ("Playing %s\n", argv [k]) ;
+		if (! (sndfile = sf_open (argv [k], SFM_READ, &sfinfo)))
+		{	puts (sf_strerror (NULL)) ;
+			continue ;
+			} ;
+
+		if (sfinfo.channels < 1 || sfinfo.channels > 2)
+		{	printf ("Error : channels = %d.\n", sfinfo.channels) ;
+			continue ;
+			} ;
+
+		if ((hdl = sio_open (NULL, SIO_PLAY, 0)) == NULL)
+		{	fprintf (stderr, "open sndio device failed") ;
+			return ;
+			} ;
+
+		sio_initpar (&par) ;
+		par.rate = sfinfo.samplerate ;
+		par.pchan = sfinfo.channels ;
+		par.bits = 16 ;
+		par.sig = 1 ;
+		par.le = SIO_LE_NATIVE ;
+
+		if (! sio_setpar (hdl, &par) || ! sio_getpar (hdl, &par))
+		{	fprintf (stderr, "set sndio params failed") ;
+			return ;
+			} ;
+
+		if (! sio_start (hdl))
+		{	fprintf (stderr, "sndio start failed") ;
+			return ;
+			} ;
+
+		while ((readcount = sf_read_short (sndfile, buffer, BUFFER_LEN)))
+			sio_write (hdl, buffer, readcount * sizeof (short)) ;
+
+		sio_close (hdl) ;
+		} ;
+
+	return ;
+} /* sndio_play */
+
+#endif /* sndio */
+
+/*------------------------------------------------------------------------------
+**	Solaris.
+*/
+
+#if (defined (sun) && defined (unix)) || defined(__NetBSD__)
+
+static void
+solaris_play (int argc, char *argv [])
+{	static short 	buffer [BUFFER_LEN] ;
+	audio_info_t	audio_info ;
+	SNDFILE			*sndfile ;
+	SF_INFO			sfinfo ;
+	unsigned long	delay_time ;
+	long			k, start_count, output_count, write_count, read_count ;
+	int				audio_fd, error, done ;
+
+	for (k = 1 ; k < argc ; k++)
+	{	printf ("Playing %s\n", argv [k]) ;
+		if (! (sndfile = sf_open (argv [k], SFM_READ, &sfinfo)))
+		{	puts (sf_strerror (NULL)) ;
+			continue ;
+			} ;
+
+		if (sfinfo.channels < 1 || sfinfo.channels > 2)
+		{	printf ("Error : channels = %d.\n", sfinfo.channels) ;
+			continue ;
+			} ;
+
+		/* open the audio device - write only, non-blocking */
+		if ((audio_fd = open ("/dev/audio", O_WRONLY | O_NONBLOCK)) < 0)
+		{	perror ("open (/dev/audio) failed") ;
+			return ;
+			} ;
+
+		/*	Retrive standard values. */
+		AUDIO_INITINFO (&audio_info) ;
+
+		audio_info.play.sample_rate = sfinfo.samplerate ;
+		audio_info.play.channels = sfinfo.channels ;
+		audio_info.play.precision = 16 ;
+		audio_info.play.encoding = AUDIO_ENCODING_LINEAR ;
+
+		if ((error = ioctl (audio_fd, AUDIO_SETINFO, &audio_info)))
+		{	perror ("ioctl (AUDIO_SETINFO) failed") ;
+			return ;
+			} ;
+
+		/* Delay time equal to 1/4 of a buffer in microseconds. */
+		delay_time = (BUFFER_LEN * 1000000) / (audio_info.play.sample_rate * 4) ;
+
+		done = 0 ;
+		while (! done)
+		{	read_count = sf_read_short (sndfile, buffer, BUFFER_LEN) ;
+			if (read_count < BUFFER_LEN)
+			{	memset (&(buffer [read_count]), 0, (BUFFER_LEN - read_count) * sizeof (short)) ;
+				/* Tell the main application to terminate. */
+				done = SF_TRUE ;
+				} ;
+
+			start_count = 0 ;
+			output_count = BUFFER_LEN * sizeof (short) ;
+
+			while (output_count > 0)
+			{	/* write as much data as possible */
+				write_count = write (audio_fd, &(buffer [start_count]), output_count) ;
+				if (write_count > 0)
+				{	output_count -= write_count ;
+					start_count += write_count ;
+					}
+				else
+				{	/*	Give the audio output time to catch up. */
+					usleep (delay_time) ;
+					} ;
+				} ; /* while (outpur_count > 0) */
+			} ; /* while (! done) */
+
+		close (audio_fd) ;
+		} ;
+
+	return ;
+} /* solaris_play */
+
+#endif /* Solaris or NetBSD */
+
+/*==============================================================================
+**	Main function.
+*/
+
+int
+main (int argc, char *argv [])
+{
+	if (argc < 2)
+	{
+		printf ("\nUsage : %s <input sound file>\n\n", program_name (argv [0])) ;
+		printf ("Using %s.\n\n", sf_version_string ()) ;
+#if (OS_IS_WIN32 == 1)
+		printf ("This is a Unix style command line application which\n"
+				"should be run in a MSDOS box or Command Shell window.\n\n") ;
+		printf ("Sleeping for 5 seconds before exiting.\n\n") ;
+
+		Sleep (5 * 1000) ;
+#endif
+		return 1 ;
+		} ;
+
+#if defined (__ANDROID__)
+	puts ("*** Playing sound not yet supported on Android.") ;
+	puts ("*** Please feel free to submit a patch.") ;
+	return 1 ;
+#elif defined (__linux__)
+	#if HAVE_ALSA_ASOUNDLIB_H
+		if (access ("/proc/asound/cards", R_OK) == 0)
+			alsa_play (argc, argv) ;
+		else
+	#endif
+		opensoundsys_play (argc, argv) ;
+#elif defined (__FreeBSD_kernel__) || defined (__FreeBSD__) || defined (__riscos__)
+	opensoundsys_play (argc, argv) ;
+#elif HAVE_SNDIO_H
+	sndio_play (argc, argv) ;
+#elif (defined (sun) && defined (unix)) || defined(__NetBSD__)
+	solaris_play (argc, argv) ;
+#elif (OS_IS_WIN32 == 1)
+	win32_play (argc, argv) ;
+#else
+	puts ("*** Playing sound not supported on this platform.") ;
+	puts ("*** Please feel free to submit a patch.") ;
+	return 1 ;
+#endif
+
+	return 0 ;
+} /* main */
+
diff --git a/programs/sndfile-salvage.c b/programs/sndfile-salvage.c
new file mode 100644
index 0000000..1a76dc3
--- /dev/null
+++ b/programs/sndfile-salvage.c
@@ -0,0 +1,302 @@
+/*
+** Copyright (C) 2010-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in
+**       the documentation and/or other materials provided with the
+**       distribution.
+**     * Neither the author nor the names of any contributors may be used
+**       to endorse or promote products derived from this software without
+**       specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<stdlib.h>
+#include	<string.h>
+#include	<inttypes.h>
+#include	<ctype.h>
+#include	<math.h>
+#include	<errno.h>
+#if HAVE_UNISTD_H
+#include	<unistd.h>
+#else
+#include	"sf_unistd.h"
+#endif
+#include	<fcntl.h>
+#include	<sys/stat.h>
+#include	<sys/types.h>
+
+#include	<sndfile.h>
+
+#include	"common.h"
+
+#define	BUFFER_LEN		(1 << 16)
+
+#define	NOT(x)			(! (x))
+
+#ifndef _WIN32
+typedef off_t sf_off_t ;
+#else
+typedef long long sf_off_t ;
+#endif
+
+
+static void usage_exit (const char *progname) ;
+static void salvage_file (const char * broken_wav, const char * fixed_w64) ;
+
+int
+main (int argc, char *argv [])
+{
+	if (argc != 3)
+		usage_exit (program_name (argv [0])) ;
+
+	salvage_file (argv [1], argv [2]) ;
+
+	return 0 ;
+} /* main */
+
+/*==============================================================================
+*/
+
+static void lseek_or_die (int fd, sf_off_t offset, int whence) ;
+static sf_off_t get_file_length (int fd, const char * name) ;
+static sf_count_t find_data_offset (int fd, int format) ;
+static void copy_data (int fd, SNDFILE * sndfile, int readsize) ;
+
+
+static void
+usage_exit (const char *progname)
+{	printf ("Usage :\n\n  %s <broken wav file> <fixed w64 file>\n\n", progname) ;
+	puts ("Salvages the audio data from WAV files which are more than 4G in length.\n") ;
+	printf ("Using %s.\n\n", sf_version_string ()) ;
+	exit (1) ;
+} /* usage_exit */
+
+static void
+salvage_file (const char * broken_wav, const char * fixed_w64)
+{	SNDFILE * sndfile ;
+	SF_INFO sfinfo ;
+	sf_count_t broken_len, data_offset ;
+	int fd, read_size ;
+
+	if (strcmp (broken_wav, fixed_w64) == 0)
+	{	printf ("Error : Input and output files must be different.\n\n") ;
+		exit (1) ;
+		} ;
+
+	if ((fd = open (broken_wav, O_RDONLY)) < 0)
+	{	printf ("Error : Not able to open file '%s' : %s\n", broken_wav, strerror (errno)) ;
+		exit (1) ;
+		} ;
+
+	broken_len = get_file_length (fd, broken_wav) ;
+	if (broken_len <= 0xffffffff)
+		printf ("File is not greater than 4Gig but salvaging anyway.\n") ;
+
+	/* Grab the format info from the broken file. */
+	memset (&sfinfo, 0, sizeof (sfinfo)) ;
+	if ((sndfile = sf_open (broken_wav, SFM_READ, &sfinfo)) == NULL)
+	{	printf ("sf_open ('%s') failed : %s\n", broken_wav, sf_strerror (NULL)) ;
+		exit (1) ;
+		} ;
+	sf_close (sndfile) ;
+
+	data_offset = find_data_offset (fd, sfinfo.format & SF_FORMAT_TYPEMASK) ;
+
+	printf ("Offset to audio data : %" PRId64 "\n", data_offset) ;
+
+	switch (sfinfo.format & SF_FORMAT_TYPEMASK)
+	{	case SF_FORMAT_WAV :
+		case SF_FORMAT_WAVEX :
+			sfinfo.format = SF_FORMAT_W64 | (sfinfo.format & SF_FORMAT_SUBMASK) ;
+			break ;
+
+		default :
+			printf ("Don't currently support this file type.\n") ;
+			exit (1) ;
+		} ;
+
+	switch (sfinfo.format & SF_FORMAT_SUBMASK)
+	{	case SF_FORMAT_PCM_U8 :
+		case SF_FORMAT_PCM_S8 :
+				read_size = 1 ;
+				break ;
+
+		case SF_FORMAT_PCM_16 :
+				read_size = 2 ;
+				break ;
+
+		case SF_FORMAT_PCM_24 :
+				read_size = 3 ;
+				break ;
+
+		case SF_FORMAT_PCM_32 :
+		case SF_FORMAT_FLOAT :
+				read_size = 4 ;
+				break ;
+
+		case SF_FORMAT_DOUBLE :
+				read_size = 8 ;
+				break ;
+
+		default :
+			printf ("Sorry, don't currently support this file encoding type.\n") ;
+			exit (1) ;
+		} ;
+
+	read_size *= sfinfo.channels ;
+
+	if ((sndfile = sf_open (fixed_w64, SFM_WRITE, &sfinfo)) == NULL)
+	{	printf ("sf_open ('%s') failed : %s\n", fixed_w64, sf_strerror (NULL)) ;
+		exit (1) ;
+		} ;
+
+	lseek_or_die (fd, data_offset, SEEK_SET) ;
+
+	copy_data (fd, sndfile, read_size) ;
+
+	sf_close (sndfile) ;
+
+	puts ("Done!") ;
+} /* salvage_file */
+
+/*------------------------------------------------------------------------------
+*/
+
+static void
+lseek_or_die (int fd, sf_off_t offset, int whence)
+{
+#ifndef _WIN32
+	if (lseek (fd, offset, whence) < 0)
+#else
+	if (_lseeki64 (fd, offset, whence) < 0)
+#endif
+	{	printf ("lseek failed : %s\n", strerror (errno)) ;
+		exit (1) ;
+		} ;
+
+	return ;
+} /* lseek_or_die */
+
+
+static sf_off_t
+get_file_length (int fd, const char * name)
+{
+#ifndef _WIN32
+	struct stat sbuf ;
+#else
+	struct _stat64 sbuf ;
+#endif
+
+	if (sizeof (sbuf.st_size) != 8)
+	{	puts ("Error : sizeof (sbuf.st_size) != 8. Was program compiled with\n"
+				"        64 bit file offsets?\n") ;
+		exit (1) ;
+		} ;
+
+#ifndef _WIN32
+	if (fstat (fd, &sbuf) != 0)
+#else
+	if (_fstat64 (fd, &sbuf) != 0)
+#endif
+	{	printf ("Error : fstat ('%s') failed : %s\n", name, strerror (errno)) ;
+		exit (1) ;
+		} ;
+
+	return sbuf.st_size ;
+} /* get_file_length */
+
+static sf_count_t
+find_data_offset (int fd, int format)
+{	char buffer [8192], *cptr ;
+	const char * target = "XXXX" ;
+	sf_count_t offset = -1, extra ;
+	int rlen, slen ;
+
+	switch (format)
+	{	case SF_FORMAT_WAV :
+		case SF_FORMAT_WAVEX :
+			target = "data" ;
+			extra = 8 ;
+			break ;
+
+		case SF_FORMAT_AIFF :
+			target = "SSND" ;
+			extra = 16 ;
+			break ;
+
+		default :
+			puts ("Error : Sorry, don't handle this input file format.\n") ;
+			exit (1) ;
+		} ;
+
+	slen = (int) strlen (target) ;
+
+	lseek_or_die (fd, 0, SEEK_SET) ;
+
+	printf ("Searching for '%s' maker.\n", target) ;
+
+	if ((rlen = read (fd, buffer, sizeof (buffer))) < 0)
+	{	printf ("Error : failed read : %s\n", strerror (errno)) ;
+		exit (1) ;
+		} ;
+
+	cptr = memchr (buffer, target [0], rlen - slen) ;
+	if (cptr && memcmp (cptr, target, slen) == 0)
+		offset = cptr - buffer ;
+	else
+	{	printf ("Error : Could not find data offset.\n") ;
+		exit (1) ;
+		} ;
+
+	return offset + extra ;
+} /* find_data_offset */
+
+static void
+copy_data (int fd, SNDFILE * sndfile, int readsize)
+{	static char * buffer ;
+	sf_count_t readlen, count ;
+	int bufferlen, done = 0 ;
+
+	bufferlen = readsize * 1024 ;
+	buffer = malloc (bufferlen) ;
+
+	while (NOT (done) && (readlen = read (fd, buffer, bufferlen)) >= 0)
+	{	if (readlen < bufferlen)
+		{	readlen -= readlen % readsize ;
+			done = 1 ;
+			} ;
+
+		if ((count = sf_write_raw (sndfile, buffer, readlen)) != readlen)
+		{	printf ("Error : sf_write_raw returned %" PRId64 " : %s\n", count, sf_strerror (sndfile)) ;
+			return ;
+			} ;
+		} ;
+
+	free (buffer) ;
+
+	return ;
+} /* copy_data */
+
diff --git a/programs/test-sndfile-metadata-set.py b/programs/test-sndfile-metadata-set.py
new file mode 100755
index 0000000..0006936
--- /dev/null
+++ b/programs/test-sndfile-metadata-set.py
@@ -0,0 +1,198 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2008-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#     * Neither the author nor the names of any contributors may be used
+#       to endorse or promote products derived from this software without
+#       specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Simple test script for the sndfile-metadata-set program.
+
+from __future__ import print_function
+
+try:
+    # py2
+    import commands
+except ImportError:
+    # py3
+    import subprocess as commands
+
+import os, sys
+import time, datetime
+
+class Programs:
+    def __init__ (self, needs_exe):
+        if needs_exe:
+            extension = ".exe"
+        else:
+            extension = ""
+        self.meta_set_prog = "./sndfile-metadata-set" + extension
+        self.meta_get_prog = "./sndfile-metadata-get" + extension
+        self.make_sine_prog = "../examples/make_sine" + extension
+
+    def _run_command (self, should_fail, cmd):
+        status, output = commands.getstatusoutput (cmd)
+        if should_fail and not status:
+            print("\n\nError : command '%s' should have failed." % cmd)
+            print(output)
+            print()
+            sys.exit (1)
+        if not should_fail and status:
+            print("\n\nError : command '%s' should not have failed." % cmd)
+            print(output)
+            print()
+            sys.exit (1)
+        return output
+
+    def meta_set (self, should_fail, args):
+        return self._run_command (should_fail, self.meta_set_prog + " " + args)
+
+    def meta_get (self, should_fail, args):
+        return self._run_command (should_fail, self.meta_get_prog + " " + args)
+
+    def make_sine (self):
+        return os.system (self.make_sine_prog)
+
+    def check_executables (self):
+        for name in [ self.meta_set_prog, self.meta_get_prog, self.make_sine_prog ]:
+            if not (os.path.isfile (name)):
+                print("\n\nError : Can't find executable '%s'. Have you run make?" % name)
+                sys.exit (1)
+
+
+def print_test_name (name):
+    print("    %-30s :" % name, end="")
+
+def assert_info (programs, filename, arg, value):
+    output = programs.meta_get (False, "%s %s" % (arg, filename))
+    if output.find (value) < 0:
+        print("\n\nError : not able to find '%s'." % value)
+        print(output)
+        sys.exit (1)
+    return
+
+
+def test_empty_fail (programs):
+    print_test_name ("Empty fail test")
+    output = programs.meta_set (True, "--bext-description Alpha sine.wav")
+    print("ok")
+
+def test_copy (programs):
+    print_test_name ("Copy test")
+    output = programs.meta_set (False, "--bext-description \"First Try\" sine.wav output.wav")
+    assert_info (programs, "output.wav", "--bext-description", "First Try")
+    print("ok")
+
+def test_update (programs, tests):
+    print_test_name ("Update test")
+    for arg, value in tests:
+        output = programs.meta_set (False, "%s \"%s\" output.wav" % (arg, value))
+        assert_info (programs, "output.wav", arg, value)
+    print("ok")
+
+def test_post_mod (programs, tests):
+    print_test_name ("Post mod test")
+    for arg, value in tests:
+        assert_info (programs, "output.wav", arg, value)
+    print("ok")
+
+def test_auto_date (programs):
+    print_test_name ("Auto date test")
+    output = programs.meta_set (False, "--bext-auto-time-date sine.wav date-time.wav")
+    target = datetime.date.today ().__str__ ()
+    assert_info (programs, "date-time.wav", "--bext-orig-date", target)
+    print("ok")
+
+
+#-------------------------------------------------------------------------------
+
+def test_coding_history (programs):
+    print_test_name ("Coding history test")
+    output = programs.meta_set (False, "--bext-coding-hist \"alpha beta\" output.wav")
+    output = programs.meta_get (False, "--bext-coding-hist output.wav")
+    print("ok")
+
+#-------------------------------------------------------------------------------
+
+def test_rewrite (programs):
+    print_test_name ("Rewrite test")
+    output = programs.meta_set (False, "--bext-originator \"Really, really long string\" output.wav")
+    output = programs.meta_set (False, "--bext-originator \"Short\" output.wav")
+    output = programs.meta_get (False, "--bext-originator output.wav")
+    if output.find ("really long") > 0:
+        print("\n\nError : output '%s' should not contain 'really long'." % output)
+        sys.exit (1)
+    print("ok")
+
+#===============================================================================
+
+test_dir = "programs"
+
+print("\nTesting WAV metadata manipulation:")
+
+if os.path.isdir (test_dir):
+    os.chdir (test_dir)
+
+if len (sys.argv) >= 1 and sys.argv [1].endswith ("mingw32"):
+    needs_exe = True
+else:
+    needs_exe = False
+
+programs = Programs (needs_exe)
+
+programs.check_executables ()
+
+programs.make_sine ()
+if not os.path.isfile ("sine.wav"):
+    print("\n\nError : Can't file file 'sine.wav'.")
+    sys.exit (1)
+
+test_empty_fail (programs)
+test_copy (programs)
+
+tests = [
+    ("--bext-description", "Alpha"), ("--bext-originator", "Beta"), ("--bext-orig-ref", "Charlie"),
+    ("--bext-umid", "Delta"), ("--bext-orig-date", "2001-10-01"),  ("--bext-orig-time", "01:02:03"),
+    ("--str-title", "Echo"), ("--str-artist", "Fox trot")
+    ]
+
+test_auto_date (programs)
+test_update (programs, tests)
+test_post_mod (programs, tests)
+
+test_update (programs, [ ("--str-artist", "Fox") ])
+
+# This never worked.
+# test_coding_history ()
+
+test_rewrite (programs)
+
+
+print()
+
+sys.exit (0)
+
diff --git a/sndfile.pc.in b/sndfile.pc.in
new file mode 100644
index 0000000..6b88453
--- /dev/null
+++ b/sndfile.pc.in
@@ -0,0 +1,13 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: sndfile
+Description: A library for reading and writing audio files
+Requires:
+Requires.private: @EXTERNAL_XIPH_REQUIRE@ @EXTERNAL_MPEG_REQUIRE@
+Version: @VERSION@
+Libs: -L${libdir} -lsndfile
+Libs.private: @EXTERNAL_MPEG_LIBS@
+Cflags: -I${includedir}
diff --git a/src/ALAC/ALACAudioTypes.h b/src/ALAC/ALACAudioTypes.h
new file mode 100644
index 0000000..52d3f44
--- /dev/null
+++ b/src/ALAC/ALACAudioTypes.h
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	 http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+	File:		ALACAudioTypes.h
+*/
+
+#ifndef ALACAUDIOTYPES_H
+#define ALACAUDIOTYPES_H
+
+/* Force these Mac OS specific things to zero. */
+#define PRAGMA_STRUCT_ALIGN 0
+#define PRAGMA_STRUCT_PACKPUSH 0
+#define PRAGMA_STRUCT_PACK 0
+#define PRAGMA_ONCE 0
+#define PRAGMA_MARK 0
+
+
+#if PRAGMA_ONCE
+#pragma once
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+#include "sfendian.h"
+
+#if CPU_IS_BIG_ENDIAN == 1
+#define TARGET_RT_BIG_ENDIAN 1
+#else
+#define TARGET_RT_BIG_ENDIAN 0
+#endif
+
+#define kChannelAtomSize 12
+
+enum
+{
+	kALAC_UnimplementedError	= -4,
+	kALAC_FileNotFoundError		= -43,
+	kALAC_ParamError			= -50,
+	kALAC_MemFullError			= -108,
+	fALAC_FrameLengthError		= -666,
+
+	/* Add for libsndfile */
+	kALAC_BadBitWidth			= -0x100000,
+	kALAC_IncompatibleVersion	= -0x100001,
+	kALAC_BadSpecificConfigSize	= -0x100002,
+	kALAC_ZeroChannelCount		= -0x100003,
+	kALAC_NumSamplesTooBig		= -0x100004,
+	kALAC_UnsupportedElement	= -0x100005,
+} ;
+
+enum
+{
+	kALACFormatAppleLossless = MAKE_MARKER ('a', 'l', 'a', 'c'),
+	kALACFormatLinearPCM = MAKE_MARKER ('l', 'p', 'c', 'm')
+} ;
+
+enum
+{
+	kALACMaxChannels			= 8,
+	kALACMaxEscapeHeaderBytes	= 8,
+	kALACMaxSearches			= 16,
+	kALACMaxCoefs				= 16,
+	kALACDefaultFramesPerPacket = 4096
+} ;
+
+typedef uint32_t ALACChannelLayoutTag ;
+
+enum
+{
+	kALACFormatFlagIsFloat					= (1 << 0),	// 0x1
+	kALACFormatFlagIsBigEndian				= (1 << 1),	// 0x2
+	kALACFormatFlagIsSignedInteger			= (1 << 2),	// 0x4
+	kALACFormatFlagIsPacked					= (1 << 3),	// 0x8
+	kALACFormatFlagIsAlignedHigh			= (1 << 4),	// 0x10
+} ;
+
+enum
+{
+#if TARGET_RT_BIG_ENDIAN
+	kALACFormatFlagsNativeEndian	= kALACFormatFlagIsBigEndian
+#else
+	kALACFormatFlagsNativeEndian	= 0
+#endif
+} ;
+
+// this is required to be an IEEE 64bit float
+typedef double alac_float64_t ;
+
+// These are the Channel Layout Tags used in the Channel Layout Info portion of the ALAC magic cookie
+enum
+{	kALACChannelLayoutTag_Mono			= (100 << 16) | 1,	// C
+	kALACChannelLayoutTag_Stereo		= (101 << 16) | 2,	// L R
+	kALACChannelLayoutTag_MPEG_3_0_B	= (113 << 16) | 3,	// C L R
+	kALACChannelLayoutTag_MPEG_4_0_B	= (116 << 16) | 4,	// C L R Cs
+	kALACChannelLayoutTag_MPEG_5_0_D	= (120 << 16) | 5,	// C L R Ls Rs
+	kALACChannelLayoutTag_MPEG_5_1_D	= (124 << 16) | 6,	// C L R Ls Rs LFE
+	kALACChannelLayoutTag_AAC_6_1		= (142 << 16) | 7,	// C L R Ls Rs Cs LFE
+	kALACChannelLayoutTag_MPEG_7_1_B	= (127 << 16) | 8	// C Lc Rc L R Ls Rs LFE	(doc: IS-13818-7 MPEG2-AAC)
+} ;
+
+// ALAC currently only utilizes these channels layouts. There is a one for one correspondance between a
+// given number of channels and one of these layout tags
+static const ALACChannelLayoutTag	ALACChannelLayoutTags [kALACMaxChannels] =
+{
+	kALACChannelLayoutTag_Mono,			// C
+	kALACChannelLayoutTag_Stereo,		// L R
+	kALACChannelLayoutTag_MPEG_3_0_B,	// C L R
+	kALACChannelLayoutTag_MPEG_4_0_B,	// C L R Cs
+	kALACChannelLayoutTag_MPEG_5_0_D,	// C L R Ls Rs
+	kALACChannelLayoutTag_MPEG_5_1_D,	// C L R Ls Rs LFE
+	kALACChannelLayoutTag_AAC_6_1,		// C L R Ls Rs Cs LFE
+	kALACChannelLayoutTag_MPEG_7_1_B	// C Lc Rc L R Ls Rs LFE	(doc: IS-13818-7 MPEG2-AAC)
+} ;
+
+// AudioChannelLayout from CoreAudioTypes.h. We never need the AudioChannelDescription so we remove it
+struct ALACAudioChannelLayout
+{	ALACChannelLayoutTag		mChannelLayoutTag ;
+	uint32_t					mChannelBitmap ;
+	uint32_t					mNumberChannelDescriptions ;
+} ;
+typedef struct ALACAudioChannelLayout ALACAudioChannelLayout ;
+
+struct AudioFormatDescription
+{
+	alac_float64_t mSampleRate ;
+	uint32_t mFormatID ;
+	uint32_t mFormatFlags ;
+	uint32_t mBytesPerPacket ;
+	uint32_t mFramesPerPacket ;
+	uint32_t mBytesPerFrame ;
+	uint32_t mChannelsPerFrame ;
+	uint32_t mBitsPerChannel ;
+	uint32_t mReserved ;
+} ;
+typedef struct AudioFormatDescription AudioFormatDescription ;
+
+/* Lossless Definitions */
+
+enum
+{
+	kALACCodecFormat		= MAKE_MARKER ('a', 'l', 'a', 'c'),
+	kALACVersion			= 0,
+	kALACCompatibleVersion	= kALACVersion,
+	kALACDefaultFrameSize	= 4096
+} ;
+
+// note: this struct is wrapped in an 'alac' atom in the sample description extension area
+// note: in QT movies, it will be further wrapped in a 'wave' atom surrounded by 'frma' and 'term' atoms
+typedef struct ALACSpecificConfig
+{
+	uint32_t				frameLength ;
+	uint8_t					compatibleVersion ;
+	uint8_t					bitDepth ;							// max 32
+	uint8_t					pb ;									// 0 <= pb <= 255
+	uint8_t					mb ;
+	uint8_t					kb ;
+	uint8_t					numChannels ;
+	uint16_t				maxRun ;
+	uint32_t				maxFrameBytes ;
+	uint32_t				avgBitRate ;
+	uint32_t				sampleRate ;
+
+} ALACSpecificConfig ;
+
+
+// The AudioChannelLayout atom type is not exposed yet so define it here
+enum
+{
+	AudioChannelLayoutAID = MAKE_MARKER ('c', 'h', 'a', 'n')
+} ;
+
+#if PRAGMA_STRUCT_ALIGN
+	#pragma options align = reset
+#elif PRAGMA_STRUCT_PACKPUSH
+	#pragma pack (pop)
+#elif PRAGMA_STRUCT_PACK
+	#pragma pack ()
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* ALACAUDIOTYPES_H */
diff --git a/src/ALAC/ALACBitUtilities.c b/src/ALAC/ALACBitUtilities.c
new file mode 100644
index 0000000..d861be4
--- /dev/null
+++ b/src/ALAC/ALACBitUtilities.c
@@ -0,0 +1,262 @@
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	 http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*=============================================================================
+	File:		ALACBitUtilities.c
+
+	$NoKeywords: $
+=============================================================================*/
+
+#include <stdio.h>
+#include "ALACBitUtilities.h"
+
+#define PRAGMA_MARK 0
+
+// BitBufferInit
+//
+void BitBufferInit (BitBuffer * bits, uint8_t * buffer, uint32_t byteSize)
+{
+	bits->cur		= buffer ;
+	bits->end		= bits->cur + byteSize ;
+	bits->bitIndex	= 0 ;
+	bits->byteSize	= byteSize ;
+}
+
+// BitBufferRead
+//
+uint32_t BitBufferRead (BitBuffer * bits, uint8_t numBits)
+{
+	uint32_t		returnBits ;
+
+	//Assert (numBits <= 16) ;
+
+	returnBits = ((uint32_t) bits->cur [0] << 16) | ((uint32_t) bits->cur [1] << 8) | ((uint32_t) bits->cur [2]) ;
+	returnBits = returnBits << bits->bitIndex ;
+	returnBits &= 0x00FFFFFF ;
+
+	bits->bitIndex += numBits ;
+
+	returnBits = returnBits >> (24 - numBits) ;
+
+	bits->cur		+= (bits->bitIndex >> 3) ;
+	bits->bitIndex	&= 7 ;
+
+	//Assert (bits->cur <= bits->end) ;
+
+	return returnBits ;
+}
+
+// BitBufferReadSmall
+//
+// Reads up to 8 bits
+uint8_t BitBufferReadSmall (BitBuffer * bits, uint8_t numBits)
+{
+	uint16_t		returnBits ;
+
+	//Assert (numBits <= 8) ;
+
+	returnBits = (bits->cur [0] << 8) | bits->cur [1] ;
+	returnBits = returnBits << bits->bitIndex ;
+
+	bits->bitIndex += numBits ;
+
+	returnBits = returnBits >> (16 - numBits) ;
+
+	bits->cur		+= (bits->bitIndex >> 3) ;
+	bits->bitIndex	&= 7 ;
+
+	//Assert (bits->cur <= bits->end) ;
+
+	return (uint8_t) returnBits ;
+}
+
+// BitBufferReadOne
+//
+// Reads one byte
+uint8_t BitBufferReadOne (BitBuffer * bits)
+{
+	uint8_t		returnBits ;
+
+	returnBits = (bits->cur [0] >> (7 - bits->bitIndex)) & 1 ;
+
+	bits->bitIndex++ ;
+
+	bits->cur		+= (bits->bitIndex >> 3) ;
+	bits->bitIndex	&= 7 ;
+
+	//Assert (bits->cur <= bits->end) ;
+
+	return returnBits ;
+}
+
+// BitBufferPeek
+//
+uint32_t BitBufferPeek (BitBuffer * bits, uint8_t numBits)
+{
+	return ((((((uint32_t) bits->cur [0] << 16) | ((uint32_t) bits->cur [1] << 8) |
+			((uint32_t) bits->cur [2])) << bits->bitIndex) & 0x00FFFFFF) >> (24 - numBits)) ;
+}
+
+// BitBufferPeekOne
+//
+uint32_t BitBufferPeekOne (BitBuffer * bits)
+{
+	return ((bits->cur [0] >> (7 - bits->bitIndex)) & 1) ;
+}
+
+// BitBufferUnpackBERSize
+//
+uint32_t BitBufferUnpackBERSize (BitBuffer * bits)
+{
+	uint32_t		size ;
+	uint8_t		tmp ;
+
+	for (size = 0, tmp = 0x80u ; tmp &= 0x80u ; size = (size << 7u) | (tmp & 0x7fu))
+		tmp = (uint8_t) BitBufferReadSmall (bits, 8) ;
+
+	return size ;
+}
+
+// BitBufferGetPosition
+//
+uint32_t BitBufferGetPosition (BitBuffer * bits)
+{
+	uint8_t *		begin ;
+
+	begin = bits->end - bits->byteSize ;
+
+	return ((uint32_t) (bits->cur - begin) * 8) + bits->bitIndex ;
+}
+
+// BitBufferByteAlign
+//
+void BitBufferByteAlign (BitBuffer * bits, int32_t addZeros)
+{
+	// align bit buffer to next byte boundary, writing zeros if requested
+	if (bits->bitIndex == 0)
+		return ;
+
+	if (addZeros)
+		BitBufferWrite (bits, 0, 8 - bits->bitIndex) ;
+	else
+		BitBufferAdvance (bits, 8 - bits->bitIndex) ;
+}
+
+// BitBufferAdvance
+//
+void BitBufferAdvance (BitBuffer * bits, uint32_t numBits)
+{
+	if (numBits)
+	{
+		bits->bitIndex += numBits ;
+		bits->cur += (bits->bitIndex >> 3) ;
+		bits->bitIndex &= 7 ;
+	}
+}
+
+// BitBufferRewind
+//
+void BitBufferRewind (BitBuffer * bits, uint32_t numBits)
+{
+	uint32_t	numBytes ;
+
+	if (numBits == 0)
+		return ;
+
+	if (bits->bitIndex >= numBits)
+	{
+		bits->bitIndex -= numBits ;
+		return ;
+	}
+
+	numBits -= bits->bitIndex ;
+	bits->bitIndex = 0 ;
+
+	numBytes	= numBits / 8 ;
+	numBits		= numBits % 8 ;
+
+	bits->cur -= numBytes ;
+
+	if (numBits > 0)
+	{
+		bits->bitIndex = 8 - numBits ;
+		bits->cur-- ;
+	}
+
+	if (bits->cur < (bits->end - bits->byteSize))
+	{
+		//DebugCMsg ("BitBufferRewind: Rewound too far.") ;
+
+		bits->cur		= (bits->end - bits->byteSize) ;
+		bits->bitIndex	= 0 ;
+	}
+}
+
+// BitBufferWrite
+//
+void BitBufferWrite (BitBuffer * bits, uint32_t bitValues, uint32_t numBits)
+{
+	uint32_t				invBitIndex ;
+
+	RequireAction (bits != NULL, return ;) ;
+	RequireActionSilent (numBits > 0, return ;) ;
+
+	invBitIndex = 8 - bits->bitIndex ;
+
+	while (numBits > 0)
+	{
+		uint32_t		tmp ;
+		uint8_t		shift ;
+		uint8_t		mask ;
+		uint32_t		curNum ;
+
+		curNum = MIN (invBitIndex, numBits) ;
+
+		tmp = bitValues >> (numBits - curNum) ;
+
+		shift = (uint8_t) (invBitIndex - curNum) ;
+		mask = 0xffu >> (8 - curNum) ;		// must be done in two steps to avoid compiler sequencing ambiguity
+		mask <<= shift ;
+
+		bits->cur [0] = (bits->cur [0] & ~mask) | (((uint8_t) tmp << shift) & mask) ;
+		numBits -= curNum ;
+
+		// increment to next byte if need be
+		invBitIndex -= curNum ;
+		if (invBitIndex == 0)
+		{
+			invBitIndex = 8 ;
+			bits->cur++ ;
+		}
+	}
+
+	bits->bitIndex = 8 - invBitIndex ;
+}
+
+void	BitBufferReset (BitBuffer * bits)
+//void BitBufferInit (BitBuffer * bits, uint8_t * buffer, uint32_t byteSize)
+{
+	bits->cur = bits->end - bits->byteSize ;
+	bits->bitIndex = 0 ;
+}
+
+#if PRAGMA_MARK
+#pragma mark -
+#endif
diff --git a/src/ALAC/ALACBitUtilities.h b/src/ALAC/ALACBitUtilities.h
new file mode 100644
index 0000000..97080cd
--- /dev/null
+++ b/src/ALAC/ALACBitUtilities.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	 http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*=============================================================================
+	File:		ALACBitUtilities.h
+
+	$NoKeywords: $
+=============================================================================*/
+
+#ifndef __ALACBITUTILITIES_H
+#define __ALACBITUTILITIES_H
+
+#include <stdint.h>
+
+#ifndef MIN
+#define MIN(x, y) 			((x) < (y) ? (x) : (y))
+#endif // MIN
+#ifndef MAX
+#define MAX(x, y) 			((x) > (y) ? (x) : (y))
+#endif // MAX
+
+#define RequireAction(condition, action)		if (! (condition)) { action }
+#define RequireActionSilent(condition, action)	if (! (condition)) { action }
+#define RequireNoErr(condition, action)			if (condition) { action }
+
+enum
+{
+	ALAC_noErr = 0
+} ;
+
+
+typedef enum
+{	ID_SCE = 0,						/* Single Channel Element   */
+	ID_CPE = 1,						/* Channel Pair Element	 */
+	ID_CCE = 2,						/* Coupling Channel Element */
+	ID_LFE = 3,						/* LFE Channel Element	  */
+	ID_DSE = 4,						/* not yet supported		*/
+	ID_PCE = 5,
+	ID_FIL = 6,
+	ID_END = 7
+} ELEMENT_TYPE ;
+
+// types
+typedef struct BitBuffer
+{
+	uint8_t *		cur ;
+	uint8_t *		end ;
+	uint32_t		bitIndex ;
+	uint32_t		byteSize ;
+
+} BitBuffer ;
+
+/*
+	BitBuffer routines
+	- these routines take a fixed size buffer and read/write to it
+	- bounds checking must be done by the client
+*/
+void		BitBufferInit (BitBuffer * bits, uint8_t * buffer, uint32_t byteSize) ;
+uint32_t	BitBufferRead (BitBuffer * bits, uint8_t numBits) ;	// note: cannot read more than 16 bits at a time
+uint8_t		BitBufferReadSmall (BitBuffer * bits, uint8_t numBits) ;
+uint8_t		BitBufferReadOne (BitBuffer * bits) ;
+uint32_t	BitBufferPeek (BitBuffer * bits, uint8_t numBits) ;		// note: cannot read more than 16 bits at a time
+uint32_t	BitBufferPeekOne (BitBuffer * bits) ;
+uint32_t	BitBufferUnpackBERSize (BitBuffer * bits) ;
+uint32_t	BitBufferGetPosition (BitBuffer * bits) ;
+void		BitBufferByteAlign (BitBuffer * bits, int32_t addZeros) ;
+void		BitBufferAdvance (BitBuffer * bits, uint32_t numBits) ;
+void		BitBufferRewind (BitBuffer * bits, uint32_t numBits) ;
+void		BitBufferWrite (BitBuffer * bits, uint32_t value, uint32_t numBits) ;
+void		BitBufferReset (BitBuffer * bits) ;
+
+#endif	/* __BITUTILITIES_H */
diff --git a/src/ALAC/ALACDecoder.h b/src/ALAC/ALACDecoder.h
new file mode 100644
index 0000000..8eb7531
--- /dev/null
+++ b/src/ALAC/ALACDecoder.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+	File:		ALACDecoder.h
+*/
+
+#ifndef _ALACDECODER_H
+#define _ALACDECODER_H
+
+#include <stdint.h>
+
+#include "ALACAudioTypes.h"
+
+struct BitBuffer ;
+
+class ALACDecoder
+{
+	public:
+		ALACDecoder () ;
+		~ALACDecoder () ;
+
+		int32_t	Init (void * inMagicCookie, uint32_t inMagicCookieSize) ;
+		int32_t	Decode (struct BitBuffer * bits, uint8_t * sampleBuffer, uint32_t numSamples, uint32_t numChannels, uint32_t * outNumSamples) ;
+
+	public:
+		// decoding parameters (public for use in the analyzer)
+		ALACSpecificConfig		mConfig ;
+
+	protected:
+		int32_t	FillElement (struct BitBuffer * bits) ;
+		int32_t	DataStreamElement (struct BitBuffer * bits) ;
+
+		uint16_t			mActiveElements ;
+
+		// decoding buffers
+		int32_t *			mMixBufferU ;
+		int32_t *			mMixBufferV ;
+		int32_t *			mPredictor ;
+		uint16_t *			mShiftBuffer ;	// note: this points to mPredictor's memory but different
+											//		 variable for clarity and type difference
+} ;
+
+#endif	/* _ALACDECODER_H */
diff --git a/src/ALAC/ALACEncoder.h b/src/ALAC/ALACEncoder.h
new file mode 100644
index 0000000..eb5b79e
--- /dev/null
+++ b/src/ALAC/ALACEncoder.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+	File:		ALACEncoder.h
+*/
+
+#pragma once
+
+#include <stdint.h>
+
+#include "ALACAudioTypes.h"
+
+
+struct BitBuffer ;
+
+class ALACEncoder
+{
+	public :
+		ALACEncoder () ;
+		virtual ~ALACEncoder () ;
+
+		virtual int32_t	Encode (AudioFormatDescription theInputFormat, AudioFormatDescription theOutputFormat,
+								unsigned char * theReadBuffer, unsigned char * theWriteBuffer, int32_t * ioNumBytes) ;
+		virtual int32_t	Finish () ;
+
+		void			SetFastMode (bool fast) { mFastMode = fast ; } ;
+
+		// this must be called *before* InitializeEncoder ()
+		void			SetFrameSize (uint32_t frameSize) { mFrameSize = frameSize ; } ;
+
+		void			GetConfig (ALACSpecificConfig & config) ;
+		uint32_t		GetMagicCookieSize (uint32_t inNumChannels) ;
+		void			GetMagicCookie (void * config, uint32_t * ioSize) ;
+
+		virtual int32_t	InitializeEncoder (AudioFormatDescription theOutputFormat) ;
+
+	protected :
+		virtual void	GetSourceFormat (const AudioFormatDescription * source, AudioFormatDescription * output) ;
+
+		int32_t			EncodeStereo (struct BitBuffer * bitstream, void * input, uint32_t stride, uint32_t channelIndex, uint32_t numSamples) ;
+		int32_t			EncodeStereoFast (struct BitBuffer * bitstream, void * input, uint32_t stride, uint32_t channelIndex, uint32_t numSamples) ;
+		int32_t			EncodeStereoEscape (struct BitBuffer * bitstream, void * input, uint32_t stride, uint32_t numSamples) ;
+		int32_t			EncodeMono (struct BitBuffer * bitstream, void * input, uint32_t stride, uint32_t channelIndex, uint32_t numSamples) ;
+
+
+		// ALAC encoder parameters
+		int16_t			mBitDepth ;
+		bool			mFastMode ;
+
+		// encoding state
+		int16_t			mLastMixRes [kALACMaxChannels] ;
+
+		// encoding buffers
+		int32_t *		mMixBufferU ;
+		int32_t *		mMixBufferV ;
+		int32_t *		mPredictorU ;
+		int32_t *		mPredictorV ;
+		uint16_t *		mShiftBufferUV ;
+
+		uint8_t *		mWorkBuffer ;
+
+		// per-channel coefficients buffers
+		int16_t			mCoefsU [kALACMaxChannels][kALACMaxSearches][kALACMaxCoefs] ;
+		int16_t			mCoefsV [kALACMaxChannels][kALACMaxSearches][kALACMaxCoefs] ;
+
+		// encoding statistics
+		uint32_t		mTotalBytesGenerated ;
+		uint32_t		mAvgBitRate ;
+		uint32_t		mMaxFrameBytes ;
+		uint32_t		mFrameSize ;
+		uint32_t		mMaxOutputBytes ;
+		uint32_t		mNumChannels ;
+		uint32_t		mOutputSampleRate ;
+} ;
diff --git a/src/ALAC/EndianPortable.h b/src/ALAC/EndianPortable.h
new file mode 100644
index 0000000..aa1f449
--- /dev/null
+++ b/src/ALAC/EndianPortable.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+** Copyright (C) 2013-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+//
+//  EndianPortable.h
+//
+//  Copyright 2011 Apple Inc. All rights reserved.
+//
+
+#ifndef _EndianPortable_h
+#define _EndianPortable_h
+
+#include <sfendian.h>
+
+#define Swap16NtoB(x)	H2BE_16 (x)
+#define Swap16BtoN(x)	BE2H_16 (x)
+
+#define Swap32NtoB(x)	H2BE_32 (x)
+#define Swap32BtoN(x)	BE2H_32 (x)
+
+#endif
diff --git a/src/ALAC/LICENSE b/src/ALAC/LICENSE
new file mode 100644
index 0000000..7d85954
--- /dev/null
+++ b/src/ALAC/LICENSE
@@ -0,0 +1,170 @@
+Apache License
+
+Version 2.0, January 2004
+
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and
+distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the
+copyright owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other
+entities that control, are controlled by, or are under common control
+with that entity. For the purposes of this definition, "control" means
+(i) the power, direct or indirect, to cause the direction or management
+of such entity, whether by contract or otherwise, or (ii) ownership
+of fifty percent (50%) or more of the outstanding shares, or (iii)
+beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising
+permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications,
+including but not limited to software source code, documentation source,
+and configuration files.
+
+"Object" form shall mean any form resulting from mechanical
+transformation or translation of a Source form, including but not
+limited to compiled object code, generated documentation, and
+conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object
+form, made available under the License, as indicated by a copyright
+notice that is included in or attached to the work (an example is
+provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object
+form, that is based on (or derived from) the Work and for which the
+editorial revisions, annotations, elaborations, or other modifications
+represent, as a whole, an original work of authorship. For the purposes
+of this License, Derivative Works shall not include works that remain
+separable from, or merely link (or bind by name) to the interfaces of,
+the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original
+version of the Work and any modifications or additions to that Work or
+Derivative Works thereof, that is intentionally submitted to Licensor
+for inclusion in the Work by the copyright owner or by an individual
+or Legal Entity authorized to submit on behalf of the copyright owner.
+For the purposes of this definition, "submitted" means any form of
+electronic, verbal, or written communication sent to the Licensor
+or its representatives, including but not limited to communication
+on electronic mailing lists, source code control systems, and issue
+tracking systems that are managed by, or on behalf of, the Licensor
+for the purpose of discussing and improving the Work, but excluding
+communication that is conspicuously marked or otherwise designated in
+writing by the copyright owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity
+on behalf of whom a Contribution has been received by Licensor and
+subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions
+of this License, each Contributor hereby grants to You a perpetual,
+worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+copyright license to reproduce, prepare Derivative Works of, publicly
+display, publicly perform, sublicense, and distribute the Work and such
+Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+this License, each Contributor hereby grants to You a perpetual,
+worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except
+as stated in this section) patent license to make, have made, use,
+offer to sell, sell, import, and otherwise transfer the Work, where
+such license applies only to those patent claims licensable by such
+Contributor that are necessarily infringed by their Contribution(s)
+alone or by combination of their Contribution(s) with the Work to which
+such Contribution(s) was submitted. If You institute patent litigation
+against any entity (including a cross-claim or counterclaim in a
+lawsuit) alleging that the Work or a Contribution incorporated within
+the Work constitutes direct or contributory patent infringement, then
+any patent licenses granted to You under this License for that Work
+shall terminate as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+Work or Derivative Works thereof in any medium, with or without
+modifications, and in Source or Object form, provided that You meet the
+following conditions:
+
+You must give any other recipients of the Work or Derivative Works a
+copy of this License; and
+
+You must cause any modified files to carry prominent notices stating
+that You changed the files; and
+
+You must retain, in the Source form of any Derivative Works that You
+distribute, all copyright, patent, trademark, and attribution notices
+from the Source form of the Work, excluding those notices that do not
+pertain to any part of the Derivative Works; and
+
+If the Work includes a "NOTICE" text file as part of its distribution,
+then any Derivative Works that You distribute must include a readable
+copy of the attribution notices contained within such NOTICE file,
+excluding those notices that do not pertain to any part of the
+Derivative Works, in at least one of the following places: within a
+NOTICE text file distributed as part of the Derivative Works; within
+the Source form or documentation, if provided along with the Derivative
+Works; or, within a display generated by the Derivative Works, if and
+wherever such third-party notices normally appear. The contents of the
+NOTICE file are for informational purposes only and do not modify the
+License. You may add Your own attribution notices within Derivative
+Works that You distribute, alongside or as an addendum to the NOTICE
+text from the Work, provided that such additional attribution notices
+cannot be construed as modifying the License. You may add Your own
+copyright statement to Your modifications and may provide additional
+or different license terms and conditions for use, reproduction, or
+distribution of Your modifications, or for any such Derivative Works as
+a whole, provided Your use, reproduction, and distribution of the Work
+otherwise complies with the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+any Contribution intentionally submitted for inclusion in the Work by
+You to the Licensor shall be under the terms and conditions of this
+License, without any additional terms or conditions. Notwithstanding
+the above, nothing herein shall supersede or modify the terms of any
+separate license agreement you may have executed with Licensor regarding
+such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+names, trademarks, service marks, or product names of the Licensor,
+except as required for reasonable and customary use in describing the
+origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or agreed
+to in writing, Licensor provides the Work (and each Contributor
+provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+CONDITIONS OF ANY KIND, either express or implied, including, without
+limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT,
+MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
+solely responsible for determining the appropriateness of using or
+redistributing the Work and assume any risks associated with Your
+exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+whether in tort (including negligence), contract, or otherwise, unless
+required by applicable law (such as deliberate and grossly negligent
+acts) or agreed to in writing, shall any Contributor be liable to You
+for damages, including any direct, indirect, special, incidental, or
+consequential damages of any character arising as a result of this
+License or out of the use or inability to use the Work (including but
+not limited to damages for loss of goodwill, work stoppage, computer
+failure or malfunction, or any and all other commercial damages or
+losses), even if such Contributor has been advised of the possibility of
+such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing the
+Work or Derivative Works thereof, You may choose to offer, and charge a
+fee for, acceptance of support, warranty, indemnity, or other liability
+obligations and/or rights consistent with this License. However, in
+accepting such obligations, You may act only on Your own behalf and
+on Your sole responsibility, not on behalf of any other Contributor,
+and only if You agree to indemnify, defend, and hold each Contributor
+harmless for any liability incurred by, or claims asserted against, such
+Contributor by reason of your accepting any such warranty or additional
+liability.
diff --git a/src/ALAC/ag_dec.c b/src/ALAC/ag_dec.c
new file mode 100644
index 0000000..37671d2
--- /dev/null
+++ b/src/ALAC/ag_dec.c
@@ -0,0 +1,358 @@
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	 http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+	File:		ag_dec.c
+
+	Contains:   Adaptive Golomb decode routines.
+
+	Copyright:	(c) 2001-2011 Apple, Inc.
+*/
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "aglib.h"
+#include "ALACBitUtilities.h"
+#include "ALACAudioTypes.h"
+
+#define CODE_TO_LONG_MAXBITS	32
+#define N_MAX_MEAN_CLAMP		0xffff
+#define N_MEAN_CLAMP_VAL		0xffff
+#define REPORT_VAL				40
+
+#if __GNUC__
+#define ALWAYS_INLINE		__attribute__ ((always_inline))
+#elif defined _MSC_VER
+#define ALWAYS_INLINE		__forceinline
+#else
+#define ALWAYS_INLINE
+#endif
+
+/*	And on the subject of the CodeWarrior x86 compiler and inlining, I reworked a lot of this
+	to help the compiler out.   In many cases this required manual inlining or a macro.  Sorry
+	if it is ugly but the performance gains are well worth it.
+	- WSK 5/19/04
+*/
+
+void set_standard_ag_params (AGParamRecPtr params, uint32_t fullwidth, uint32_t sectorwidth)
+{
+	/* Use
+		fullwidth = sectorwidth = numOfSamples, for analog 1-dimensional type-short data,
+		but use
+		fullwidth = full image width, sectorwidth = sector (patch) width
+		for such as image (2-dim.) data.
+	*/
+	set_ag_params (params, MB0, PB0, KB0, fullwidth, sectorwidth, MAX_RUN_DEFAULT) ;
+}
+
+void set_ag_params (AGParamRecPtr params, uint32_t m, uint32_t p, uint32_t k, uint32_t f, uint32_t s, uint32_t maxrun)
+{
+	params->mb = params->mb0 = m ;
+	params->pb = p ;
+	params->kb = k ;
+	params->wb = (1u << params->kb) - 1 ;
+	params->qb = QB-params->pb ;
+	params->fw = f ;
+	params->sw = s ;
+	params->maxrun = maxrun ;
+}
+
+#if PRAGMA_MARK
+#pragma mark -
+#endif
+
+
+// note: implementing this with some kind of "count leading zeros" assembly is a big performance win
+static inline int32_t lead (int32_t m)
+{
+	long j ;
+	unsigned long c = (1ul << 31) ;
+
+	for (j = 0 ; j < 32 ; j++)
+	{
+		if ((c & m) != 0)
+			break ;
+		c >>= 1 ;
+	}
+	return j ;
+}
+
+#define arithmin(a, b) ((a) < (b) ? (a) : (b))
+
+static inline int32_t ALWAYS_INLINE lg3a (int32_t x)
+{
+	int32_t result ;
+
+	x += 3 ;
+	result = lead (x) ;
+
+	return 31 - result ;
+}
+
+static inline uint32_t ALWAYS_INLINE read32bit (uint8_t * buffer)
+{
+	// embedded CPUs typically can't read unaligned 32-bit words so just read the bytes
+	uint32_t		value ;
+
+	value = ((uint32_t) buffer [0] << 24) | ((uint32_t) buffer [1] << 16) |
+				((uint32_t) buffer [2] << 8) | (uint32_t) buffer [3] ;
+	return value ;
+
+}
+
+#if PRAGMA_MARK
+#pragma mark -
+#endif
+
+#define get_next_fromlong(inlong, suff)		((inlong) >> (32 - (suff)))
+
+
+static inline uint32_t ALWAYS_INLINE
+getstreambits (uint8_t *in, int32_t bitoffset, int32_t numbits)
+{
+	uint32_t	load1, load2 ;
+	uint32_t	byteoffset = bitoffset / 8 ;
+	uint32_t	result ;
+
+	//Assert (numbits <= 32) ;
+
+	load1 = read32bit (in + byteoffset) ;
+
+	if ((numbits + (bitoffset & 0x7)) > 32)
+	{
+		int32_t load2shift ;
+
+		result = load1 << (bitoffset & 0x7) ;
+		load2 = (uint32_t) in [byteoffset + 4] ;
+		load2shift = (8 - (numbits + (bitoffset & 0x7) - 32)) ;
+		load2 >>= load2shift ;
+		result >>= (32 - numbits) ;
+		result |= load2 ;
+	}
+	else
+	{
+		result = load1 >> (32 - numbits - (bitoffset & 7)) ;
+	}
+
+	// a shift of >= "the number of bits in the type of the value being shifted" results in undefined
+	// behavior so don't try to shift by 32
+	if (numbits != (sizeof (result) * 8))
+		result &= ~ (0xfffffffful << numbits) ;
+
+	return result ;
+}
+
+
+static inline int32_t dyn_get (unsigned char *in, uint32_t *bitPos, uint32_t m, uint32_t k)
+{
+	uint32_t	tempbits = *bitPos ;
+	uint32_t		result ;
+	uint32_t		pre = 0, v ;
+	uint32_t		streamlong ;
+
+	streamlong = read32bit (in + (tempbits >> 3)) ;
+	streamlong <<= (tempbits & 7) ;
+
+	/* find the number of bits in the prefix */
+	{
+		uint32_t	notI = ~streamlong ;
+		pre = lead (notI) ;
+	}
+
+	if (pre >= MAX_PREFIX_16)
+	{
+		pre = MAX_PREFIX_16 ;
+		tempbits += pre ;
+		streamlong <<= pre ;
+		result = get_next_fromlong (streamlong, MAX_DATATYPE_BITS_16) ;
+		tempbits += MAX_DATATYPE_BITS_16 ;
+
+	}
+	else
+	{
+		// all of the bits must fit within the long we have loaded
+		//Assert (pre+1+k <= 32) ;
+
+		tempbits += pre ;
+		tempbits += 1 ;
+		streamlong <<= pre + 1 ;
+		v = get_next_fromlong (streamlong, k) ;
+		tempbits += k ;
+
+		result = pre*m + v-1 ;
+
+		if (v < 2)
+		{
+			result -= (v-1) ;
+			tempbits -= 1 ;
+		}
+	}
+
+	*bitPos = tempbits ;
+	return result ;
+}
+
+
+static inline int32_t dyn_get_32bit (uint8_t * in, uint32_t * bitPos, int32_t m, int32_t k, int32_t maxbits)
+{
+	uint32_t	tempbits = *bitPos ;
+	uint32_t		v ;
+	uint32_t		streamlong ;
+	uint32_t		result ;
+
+	streamlong = read32bit (in + (tempbits >> 3)) ;
+	streamlong <<= (tempbits & 7) ;
+
+	/* find the number of bits in the prefix */
+	{
+		uint32_t notI = ~streamlong ;
+		result = lead (notI) ;
+	}
+
+	if (result >= MAX_PREFIX_32)
+	{
+		result = getstreambits (in, tempbits+MAX_PREFIX_32, maxbits) ;
+		tempbits += MAX_PREFIX_32 + maxbits ;
+	}
+	else
+	{
+		/* all of the bits must fit within the long we have loaded*/
+		//Assert (k<=14) ;
+		//Assert (result<MAX_PREFIX_32) ;
+		//Assert (result+1+k <= 32) ;
+
+		tempbits += result ;
+		tempbits += 1 ;
+
+		if (k != 1)
+		{
+			streamlong <<= result + 1 ;
+			v = get_next_fromlong (streamlong, k) ;
+			tempbits += k ;
+			tempbits -= 1 ;
+			result = result*m ;
+
+			if (v >= 2)
+			{
+				result += (v-1) ;
+				tempbits += 1 ;
+			}
+		}
+	}
+
+	*bitPos = tempbits ;
+
+	return result ;
+}
+
+int32_t dyn_decomp (AGParamRecPtr params, BitBuffer * bitstream, int32_t * pc, int32_t numSamples, int32_t maxSize, uint32_t * outNumBits)
+{
+	uint8_t 		*in ;
+	int32_t			*outPtr = pc ;
+	uint32_t 	bitPos, startPos, maxPos ;
+	uint32_t		j, m, k, n, c, mz ;
+	int32_t			del, zmode ;
+	uint32_t 	mb ;
+	uint32_t	pb_local = params->pb ;
+	uint32_t	kb_local = params->kb ;
+	uint32_t	wb_local = params->wb ;
+	int32_t				status ;
+
+	RequireAction ((bitstream != NULL) && (pc != NULL) && (outNumBits != NULL), return kALAC_ParamError ;) ;
+	*outNumBits = 0 ;
+
+	in = bitstream->cur ;
+	startPos = bitstream->bitIndex ;
+	maxPos = bitstream->byteSize * 8 ;
+	bitPos = startPos ;
+
+	mb = params->mb0 ;
+	zmode = 0 ;
+
+	c = 0 ;
+	status = ALAC_noErr ;
+
+	while (c < (uint32_t) numSamples)
+	{
+		// bail if we've run off the end of the buffer
+		RequireAction (bitPos < maxPos, status = kALAC_ParamError ; goto Exit ;) ;
+
+		m = (mb) >> QBSHIFT ;
+		k = lg3a (m) ;
+
+		k = arithmin (k, kb_local) ;
+		m = (1 << k) - 1 ;
+
+		n = dyn_get_32bit (in, &bitPos, m, k, maxSize) ;
+
+		// least significant bit is sign bit
+		{
+			uint32_t	ndecode = n + zmode ;
+			int32_t		multiplier = - (int) (ndecode & 1) ;
+
+			multiplier |= 1 ;
+			del = ((ndecode+1) >> 1) * (multiplier) ;
+		}
+
+		*outPtr++ = del ;
+
+		c++ ;
+
+		mb = pb_local * (n + zmode) + mb - ((pb_local * mb) >> QBSHIFT) ;
+
+		// update mean tracking
+		if (n > N_MAX_MEAN_CLAMP)
+			mb = N_MEAN_CLAMP_VAL ;
+
+		zmode = 0 ;
+
+		if (((mb << MMULSHIFT) < QB) && (c < (uint32_t) numSamples))
+		{
+			zmode = 1 ;
+			k = lead (mb) - BITOFF + ((mb + MOFF) >> MDENSHIFT) ;
+			mz = ((1 << k) - 1) & wb_local ;
+
+			n = dyn_get (in, &bitPos, mz, k) ;
+
+			RequireAction (c+n <= (uint32_t) numSamples, status = kALAC_ParamError ; goto Exit ;) ;
+
+			for (j = 0 ; j < n ; j++)
+			{
+				*outPtr++ = 0 ;
+				++c ;
+			}
+
+			if (n >= 65535)
+				zmode = 0 ;
+
+			mb = 0 ;
+		}
+	}
+
+Exit:
+	*outNumBits = (bitPos - startPos) ;
+	BitBufferAdvance (bitstream, *outNumBits) ;
+	RequireAction (bitstream->cur <= bitstream->end, status = kALAC_ParamError ;) ;
+
+	return status ;
+}
diff --git a/src/ALAC/ag_enc.c b/src/ALAC/ag_enc.c
new file mode 100644
index 0000000..4737b96
--- /dev/null
+++ b/src/ALAC/ag_enc.c
@@ -0,0 +1,352 @@
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	 http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+	File:		ag_enc.c
+
+	Contains:   Adaptive Golomb encode routines.
+
+	Copyright:	(c) 2001-2011 Apple, Inc.
+*/
+
+#include "aglib.h"
+#include "ALACBitUtilities.h"
+#include "EndianPortable.h"
+#include "ALACAudioTypes.h"
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define CODE_TO_LONG_MAXBITS	32
+#define N_MAX_MEAN_CLAMP		0xffff
+#define N_MEAN_CLAMP_VAL		0xffff
+#define REPORT_VAL				40
+
+#if __GNUC__
+#define ALWAYS_INLINE		__attribute__ ((always_inline))
+#elif defined _MSC_VER
+#define ALWAYS_INLINE		__forceinline
+#else
+#define ALWAYS_INLINE
+#endif
+
+
+/*	And on the subject of the CodeWarrior x86 compiler and inlining, I reworked a lot of this
+	to help the compiler out.   In many cases this required manual inlining or a macro.  Sorry
+	if it is ugly but the performance gains are well worth it.
+	- WSK 5/19/04
+*/
+
+// note: implementing this with some kind of "count leading zeros" assembly is a big performance win
+static inline int32_t lead (int32_t m)
+{
+	long j ;
+	unsigned long c = (1ul << 31) ;
+
+	for (j = 0 ; j < 32 ; j++)
+	{
+		if ((c & m) != 0)
+			break ;
+		c >>= 1 ;
+	}
+	return j ;
+}
+
+#define arithmin (a, b) ((a) < (b) ? (a) : (b))
+
+static inline int32_t ALWAYS_INLINE lg3a (int32_t x)
+{
+	int32_t result ;
+
+	x += 3 ;
+	result = lead (x) ;
+
+	return 31 - result ;
+}
+
+static inline int32_t ALWAYS_INLINE abs_func (int32_t a)
+{
+	// note: the CW PPC intrinsic __abs () turns into these instructions so no need to try and use it
+	int32_t isneg = a >> 31 ;
+	int32_t xorval = a ^ isneg ;
+	int32_t result = xorval-isneg ;
+
+	return result ;
+}
+
+#if PRAGMA_MARK
+#pragma mark -
+#endif
+
+static inline int32_t dyn_code (int32_t m, int32_t k, int32_t n, uint32_t *outNumBits)
+{
+	uint32_t 	divx, mod, de ;
+	uint32_t	numBits ;
+	uint32_t	value ;
+
+	// Assert (n >= 0) ;
+
+	divx = n / m ;
+
+	if (divx >= MAX_PREFIX_16)
+	{
+		numBits = MAX_PREFIX_16 + MAX_DATATYPE_BITS_16 ;
+		value = (((1 << MAX_PREFIX_16) - 1) << MAX_DATATYPE_BITS_16) + n ;
+	}
+	else
+	{
+		mod = n%m ;
+		de = (mod == 0) ;
+		numBits = divx + k + 1 - de ;
+		value = (((1 << divx) - 1) << (numBits - divx)) + mod + 1 - de ;
+
+		// if coding this way is bigger than doing escape, then do escape
+		if (numBits > MAX_PREFIX_16 + MAX_DATATYPE_BITS_16)
+		{
+			numBits = MAX_PREFIX_16 + MAX_DATATYPE_BITS_16 ;
+			value = (((1 << MAX_PREFIX_16) - 1) << MAX_DATATYPE_BITS_16) + n ;
+		}
+	}
+
+	*outNumBits = numBits ;
+
+	return (int32_t) value ;
+}
+
+
+static inline int32_t dyn_code_32bit (int32_t maxbits, uint32_t m, uint32_t k, uint32_t n, uint32_t *outNumBits, uint32_t *outValue, uint32_t *overflow, uint32_t *overflowbits)
+{
+	uint32_t 	divx, mod, de ;
+	uint32_t	numBits ;
+	uint32_t	value ;
+	int32_t			didOverflow = 0 ;
+
+	divx = n / m ;
+
+	if (divx < MAX_PREFIX_32)
+	{
+		mod = n - (m * divx) ;
+
+		de = (mod == 0) ;
+		numBits = divx + k + 1 - de ;
+		value = (((1 << divx) - 1) << (numBits - divx)) + mod + 1 - de ;
+		if (numBits > 25)
+			goto codeasescape ;
+	}
+	else
+	{
+codeasescape:
+		numBits = MAX_PREFIX_32 ;
+		value = (((1 << MAX_PREFIX_32) - 1)) ;
+		*overflow = n ;
+		*overflowbits = maxbits ;
+		didOverflow = 1 ;
+	}
+
+	*outNumBits = numBits ;
+	*outValue = value ;
+
+	return didOverflow ;
+}
+
+
+static inline void ALWAYS_INLINE dyn_jam_noDeref (unsigned char *out, uint32_t bitPos, uint32_t numBits, uint32_t value)
+{
+	uint32_t	mask ;
+	uint32_t	curr ;
+	uint32_t	shift ;
+
+	//Assert (numBits <= 32) ;
+
+	curr = psf_get_be32 (out, bitPos >> 3) ;
+
+	shift = 32 - (bitPos & 7) - numBits ;
+
+	mask = ~0u >> (32 - numBits) ;		// mask must be created in two steps to avoid compiler sequencing ambiguity
+	mask <<= shift ;
+
+	value = (value << shift) & mask ;
+	value |= curr & ~mask ;
+
+	psf_put_be32 (out, bitPos >> 3, value) ;
+}
+
+
+static inline void ALWAYS_INLINE dyn_jam_noDeref_large (unsigned char *out, uint32_t bitPos, uint32_t numBits, uint32_t value)
+{
+	uint32_t	w ;
+	uint32_t	curr ;
+	uint32_t	mask ;
+	int32_t		shiftvalue = (32 - (bitPos & 7) - numBits) ;
+
+	//Assert (numBits <= 32) ;
+
+	curr = psf_get_be32 (out, bitPos >> 3) ;
+
+	if (shiftvalue < 0)
+	{
+		uint8_t 	tailbyte ;
+		uint8_t 	*tailptr ;
+
+		w = value >> -shiftvalue ;
+		mask = ~0u >> -shiftvalue ;
+		w |= (curr & ~mask) ;
+
+		tailptr = out + (bitPos >> 3) + 4 ;
+		tailbyte = (value << ((8+shiftvalue))) & 0xff ;
+		*tailptr = (uint8_t) tailbyte ;
+	}
+	else
+	{
+		mask = ~0u >> (32 - numBits) ;
+		mask <<= shiftvalue ;			// mask must be created in two steps to avoid compiler sequencing ambiguity
+
+		w = (value << shiftvalue) & mask ;
+		w |= curr & ~mask ;
+	}
+
+	psf_put_be32 (out, bitPos >> 3, w) ;
+}
+
+
+int32_t dyn_comp (AGParamRecPtr params, int32_t * pc, BitBuffer * bitstream, int32_t numSamples, int32_t bitSize, uint32_t * outNumBits)
+{
+	unsigned char *		out ;
+	uint32_t		bitPos, startPos ;
+	uint32_t			m, k, n, c, mz, nz ;
+	uint32_t		numBits ;
+	uint32_t			value ;
+	int32_t				del, zmode ;
+	uint32_t		overflow, overflowbits ;
+	int32_t					status ;
+
+	// shadow the variables in params so there's not the dereferencing overhead
+	uint32_t		mb, pb, kb, wb ;
+	int32_t					rowPos = 0 ;
+	int32_t					rowSize = params->sw ;
+	int32_t					rowJump = (params->fw) - rowSize ;
+	int32_t *			inPtr = pc ;
+
+	*outNumBits = 0 ;
+	RequireAction ((bitSize >= 1) && (bitSize <= 32), return kALAC_ParamError ;) ;
+
+	out = bitstream->cur ;
+	startPos = bitstream->bitIndex ;
+	bitPos = startPos ;
+
+	mb = params->mb = params->mb0 ;
+	pb = params->pb ;
+	kb = params->kb ;
+	wb = params->wb ;
+	zmode = 0 ;
+
+	c = 0 ;
+	status = ALAC_noErr ;
+
+	while (c < (uint32_t) numSamples)
+	{
+		m = mb >> QBSHIFT ;
+		k = lg3a (m) ;
+		if (k > kb)
+		{
+			k = kb ;
+		}
+		m = (1 << k) - 1 ;
+
+		del = *inPtr++ ;
+		rowPos++ ;
+
+		n = (abs_func (del) << 1) - ((del >> 31) & 1) - zmode ;
+		//Assert (32-lead (n) <= bitSize) ;
+
+		if (dyn_code_32bit (bitSize, m, k, n, &numBits, &value, &overflow, &overflowbits))
+		{
+			dyn_jam_noDeref (out, bitPos, numBits, value) ;
+			bitPos += numBits ;
+			dyn_jam_noDeref_large (out, bitPos, overflowbits, overflow) ;
+			bitPos += overflowbits ;
+		}
+		else
+		{
+			dyn_jam_noDeref (out, bitPos, numBits, value) ;
+			bitPos += numBits ;
+		}
+
+		c++ ;
+		if (rowPos >= rowSize)
+		{
+			rowPos = 0 ;
+			inPtr += rowJump ;
+		}
+
+		mb = pb * (n + zmode) + mb - ((pb * mb) >> QBSHIFT) ;
+
+		// update mean tracking if it's overflowed
+		if (n > N_MAX_MEAN_CLAMP)
+			mb = N_MEAN_CLAMP_VAL ;
+
+		zmode = 0 ;
+
+		RequireAction (c <= (uint32_t) numSamples, status = kALAC_ParamError ; goto Exit ;) ;
+
+		if (((mb << MMULSHIFT) < QB) && (c < (uint32_t) numSamples))
+		{
+			zmode = 1 ;
+			nz = 0 ;
+
+			while (c < (uint32_t) numSamples && *inPtr == 0)
+			{
+				/* Take care of wrap-around globals. */
+				++inPtr ;
+				++nz ;
+				++c ;
+				if (++rowPos >= rowSize)
+				{
+					rowPos = 0 ;
+					inPtr += rowJump ;
+				}
+
+				if (nz >= 65535)
+				{
+					zmode = 0 ;
+					break ;
+				}
+			}
+
+			k = lead (mb) - BITOFF + ((mb + MOFF) >> MDENSHIFT) ;
+			mz = ((1 << k) - 1) & wb ;
+
+			value = dyn_code (mz, k, nz, &numBits) ;
+			dyn_jam_noDeref (out, bitPos, numBits, value) ;
+			bitPos += numBits ;
+
+			mb = 0 ;
+		}
+	}
+
+	*outNumBits = (bitPos - startPos) ;
+	BitBufferAdvance (bitstream, *outNumBits) ;
+
+Exit:
+	return status ;
+}
diff --git a/src/ALAC/aglib.h b/src/ALAC/aglib.h
new file mode 100644
index 0000000..825c4bd
--- /dev/null
+++ b/src/ALAC/aglib.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	 http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+	File:		aglib.h
+
+	Copyright:	(C) 2001-2011 Apple, Inc.
+*/
+
+#ifndef AGLIB_H
+#define AGLIB_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define QBSHIFT		9
+#define QB			(1 << QBSHIFT)
+#define PB0			40
+#define MB0			10
+#define KB0			14
+#define MAX_RUN_DEFAULT 255
+
+#define MMULSHIFT	2
+#define MDENSHIFT	(QBSHIFT - MMULSHIFT - 1)
+#define MOFF		((1 << (MDENSHIFT - 2)))
+
+#define BITOFF 24
+
+/* Max. prefix of 1's. */
+#define MAX_PREFIX_16			9
+#define MAX_PREFIX_TOLONG_16	15
+#define MAX_PREFIX_32			9
+
+/* Max. bits in 16-bit data type */
+#define MAX_DATATYPE_BITS_16	16
+
+typedef struct AGParamRec
+{
+	uint32_t mb, mb0, pb, kb, wb, qb ;
+	uint32_t fw, sw ;
+
+	uint32_t maxrun ;
+
+	// fw = 1, sw = 1 ;
+
+} AGParamRec, *AGParamRecPtr ;
+
+struct BitBuffer ;
+
+void	set_standard_ag_params (AGParamRecPtr params, uint32_t fullwidth, uint32_t sectorwidth) ;
+void	set_ag_params (AGParamRecPtr params, uint32_t m, uint32_t p, uint32_t k, uint32_t f, uint32_t s, uint32_t maxrun) ;
+
+int32_t	dyn_comp (AGParamRecPtr params, int32_t * pc, struct BitBuffer * bitstream, int32_t numSamples, int32_t bitSize, uint32_t * outNumBits) ;
+int32_t	dyn_decomp (AGParamRecPtr params, struct BitBuffer * bitstream, int32_t * pc, int32_t numSamples, int32_t maxSize, uint32_t * outNumBits) ;
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //#ifndef AGLIB_H
diff --git a/src/ALAC/alac_codec.h b/src/ALAC/alac_codec.h
new file mode 100644
index 0000000..e762b27
--- /dev/null
+++ b/src/ALAC/alac_codec.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+	File:		alac_codec.h
+*/
+
+#ifndef ALAC_CODEC_H
+#define ALAC_CODEC_H
+
+#include <stdint.h>
+
+#include "ALACAudioTypes.h"
+
+#define		ALAC_FRAME_LENGTH	4096
+
+struct BitBuffer ;
+
+typedef struct alac_decoder_s
+{
+	// decoding parameters (public for use in the analyzer)
+	ALACSpecificConfig		mConfig ;
+
+	uint16_t				mActiveElements ;
+
+	// decoding buffers
+	int32_t				mMixBufferU [ALAC_FRAME_LENGTH] ;
+	int32_t				mMixBufferV [ALAC_FRAME_LENGTH] ;
+	union
+	{
+		int32_t			mPredictor [ALAC_FRAME_LENGTH] ;
+		uint16_t		mShiftBuffer [ALAC_FRAME_LENGTH] ;
+	} u ;
+	uint32_t			mNumChannels ;
+} ALAC_DECODER ;
+
+typedef struct alac_encoder_s
+{
+	// ALAC encoder parameters
+	int16_t			mBitDepth ;
+
+	// encoding state
+	int16_t			mLastMixRes [kALACMaxChannels] ;
+
+	int32_t			mFastMode ;
+
+	// encoding buffers
+	int32_t			mMixBufferU [ALAC_FRAME_LENGTH] ;
+	int32_t			mMixBufferV [ALAC_FRAME_LENGTH] ;
+	int32_t			mPredictorU [ALAC_FRAME_LENGTH] ;
+	int32_t			mPredictorV [ALAC_FRAME_LENGTH] ;
+	uint16_t		mShiftBufferUV [2 * ALAC_FRAME_LENGTH] ;
+	uint8_t			mWorkBuffer [4 * ALAC_FRAME_LENGTH] ;
+
+	// per-channel coefficients buffers
+	int16_t			mCoefsU [kALACMaxChannels][kALACMaxSearches][kALACMaxCoefs] ;
+	int16_t			mCoefsV [kALACMaxChannels][kALACMaxSearches][kALACMaxCoefs] ;
+
+	// encoding statistics
+	uint32_t		mTotalBytesGenerated ;
+	uint32_t		mAvgBitRate ;
+	uint32_t		mMaxFrameBytes ;
+	uint32_t		mFrameSize ;
+	uint32_t		mMaxOutputBytes ;
+	uint32_t		mNumChannels ;
+	uint32_t		mOutputSampleRate ;
+} ALAC_ENCODER ;
+
+
+int32_t	alac_decoder_init (ALAC_DECODER *p, void * inMagicCookie, uint32_t inMagicCookieSize) ;
+int32_t alac_encoder_init (ALAC_ENCODER *p, uint32_t samplerate, uint32_t channels, uint32_t format_flags, uint32_t frameSize) ;
+
+int32_t	alac_decode (ALAC_DECODER *, struct BitBuffer * bits, int32_t * sampleBuffer,
+					uint32_t numSamples, uint32_t * outNumSamples) ;
+
+int32_t	alac_encode (ALAC_ENCODER *p, uint32_t numSamples,
+					const int32_t * theReadBuffer, unsigned char * theWriteBuffer,
+					uint32_t * ioNumBytes) ;
+
+void alac_set_fastmode (ALAC_ENCODER * p, int32_t fast) ;
+
+uint32_t alac_get_magic_cookie_size (uint32_t inNumChannels) ;
+void	alac_get_magic_cookie (ALAC_ENCODER *p, void * config, uint32_t * ioSize) ;
+void	alac_get_source_format (ALAC_ENCODER *p, const AudioFormatDescription * source, AudioFormatDescription * output) ;
+
+#endif
diff --git a/src/ALAC/alac_decoder.c b/src/ALAC/alac_decoder.c
new file mode 100644
index 0000000..46d3330
--- /dev/null
+++ b/src/ALAC/alac_decoder.c
@@ -0,0 +1,652 @@
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2015 Erik de Castro Lopo <erikd@mega-nerd.com>
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	 http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+	File:		ALACDecoder.cpp
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "alac_codec.h"
+
+#include "dplib.h"
+#include "aglib.h"
+#include "matrixlib.h"
+#include "shift.h"
+
+#include "ALACBitUtilities.h"
+#include "EndianPortable.h"
+
+typedef enum
+{	false = 0,
+	true = 1
+} bool ;
+
+// constants/data
+const uint32_t kMaxBitDepth = 32 ;			// max allowed bit depth is 32
+
+
+// prototypes
+static int32_t	alac_fill_element (struct BitBuffer * bits) ;
+static int32_t	alac_data_stream_element (struct BitBuffer * bits) ;
+
+static void Zero32 (int32_t * buffer, uint32_t numItems, uint32_t stride) ;
+
+
+/*
+	Init ()
+	- initialize the decoder with the given configuration
+*/
+int32_t
+alac_decoder_init (ALAC_DECODER *p, void * inMagicCookie, uint32_t inMagicCookieSize)
+{
+	int32_t		status = ALAC_noErr ;
+	ALACSpecificConfig theConfig ;
+	uint8_t * theActualCookie = (uint8_t *) inMagicCookie ;
+	uint32_t theCookieBytesRemaining = inMagicCookieSize ;
+
+	// For historical reasons the decoder needs to be resilient to magic cookies vended by older encoders.
+	// As specified in the ALACMagicCookieDescription.txt document, there may be additional data encapsulating
+	// the ALACSpecificConfig. This would consist of format ('frma') and 'alac' atoms which precede the
+	// ALACSpecificConfig.
+	// See ALACMagicCookieDescription.txt for additional documentation concerning the 'magic cookie'
+
+	// skip format ('frma') atom if present
+	if (theActualCookie [4] == 'f' && theActualCookie [5] == 'r' && theActualCookie [6] == 'm' && theActualCookie [7] == 'a')
+	{
+		theActualCookie += 12 ;
+		theCookieBytesRemaining -= 12 ;
+	}
+
+	// skip 'alac' atom header if present
+	if (theActualCookie [4] == 'a' && theActualCookie [5] == 'l' && theActualCookie [6] == 'a' && theActualCookie [7] == 'c')
+	{
+		theActualCookie += 12 ;
+		theCookieBytesRemaining -= 12 ;
+	}
+
+	// read the ALACSpecificConfig
+	if (theCookieBytesRemaining >= sizeof (ALACSpecificConfig))
+	{
+		theConfig.frameLength = psf_get_be32 (theActualCookie, offsetof (ALACSpecificConfig, frameLength)) ;
+
+		if (theConfig.frameLength > ALAC_FRAME_LENGTH)
+			return fALAC_FrameLengthError ;
+
+		theConfig.compatibleVersion = theActualCookie [offsetof (ALACSpecificConfig, compatibleVersion)] ;
+		theConfig.bitDepth = theActualCookie [offsetof (ALACSpecificConfig, bitDepth)] ;
+		theConfig.pb = theActualCookie [offsetof (ALACSpecificConfig, pb)] ;
+		theConfig.mb = theActualCookie [offsetof (ALACSpecificConfig, mb)] ;
+		theConfig.kb = theActualCookie [offsetof (ALACSpecificConfig, kb)] ;
+		theConfig.numChannels = theActualCookie [offsetof (ALACSpecificConfig, numChannels)] ;
+		theConfig.maxRun = psf_get_be16 (theActualCookie, offsetof (ALACSpecificConfig, maxRun)) ;
+		theConfig.maxFrameBytes = psf_get_be32 (theActualCookie, offsetof (ALACSpecificConfig, maxFrameBytes)) ;
+		theConfig.avgBitRate = psf_get_be32 (theActualCookie, offsetof (ALACSpecificConfig, avgBitRate)) ;
+		theConfig.sampleRate = psf_get_be32 (theActualCookie, offsetof (ALACSpecificConfig, sampleRate)) ;
+
+		p->mConfig = theConfig ;
+		p->mNumChannels = theConfig.numChannels ;
+
+		RequireAction (p->mConfig.compatibleVersion <= kALACVersion, return kALAC_IncompatibleVersion ;) ;
+		RequireAction ((p->mConfig.bitDepth >= 8 && p->mConfig.bitDepth <= 32), return kALAC_BadBitWidth ;) ;
+		RequireAction ((p->mMixBufferU != NULL) && (p->mMixBufferV != NULL) && (p->u.mPredictor != NULL),
+						status = kALAC_MemFullError ; goto Exit ;) ;
+	}
+	else
+	{
+		status = kALAC_BadSpecificConfigSize ;
+	}
+
+	// skip to Channel Layout Info
+	// theActualCookie += sizeof (ALACSpecificConfig) ;
+
+	// Currently, the Channel Layout Info portion of the magic cookie (as defined in the
+	// ALACMagicCookieDescription.txt document) is unused by the decoder.
+
+Exit:
+	return status ;
+}
+
+/*
+	Decode ()
+	- the decoded samples are interleaved into the output buffer in the order they arrive in
+	  the bitstream
+*/
+int32_t
+alac_decode (ALAC_DECODER *p, struct BitBuffer * bits, int32_t * sampleBuffer, uint32_t numSamples, uint32_t * outNumSamples)
+{
+	BitBuffer		shiftBits ;
+	uint32_t		bits1, bits2 ;
+	uint8_t			tag ;
+	uint8_t			elementInstanceTag ;
+	AGParamRec		agParams ;
+	uint32_t		channelIndex ;
+	int16_t			coefsU [32] ;		// max possible size is 32 although NUMCOEPAIRS is the current limit
+	int16_t			coefsV [32] ;
+	uint8_t			numU, numV ;
+	uint8_t			mixBits ;
+	int8_t			mixRes ;
+	uint16_t		unusedHeader ;
+	uint8_t			escapeFlag ;
+	uint32_t		chanBits ;
+	uint8_t			bytesShifted ;
+	uint32_t		shift ;
+	uint8_t			modeU, modeV ;
+	uint32_t		denShiftU, denShiftV ;
+	uint16_t		pbFactorU, pbFactorV ;
+	uint16_t		pb ;
+	int32_t *		out32 ;
+	uint8_t			headerByte ;
+	uint8_t			partialFrame ;
+	uint32_t		extraBits ;
+	int32_t			val ;
+	uint32_t		i, j ;
+	int32_t			status ;
+	uint32_t		numChannels = p->mNumChannels ;
+
+	RequireAction ((bits != NULL) && (sampleBuffer != NULL) && (outNumSamples != NULL), return kALAC_ParamError ;) ;
+	RequireAction (p->mNumChannels > 0, return kALAC_ZeroChannelCount ;) ;
+
+	p->mActiveElements = 0 ;
+	channelIndex	= 0 ;
+
+	status = ALAC_noErr ;
+	*outNumSamples = numSamples ;
+
+	while (status == ALAC_noErr)
+	{
+		// bail if we ran off the end of the buffer
+		RequireAction (bits->cur < bits->end, status = kALAC_ParamError ; goto Exit ;) ;
+
+		// copy global decode params for this element
+		pb = p->mConfig.pb ;
+
+		// read element tag
+		tag = BitBufferReadSmall (bits, 3) ;
+		switch (tag)
+		{
+			case ID_SCE:
+			case ID_LFE:
+			{
+				// mono/LFE channel
+				elementInstanceTag = BitBufferReadSmall (bits, 4) ;
+				p->mActiveElements |= (1u << elementInstanceTag) ;
+
+				// read the 12 unused header bits
+				unusedHeader = (uint16_t) BitBufferRead (bits, 12) ;
+				RequireAction (unusedHeader == 0, status = kALAC_ParamError ; goto Exit ;) ;
+
+				// read the 1-bit "partial frame" flag, 2-bit "shift-off" flag & 1-bit "escape" flag
+				headerByte = (uint8_t) BitBufferRead (bits, 4) ;
+
+				partialFrame = headerByte >> 3 ;
+
+				bytesShifted = (headerByte >> 1) & 0x3u ;
+				RequireAction (bytesShifted != 3, status = kALAC_ParamError ; goto Exit ;) ;
+
+				shift = bytesShifted * 8 ;
+
+				escapeFlag = headerByte & 0x1 ;
+
+				chanBits = p->mConfig.bitDepth - (bytesShifted * 8) ;
+
+				// check for partial frame to override requested numSamples
+				if (partialFrame != 0)
+				{
+					numSamples = BitBufferRead (bits, 16) << 16 ;
+					numSamples |= BitBufferRead (bits, 16) ;
+
+					RequireAction (numSamples < kALACDefaultFramesPerPacket, return kALAC_NumSamplesTooBig ;) ;
+				}
+
+				if (escapeFlag == 0)
+				{
+					// compressed frame, read rest of parameters
+					mixBits	= (uint8_t) BitBufferRead (bits, 8) ;
+					mixRes	= (int8_t) BitBufferRead (bits, 8) ;
+					//Assert ((mixBits == 0) && (mixRes == 0)) ;		// no mixing for mono
+
+					headerByte	= (uint8_t) BitBufferRead (bits, 8) ;
+					modeU		= headerByte >> 4 ;
+					denShiftU	= headerByte & 0xfu ;
+
+					headerByte	= (uint8_t) BitBufferRead (bits, 8) ;
+					pbFactorU	= headerByte >> 5 ;
+					numU		= headerByte & 0x1fu ;
+
+					for (i = 0 ; i < numU ; i++)
+						coefsU [i] = (int16_t) BitBufferRead (bits, 16) ;
+
+					// if shift active, skip the shift buffer but remember where it starts
+					if (bytesShifted != 0)
+					{
+						shiftBits = *bits ;
+						BitBufferAdvance (bits, (bytesShifted * 8) * numSamples) ;
+					}
+
+					// decompress
+					set_ag_params (&agParams, p->mConfig.mb, (pb * pbFactorU) / 4, p->mConfig.kb, numSamples, numSamples, p->mConfig.maxRun) ;
+					status = dyn_decomp (&agParams, bits, p->u.mPredictor, numSamples, chanBits, &bits1) ;
+					RequireNoErr (status, goto Exit ;) ;
+
+					if (modeU == 0)
+					{
+						unpc_block (p->u.mPredictor, p->mMixBufferU, numSamples, &coefsU [0], numU, chanBits, denShiftU) ;
+					}
+					else
+					{
+						// the special "numActive == 31" mode can be done in-place
+						unpc_block (p->u.mPredictor, p->u.mPredictor, numSamples, NULL, 31, chanBits, 0) ;
+						unpc_block (p->u.mPredictor, p->mMixBufferU, numSamples, &coefsU [0], numU, chanBits, denShiftU) ;
+					}
+				}
+				else
+				{
+					//Assert (bytesShifted == 0) ;
+
+					// uncompressed frame, copy data into the mix buffer to use common output code
+					shift = 32 - chanBits ;
+					if (chanBits <= 16)
+					{
+						for (i = 0 ; i < numSamples ; i++)
+						{
+							val = (int32_t) BitBufferRead (bits, (uint8_t) chanBits) ;
+							val = (val << shift) >> shift ;
+							p->mMixBufferU [i] = val ;
+						}
+					}
+					else
+					{
+						// BitBufferRead () can't read more than 16 bits at a time so break up the reads
+						extraBits = chanBits - 16 ;
+						for (i = 0 ; i < numSamples ; i++)
+						{
+							val = (int32_t) BitBufferRead (bits, 16) ;
+							val = arith_shift_left (val, 16) >> shift ;
+							p->mMixBufferU [i] = val | BitBufferRead (bits, (uint8_t) extraBits) ;
+						}
+					}
+
+					mixBits = mixRes = 0 ;
+					bits1 = chanBits * numSamples ;
+					bytesShifted = 0 ;
+				}
+
+				// now read the shifted values into the shift buffer
+				if (bytesShifted != 0)
+				{
+					shift = bytesShifted * 8 ;
+					//Assert (shift <= 16) ;
+
+					for (i = 0 ; i < numSamples ; i++)
+						p->u.mShiftBuffer [i] = (uint16_t) BitBufferRead (&shiftBits, (uint8_t) shift) ;
+				}
+
+				// convert 32-bit integers into output buffer
+				switch (p->mConfig.bitDepth)
+				{
+					case 16:
+						out32 = sampleBuffer + channelIndex ;
+						for (i = 0, j = 0 ; i < numSamples ; i++, j += numChannels)
+							out32 [j] = arith_shift_left (p->mMixBufferU [i], 16) ;
+						break ;
+					case 20:
+						out32 = sampleBuffer + channelIndex ;
+						copyPredictorTo20 (p->mMixBufferU, out32, numChannels, numSamples) ;
+						break ;
+					case 24:
+						out32 = sampleBuffer + channelIndex ;
+						if (bytesShifted != 0)
+							copyPredictorTo24Shift (p->mMixBufferU, p->u.mShiftBuffer, out32, numChannels, numSamples, bytesShifted) ;
+						else
+							copyPredictorTo24 (p->mMixBufferU, out32, numChannels, numSamples) ;
+						break ;
+					case 32:
+						out32 = sampleBuffer + channelIndex ;
+						if (bytesShifted != 0)
+							copyPredictorTo32Shift (p->mMixBufferU, p->u.mShiftBuffer, out32, numChannels, numSamples, bytesShifted) ;
+						else
+							copyPredictorTo32 (p->mMixBufferU, out32, numChannels, numSamples) ;
+						break ;
+				}
+
+				channelIndex += 1 ;
+				*outNumSamples = numSamples ;
+				break ;
+			}
+
+			case ID_CPE:
+			{
+				// if decoding this pair would take us over the max channels limit, bail
+				if ((channelIndex + 2) > numChannels)
+					goto NoMoreChannels ;
+
+				// stereo channel pair
+				elementInstanceTag = BitBufferReadSmall (bits, 4) ;
+				p->mActiveElements |= (1u << elementInstanceTag) ;
+
+				// read the 12 unused header bits
+				unusedHeader = (uint16_t) BitBufferRead (bits, 12) ;
+				RequireAction (unusedHeader == 0, status = kALAC_ParamError ; goto Exit ;) ;
+
+				// read the 1-bit "partial frame" flag, 2-bit "shift-off" flag & 1-bit "escape" flag
+				headerByte = (uint8_t) BitBufferRead (bits, 4) ;
+
+				partialFrame = headerByte >> 3 ;
+
+				bytesShifted = (headerByte >> 1) & 0x3u ;
+				RequireAction (bytesShifted != 3, status = kALAC_ParamError ; goto Exit ;) ;
+
+				shift = bytesShifted * 8 ;
+
+				escapeFlag = headerByte & 0x1 ;
+
+				chanBits = p->mConfig.bitDepth - (bytesShifted * 8) + 1 ;
+
+				// check for partial frame length to override requested numSamples
+				if (partialFrame != 0)
+				{
+					numSamples = BitBufferRead (bits, 16) << 16 ;
+					numSamples |= BitBufferRead (bits, 16) ;
+
+					RequireAction (numSamples < kALACDefaultFramesPerPacket, return kALAC_NumSamplesTooBig ;) ;
+				}
+
+				if (escapeFlag == 0)
+				{
+					// compressed frame, read rest of parameters
+					mixBits		= (uint8_t) BitBufferRead (bits, 8) ;
+					mixRes		= (int8_t) BitBufferRead (bits, 8) ;
+
+					headerByte	= (uint8_t) BitBufferRead (bits, 8) ;
+					modeU		= headerByte >> 4 ;
+					denShiftU	= headerByte & 0xfu ;
+
+					headerByte	= (uint8_t) BitBufferRead (bits, 8) ;
+					pbFactorU	= headerByte >> 5 ;
+					numU		= headerByte & 0x1fu ;
+					for (i = 0 ; i < numU ; i++)
+						coefsU [i] = (int16_t) BitBufferRead (bits, 16) ;
+
+					headerByte	= (uint8_t) BitBufferRead (bits, 8) ;
+					modeV		= headerByte >> 4 ;
+					denShiftV	= headerByte & 0xfu ;
+
+					headerByte	= (uint8_t) BitBufferRead (bits, 8) ;
+					pbFactorV	= headerByte >> 5 ;
+					numV		= headerByte & 0x1fu ;
+					for (i = 0 ; i < numV ; i++)
+						coefsV [i] = (int16_t) BitBufferRead (bits, 16) ;
+
+					// if shift active, skip the interleaved shifted values but remember where they start
+					if (bytesShifted != 0)
+					{
+						shiftBits = *bits ;
+						BitBufferAdvance (bits, (bytesShifted * 8) * 2 * numSamples) ;
+					}
+
+					// decompress and run predictor for "left" channel
+					set_ag_params (&agParams, p->mConfig.mb, (pb * pbFactorU) / 4, p->mConfig.kb, numSamples, numSamples, p->mConfig.maxRun) ;
+					status = dyn_decomp (&agParams, bits, p->u.mPredictor, numSamples, chanBits, &bits1) ;
+					RequireNoErr (status, goto Exit ;) ;
+
+					if (modeU == 0)
+					{
+						unpc_block (p->u.mPredictor, p->mMixBufferU, numSamples, &coefsU [0], numU, chanBits, denShiftU) ;
+					}
+					else
+					{
+						// the special "numActive == 31" mode can be done in-place
+						unpc_block (p->u.mPredictor, p->u.mPredictor, numSamples, NULL, 31, chanBits, 0) ;
+						unpc_block (p->u.mPredictor, p->mMixBufferU, numSamples, &coefsU [0], numU, chanBits, denShiftU) ;
+					}
+
+					// decompress and run predictor for "right" channel
+					set_ag_params (&agParams, p->mConfig.mb, (pb * pbFactorV) / 4, p->mConfig.kb, numSamples, numSamples, p->mConfig.maxRun) ;
+					status = dyn_decomp (&agParams, bits, p->u.mPredictor, numSamples, chanBits, &bits2) ;
+					RequireNoErr (status, goto Exit ;) ;
+
+					if (modeV == 0)
+					{
+						unpc_block (p->u.mPredictor, p->mMixBufferV, numSamples, &coefsV [0], numV, chanBits, denShiftV) ;
+					}
+					else
+					{
+						// the special "numActive == 31" mode can be done in-place
+						unpc_block (p->u.mPredictor, p->u.mPredictor, numSamples, NULL, 31, chanBits, 0) ;
+						unpc_block (p->u.mPredictor, p->mMixBufferV, numSamples, &coefsV [0], numV, chanBits, denShiftV) ;
+					}
+				}
+				else
+				{
+					//Assert (bytesShifted == 0) ;
+
+					// uncompressed frame, copy data into the mix buffers to use common output code
+					chanBits = p->mConfig.bitDepth ;
+					shift = 32 - chanBits ;
+					if (chanBits <= 16)
+					{
+						for (i = 0 ; i < numSamples ; i++)
+						{
+							val = (int32_t) BitBufferRead (bits, (uint8_t) chanBits) ;
+							val = (val << shift) >> shift ;
+							p->mMixBufferU [i] = val ;
+
+							val = (int32_t) BitBufferRead (bits, (uint8_t) chanBits) ;
+							val = (val << shift) >> shift ;
+							p->mMixBufferV [i] = val ;
+						}
+					}
+					else
+					{
+						// BitBufferRead () can't read more than 16 bits at a time so break up the reads
+						extraBits = chanBits - 16 ;
+						for (i = 0 ; i < numSamples ; i++)
+						{
+							val = (int32_t) BitBufferRead (bits, 16) ;
+							val = (((uint32_t) val) << 16) >> shift ;
+							p->mMixBufferU [i] = val | BitBufferRead (bits, (uint8_t) extraBits) ;
+
+							val = (int32_t) BitBufferRead (bits, 16) ;
+							val = ((uint32_t) val) >> shift ;
+							p->mMixBufferV [i] = val | BitBufferRead (bits, (uint8_t) extraBits) ;
+						}
+					}
+
+					bits1 = chanBits * numSamples ;
+					bits2 = chanBits * numSamples ;
+					mixBits = mixRes = 0 ;
+					bytesShifted = 0 ;
+				}
+
+				// now read the shifted values into the shift buffer
+				if (bytesShifted != 0)
+				{
+					shift = bytesShifted * 8 ;
+					//Assert (shift <= 16) ;
+
+					for (i = 0 ; i < (numSamples * 2) ; i += 2)
+					{
+						p->u.mShiftBuffer [i + 0] = (uint16_t) BitBufferRead (&shiftBits, (uint8_t) shift) ;
+						p->u.mShiftBuffer [i + 1] = (uint16_t) BitBufferRead (&shiftBits, (uint8_t) shift) ;
+					}
+				}
+
+				// un-mix the data and convert to output format
+				// - note that mixRes = 0 means just interleave so we use that path for uncompressed frames
+				switch (p->mConfig.bitDepth)
+				{
+					case 16:
+						out32 = sampleBuffer + channelIndex ;
+						unmix16 (p->mMixBufferU, p->mMixBufferV, out32, numChannels, numSamples, mixBits, mixRes) ;
+						break ;
+					case 20:
+						out32 = sampleBuffer + channelIndex ;
+						unmix20 (p->mMixBufferU, p->mMixBufferV, out32, numChannels, numSamples, mixBits, mixRes) ;
+						break ;
+					case 24:
+						out32 = sampleBuffer + channelIndex ;
+						unmix24 (p->mMixBufferU, p->mMixBufferV, out32, numChannels, numSamples,
+									mixBits, mixRes, p->u.mShiftBuffer, bytesShifted) ;
+						break ;
+					case 32:
+						out32 = sampleBuffer + channelIndex ;
+						unmix32 (p->mMixBufferU, p->mMixBufferV, out32, numChannels, numSamples,
+									mixBits, mixRes, p->u.mShiftBuffer, bytesShifted) ;
+						break ;
+				}
+
+				channelIndex += 2 ;
+				*outNumSamples = numSamples ;
+				break ;
+			}
+
+			case ID_CCE:
+			case ID_PCE:
+			{
+				// unsupported element, bail
+				//AssertNoErr (tag) ;
+				status = kALAC_UnsupportedElement ;
+				break ;
+			}
+
+			case ID_DSE:
+			{
+				// data stream element -- parse but ignore
+				status = alac_data_stream_element (bits) ;
+				break ;
+			}
+
+			case ID_FIL:
+			{
+				// fill element -- parse but ignore
+				status = alac_fill_element (bits) ;
+				break ;
+			}
+
+			case ID_END:
+			{
+				// frame end, all done so byte align the frame and check for overruns
+				BitBufferByteAlign (bits, false) ;
+				//Assert (bits->cur == bits->end) ;
+				goto Exit ;
+			}
+		}
+
+#if 1 // ! DEBUG
+		// if we've decoded all of our channels, bail (but not in debug b/c we want to know if we're seeing bad bits)
+		// - this also protects us if the config does not match the bitstream or crap data bits follow the audio bits
+		if (channelIndex >= numChannels)
+			break ;
+#endif
+	}
+
+NoMoreChannels:
+
+	// if we get here and haven't decoded all of the requested channels, fill the remaining channels with zeros
+	for ( ; channelIndex < numChannels ; channelIndex++)
+	{
+		int32_t *	fill32 = sampleBuffer + channelIndex ;
+		Zero32 (fill32, numSamples, numChannels) ;
+	}
+
+Exit:
+	return status ;
+}
+
+#if PRAGMA_MARK
+#pragma mark -
+#endif
+
+/*
+	FillElement ()
+	- they're just filler so we don't need 'em
+*/
+static int32_t
+alac_fill_element (struct BitBuffer * bits)
+{
+	int16_t		count ;
+
+	// 4-bit count or (4-bit + 8-bit count) if 4-bit count == 15
+	// - plus this weird -1 thing I still don't fully understand
+	count = BitBufferReadSmall (bits, 4) ;
+	if (count == 15)
+		count += (int16_t) BitBufferReadSmall (bits, 8) - 1 ;
+
+	BitBufferAdvance (bits, count * 8) ;
+
+	RequireAction (bits->cur <= bits->end, return kALAC_ParamError ;) ;
+
+	return ALAC_noErr ;
+}
+
+/*
+	DataStreamElement ()
+	- we don't care about data stream elements so just skip them
+*/
+static int32_t
+alac_data_stream_element (struct BitBuffer * bits)
+{
+	int32_t		data_byte_align_flag ;
+	uint16_t		count ;
+
+	// the tag associates this data stream element with a given audio element
+
+	/* element_instance_tag = */ BitBufferReadSmall (bits, 4) ;
+
+	data_byte_align_flag = BitBufferReadOne (bits) ;
+
+	// 8-bit count or (8-bit + 8-bit count) if 8-bit count == 255
+	count = BitBufferReadSmall (bits, 8) ;
+	if (count == 255)
+		count += BitBufferReadSmall (bits, 8) ;
+
+	// the align flag means the bitstream should be byte-aligned before reading the following data bytes
+	if (data_byte_align_flag)
+		BitBufferByteAlign (bits, false) ;
+
+	// skip the data bytes
+	BitBufferAdvance (bits, count * 8) ;
+
+	RequireAction (bits->cur <= bits->end, return kALAC_ParamError ;) ;
+
+	return ALAC_noErr ;
+}
+
+/*
+	ZeroN ()
+	- helper routines to clear out output channel buffers when decoding fewer channels than requested
+*/
+static void Zero32 (int32_t * buffer, uint32_t numItems, uint32_t stride)
+{
+	if (stride == 1)
+	{
+		memset (buffer, 0, numItems * sizeof (int32_t)) ;
+	}
+	else
+	{
+		for (uint32_t indx = 0 ; indx < (numItems * stride) ; indx += stride)
+			buffer [indx] = 0 ;
+	}
+}
diff --git a/src/ALAC/alac_decoder.h b/src/ALAC/alac_decoder.h
new file mode 100644
index 0000000..8a43592
--- /dev/null
+++ b/src/ALAC/alac_decoder.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+	File:		alac_decoder.h
+*/
+
+#ifndef ALAC_DECODER_H
+#define ALAC_DECODER_H
+
+#include <stdint.h>
+
+#include "ALACAudioTypes.h"
+
+typedef enum
+{
+	false = 0,
+	ALAC_TRUE = 1
+} bool ;
+
+struct BitBuffer ;
+
+typedef struct alac_decoder
+{
+		// decoding parameters (public for use in the analyzer)
+		ALACSpecificConfig		mConfig ;
+
+		uint16_t					mActiveElements ;
+
+		// decoding buffers
+		int32_t *				mMixBufferU ;
+		int32_t *				mMixBufferV ;
+		int32_t *				mPredictor ;
+		uint16_t *				mShiftBuffer ;	// note: this points to mPredictor's memory but different
+												//		 variable for clarity and type difference
+} alac_decoder ;
+
+alac_decoder * alac_decoder_new (void) ;
+void alac_decoder_delete (alac_decoder *) ;
+
+int32_t	alac_init (alac_decoder *p, void * inMagicCookie, uint32_t inMagicCookieSize) ;
+int32_t	alac_decode (alac_decoder *, struct BitBuffer * bits, uint8_t * sampleBuffer, uint32_t numSamples, uint32_t numChannels, uint32_t * outNumSamples) ;
+
+#endif	/* ALAC_DECODER_H */
diff --git a/src/ALAC/alac_encoder.c b/src/ALAC/alac_encoder.c
new file mode 100644
index 0000000..599399a
--- /dev/null
+++ b/src/ALAC/alac_encoder.c
@@ -0,0 +1,1333 @@
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2015 Erik de Castro Lopo <erikd@mega-nerd.com>
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	 http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+	File:		ALACEncoder.cpp
+*/
+
+// build stuff
+#define VERBOSE_DEBUG		0
+#define DebugMsg			printf
+
+// headers
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "sfendian.h"
+
+#include "alac_codec.h"
+
+#include "aglib.h"
+#include "dplib.h"
+#include "matrixlib.h"
+
+#include "ALACBitUtilities.h"
+#include "ALACAudioTypes.h"
+#include "EndianPortable.h"
+
+typedef enum
+{
+	false = 0,
+	true = 1
+} bool ;
+
+static void	GetConfig (ALAC_ENCODER *p, ALACSpecificConfig * config) ;
+
+static int32_t	EncodeStereo (ALAC_ENCODER *p, struct BitBuffer * bitstream, const int32_t * input, uint32_t stride, uint32_t channelIndex, uint32_t numSamples) ;
+static int32_t	EncodeStereoFast (ALAC_ENCODER *p, struct BitBuffer * bitstream, const int32_t * input, uint32_t stride, uint32_t channelIndex, uint32_t numSamples) ;
+static int32_t	EncodeStereoEscape (ALAC_ENCODER *p, struct BitBuffer * bitstream, const int32_t * input, uint32_t stride, uint32_t numSamples) ;
+static int32_t	EncodeMono (ALAC_ENCODER *p, struct BitBuffer * bitstream, const int32_t * input, uint32_t stride, uint32_t channelIndex, uint32_t numSamples) ;
+
+
+
+// Note: in C you can't typecast to a 2-dimensional array pointer but that's what we need when
+// picking which coefs to use so we declare this typedef b/c we *can* typecast to this type
+typedef int16_t (*SearchCoefs) [kALACMaxCoefs] ;
+
+// defines/constants
+const uint32_t kALACEncoderMagic	= MAKE_MARKER ('d', 'p', 'g', 'e') ;
+const uint32_t kMaxSampleSize		= 32 ;			// max allowed bit width is 32
+const uint32_t kDefaultMixBits		= 2 ;
+const uint32_t kDefaultMixRes		= 0 ;
+const uint32_t kMaxRes				= 4 ;
+const uint32_t kDefaultNumUV		= 8 ;
+const uint32_t kMinUV				= 4 ;
+const uint32_t kMaxUV				= 8 ;
+
+// static functions
+#if VERBOSE_DEBUG
+static void AddFiller (BitBuffer * bits, int32_t numBytes) ;
+#endif
+
+
+/*
+	Map Format: 3-bit field per channel which is the same as the "element tag" that should be placed
+				at the beginning of the frame for that channel.  Indicates whether SCE, CPE, or LFE.
+				Each particular field is accessed via the current channel indx.  Note that the channel
+				indx increments by two for channel pairs.
+
+	For example:
+
+			C L R 3-channel input		= (ID_CPE << 3) | (ID_SCE)
+				indx 0 value = (map & (0x7ul << (0 * 3))) >> (0 * 3)
+				indx 1 value = (map & (0x7ul << (1 * 3))) >> (1 * 3)
+
+			C L R Ls Rs LFE 5.1-channel input = (ID_LFE << 15) | (ID_CPE << 9) | (ID_CPE << 3) | (ID_SCE)
+				indx 0 value = (map & (0x7ul << (0 * 3))) >> (0 * 3)
+				indx 1 value = (map & (0x7ul << (1 * 3))) >> (1 * 3)
+				indx 3 value = (map & (0x7ul << (3 * 3))) >> (3 * 3)
+				indx 5 value = (map & (0x7ul << (5 * 3))) >> (5 * 3)
+				indx 7 value = (map & (0x7ul << (7 * 3))) >> (7 * 3)
+*/
+static const uint32_t	sChannelMaps [kALACMaxChannels] =
+{
+	ID_SCE,
+	ID_CPE,
+	(ID_CPE << 3) | (ID_SCE),
+	(ID_SCE << 9) | (ID_CPE << 3) | (ID_SCE),
+	(ID_CPE << 9) | (ID_CPE << 3) | (ID_SCE),
+	(ID_SCE << 15) | (ID_CPE << 9) | (ID_CPE << 3) | (ID_SCE),
+	(ID_SCE << 18) | (ID_SCE << 15) | (ID_CPE << 9) | (ID_CPE << 3) | (ID_SCE),
+	(ID_SCE << 21) | (ID_CPE << 15) | (ID_CPE << 9) | (ID_CPE << 3) | (ID_SCE)
+} ;
+
+#if PRAGMA_MARK
+#pragma mark -
+#endif
+
+void
+alac_set_fastmode (ALAC_ENCODER * p, int32_t fast)
+{
+	p->mFastMode = fast ;
+}
+
+
+/*
+	HEADER SPECIFICATION
+
+		For every segment we adopt the following header:
+
+			1 byte reserved			(always 0)
+			1 byte flags			(see below)
+			[4 byte frame length]	(optional, see below)
+				 ---Next, the per-segment ALAC parameters---
+			1 byte mixBits			(middle-side parameter)
+			1 byte mixRes			(middle-side parameter, interpreted as signed char)
+
+			1 byte shiftU			(4 bits modeU, 4 bits denShiftU)
+			1 byte filterU			(3 bits pbFactorU, 5 bits numU)
+			(numU) shorts			(signed DP coefficients for V channel)
+				 ---Next, 2nd-channel ALAC parameters in case of stereo mode---
+			1 byte shiftV			(4 bits modeV, 4 bits denShiftV)
+			1 byte filterV			(3 bits pbFactorV, 5 bits numV)
+			(numV) shorts			(signed DP coefficients for V channel)
+				 ---After this come the shift-off bytes for (>= 24)-bit data (n-byte shift) if indicated---
+				 ---Then comes the AG-compressor bitstream---
+
+
+		FLAGS
+		-----
+
+		The presence of certain flag bits changes the header format such that the parameters might
+		not even be sent.  The currently defined flags format is:
+
+			0000psse
+
+			where		0 	= reserved, must be 0
+						p	= 1-bit field "partial frame" flag indicating 32-bit frame length follows this byte
+						ss	= 2-bit field indicating "number of shift-off bytes ignored by compression"
+						e	= 1-bit field indicating "escape"
+
+		The "partial frame" flag means that the following segment is not equal to the frame length specified
+		in the out-of-band decoder configuration.  This allows the decoder to deal with end-of-file partial
+		segments without incurring the 32-bit overhead for each segment.
+
+		The "shift-off" field indicates the number of bytes at the bottom of the word that were passed through
+		uncompressed.  The reason for this is that the entropy inherent in the LS bytes of >= 24-bit words
+		quite often means that the frame would have to be "escaped" b/c the compressed size would be >= the
+		uncompressed size.  However, by shifting the input values down and running the remaining bits through
+		the normal compression algorithm, a net win can be achieved.  If this field is non-zero, it means that
+		the shifted-off bytes follow after the parameter section of the header and before the compressed
+		bitstream.  Note that doing this also allows us to use matrixing on 32-bit inputs after one or more
+		bytes are shifted off the bottom which helps the eventual compression ratio.  For stereo channels,
+		the shifted off bytes are interleaved.
+
+		The "escape" flag means that this segment was not compressed b/c the compressed size would be
+		>= uncompressed size.  In that case, the audio data was passed through uncompressed after the header.
+		The other header parameter bytes will not be sent.
+
+
+		PARAMETERS
+		----------
+
+		If the segment is not a partial or escape segment, the total header size (in bytes) is given exactly by:
+
+			4 + (2 + 2 * numU)				   (mono mode)
+			4 + (2 + 2 * numV) + (2 + 2 * numV)  (stereo mode)
+
+		where the ALAC filter-lengths numU, numV are bounded by a
+		constant (in the current source, numU, numV <= NUMCOEPAIRS), and
+		this forces an absolute upper bound on header size.
+
+		Each segment-decode process loads up these bytes from the front of the
+		local stream, in the above order, then follows with the entropy-encoded
+		bits for the given segment.
+
+		To generalize middle-side, there are various mixing modes including middle-side, each lossless,
+		as embodied in the mix () and unmix () functions.  These functions exploit a generalized middle-side
+		transformation:
+
+		u := [(rL + (m-r)R)/m] ;
+		v := L - R ;
+
+		where [ ] denotes integer floor.  The (lossless) inverse is
+
+		L = u + v - [rV/m] ;
+		R = L - v ;
+
+		In the segment header, m and r are encoded in mixBits and mixRes.
+		Classical "middle-side" is obtained with m = 2, r = 1, but now
+		we have more generalized mixes.
+
+		NOTES
+		-----
+		The relevance of the ALAC coefficients is explained in detail
+		in patent documents.
+*/
+
+/*
+	EncodeStereo ()
+	- encode a channel pair
+*/
+static int32_t
+EncodeStereo (ALAC_ENCODER *p, struct BitBuffer * bitstream, const int32_t * inputBuffer, uint32_t stride, uint32_t channelIndex, uint32_t numSamples)
+{
+	BitBuffer		workBits ;
+	BitBuffer		startBits = *bitstream ;			// squirrel away copy of current state in case we need to go back and do an escape packet
+	AGParamRec		agParams ;
+	uint32_t		bits1, bits2 ;
+	uint32_t		dilate ;
+	int32_t			mixBits, mixRes, maxRes ;
+	uint32_t		minBits, minBits1, minBits2 ;
+	uint32_t		numU, numV ;
+	uint32_t		mode ;
+	uint32_t		pbFactor ;
+	uint32_t		chanBits ;
+	uint8_t			bytesShifted ;
+	SearchCoefs		coefsU ;
+	SearchCoefs		coefsV ;
+	uint32_t		indx ;
+	uint8_t			partialFrame ;
+	uint32_t		escapeBits ;
+	bool			doEscape ;
+	int32_t			status = ALAC_noErr ;
+	int32_t			bestRes ;
+
+	// make sure we handle this bit-depth before we get going
+	RequireAction ((p->mBitDepth == 16) || (p->mBitDepth == 20) || (p->mBitDepth == 24) || (p->mBitDepth == 32), return kALAC_ParamError ;) ;
+
+	// reload coefs pointers for this channel pair
+	// - note that, while you might think they should be re-initialized per block, retaining state across blocks
+	//	 actually results in better overall compression
+	// - strangely, re-using the same coefs for the different passes of the "mixRes" search loop instead of using
+	//	 different coefs for the different passes of "mixRes" results in even better compression
+	coefsU = (SearchCoefs) p->mCoefsU [channelIndex] ;
+	coefsV = (SearchCoefs) p->mCoefsV [channelIndex] ;
+
+	// matrix encoding adds an extra bit but 32-bit inputs cannot be matrixed b/c 33 is too many
+	// so enable 16-bit "shift off" and encode in 17-bit mode
+	// - in addition, 24-bit mode really improves with one byte shifted off
+	if (p->mBitDepth == 32)
+		bytesShifted = 2 ;
+	else if (p->mBitDepth >= 24)
+		bytesShifted = 1 ;
+	else
+		bytesShifted = 0 ;
+
+	chanBits = p->mBitDepth - (bytesShifted * 8) + 1 ;
+
+	// flag whether or not this is a partial frame
+	partialFrame = (numSamples == p->mFrameSize) ? 0 : 1 ;
+
+	// brute-force encode optimization loop
+	// - run over variations of the encoding params to find the best choice
+	mixBits		= kDefaultMixBits ;
+	maxRes		= kMaxRes ;
+	numU = numV = kDefaultNumUV ;
+	mode		= 0 ;
+	pbFactor	= 4 ;
+	dilate		= 8 ;
+
+	minBits	= minBits1 = minBits2 = 1ul << 31 ;
+
+	bestRes = p->mLastMixRes [channelIndex] ;
+
+	for (mixRes = 0 ; mixRes <= maxRes ; mixRes++)
+	{
+		// mix the stereo inputs
+		switch (p->mBitDepth)
+		{
+			case 16:
+				mix16 (inputBuffer, stride, p->mMixBufferU, p->mMixBufferV, numSamples / dilate, mixBits, mixRes) ;
+				break ;
+			case 20:
+				mix20 (inputBuffer, stride, p->mMixBufferU, p->mMixBufferV, numSamples / dilate, mixBits, mixRes) ;
+				break ;
+			case 24:
+				// includes extraction of shifted-off bytes
+				mix24 (inputBuffer, stride, p->mMixBufferU, p->mMixBufferV, numSamples / dilate,
+						mixBits, mixRes, p->mShiftBufferUV, bytesShifted) ;
+				break ;
+			case 32:
+				// includes extraction of shifted-off bytes
+				mix32 (inputBuffer, stride, p->mMixBufferU, p->mMixBufferV, numSamples / dilate,
+						mixBits, mixRes, p->mShiftBufferUV, bytesShifted) ;
+				break ;
+		}
+
+		BitBufferInit (&workBits, p->mWorkBuffer, p->mMaxOutputBytes) ;
+
+		// run the dynamic predictors
+		pc_block (p->mMixBufferU, p->mPredictorU, numSamples / dilate, coefsU [numU - 1], numU, chanBits, DENSHIFT_DEFAULT) ;
+		pc_block (p->mMixBufferV, p->mPredictorV, numSamples / dilate, coefsV [numV - 1], numV, chanBits, DENSHIFT_DEFAULT) ;
+
+		// run the lossless compressor on each channel
+		set_ag_params (&agParams, MB0, (pbFactor * PB0) / 4, KB0, numSamples / dilate, numSamples / dilate, MAX_RUN_DEFAULT) ;
+		status = dyn_comp (&agParams, p->mPredictorU, &workBits, numSamples / dilate, chanBits, &bits1) ;
+		RequireNoErr (status, goto Exit ;) ;
+
+		set_ag_params (&agParams, MB0, (pbFactor * PB0) / 4, KB0, numSamples / dilate, numSamples / dilate, MAX_RUN_DEFAULT) ;
+		status = dyn_comp (&agParams, p->mPredictorV, &workBits, numSamples / dilate, chanBits, &bits2) ;
+		RequireNoErr (status, goto Exit ;) ;
+
+		// look for best match
+		if ((bits1 + bits2) < minBits1)
+		{
+			minBits1 = bits1 + bits2 ;
+			bestRes = mixRes ;
+		}
+	}
+
+	p->mLastMixRes [channelIndex] = (int16_t) bestRes ;
+
+	// mix the stereo inputs with the current best mixRes
+	mixRes = p->mLastMixRes [channelIndex] ;
+	switch (p->mBitDepth)
+	{
+		case 16:
+			mix16 (inputBuffer, stride, p->mMixBufferU, p->mMixBufferV, numSamples, mixBits, mixRes) ;
+			break ;
+		case 20:
+			mix20 (inputBuffer, stride, p->mMixBufferU, p->mMixBufferV, numSamples, mixBits, mixRes) ;
+			break ;
+		case 24:
+			// also extracts the shifted off bytes into the shift buffers
+			mix24 (inputBuffer, stride, p->mMixBufferU, p->mMixBufferV, numSamples,
+					mixBits, mixRes, p->mShiftBufferUV, bytesShifted) ;
+			break ;
+		case 32:
+			// also extracts the shifted off bytes into the shift buffers
+			mix32 (inputBuffer, stride, p->mMixBufferU, p->mMixBufferV, numSamples,
+					mixBits, mixRes, p->mShiftBufferUV, bytesShifted) ;
+			break ;
+	}
+
+	// now it's time for the predictor coefficient search loop
+	numU = numV = kMinUV ;
+	minBits1 = minBits2 = 1ul << 31 ;
+
+	for (uint32_t numUV = kMinUV ; numUV <= kMaxUV ; numUV += 4)
+	{
+		BitBufferInit (&workBits, p->mWorkBuffer, p->mMaxOutputBytes) ;
+
+		dilate = 32 ;
+
+		// run the predictor over the same data multiple times to help it converge
+		for (uint32_t converge = 0 ; converge < 8 ; converge++)
+		{
+			pc_block (p->mMixBufferU, p->mPredictorU, numSamples / dilate, coefsU [numUV-1], numUV, chanBits, DENSHIFT_DEFAULT) ;
+			pc_block (p->mMixBufferV, p->mPredictorV, numSamples / dilate, coefsV [numUV-1], numUV, chanBits, DENSHIFT_DEFAULT) ;
+		}
+
+		dilate = 8 ;
+
+		set_ag_params (&agParams, MB0, (pbFactor * PB0) / 4, KB0, numSamples / dilate, numSamples / dilate, MAX_RUN_DEFAULT) ;
+		status = dyn_comp (&agParams, p->mPredictorU, &workBits, numSamples / dilate, chanBits, &bits1) ;
+
+		if ((bits1 * dilate + 16 * numUV) < minBits1)
+		{
+			minBits1 = bits1 * dilate + 16 * numUV ;
+			numU = numUV ;
+		}
+
+		set_ag_params (&agParams, MB0, (pbFactor * PB0) / 4, KB0, numSamples / dilate, numSamples / dilate, MAX_RUN_DEFAULT) ;
+		status = dyn_comp (&agParams, p->mPredictorV, &workBits, numSamples / dilate, chanBits, &bits2) ;
+
+		if ((bits2 * dilate + 16 * numUV) < minBits2)
+		{
+			minBits2 = bits2 * dilate + 16 * numUV ;
+			numV = numUV ;
+		}
+	}
+
+	// test for escape hatch if best calculated compressed size turns out to be more than the input size
+	minBits = minBits1 + minBits2 + (8 /* mixRes/maxRes/etc. */ * 8) + ((partialFrame == true) ? 32 : 0) ;
+	if (bytesShifted != 0)
+		minBits += (numSamples * (bytesShifted * 8) * 2) ;
+
+	escapeBits = (numSamples * p->mBitDepth * 2) + ((partialFrame == true) ? 32 : 0) + (2 * 8) ;	/* 2 common header bytes */
+
+	doEscape = (minBits >= escapeBits) ? true : false ;
+
+	if (doEscape == false)
+	{
+		// write bitstream header and coefs
+		BitBufferWrite (bitstream, 0, 12) ;
+		BitBufferWrite (bitstream, (partialFrame << 3) | (bytesShifted << 1), 4) ;
+		if (partialFrame)
+			BitBufferWrite (bitstream, numSamples, 32) ;
+		BitBufferWrite (bitstream, mixBits, 8) ;
+		BitBufferWrite (bitstream, mixRes, 8) ;
+
+		//Assert ((mode < 16) && (DENSHIFT_DEFAULT < 16)) ;
+		//Assert ((pbFactor < 8) && (numU < 32)) ;
+		//Assert ((pbFactor < 8) && (numV < 32)) ;
+
+		BitBufferWrite (bitstream, (mode << 4) | DENSHIFT_DEFAULT, 8) ;
+		BitBufferWrite (bitstream, (pbFactor << 5) | numU, 8) ;
+		for (indx = 0 ; indx < numU ; indx++)
+			BitBufferWrite (bitstream, coefsU [numU - 1][indx], 16) ;
+
+		BitBufferWrite (bitstream, (mode << 4) | DENSHIFT_DEFAULT, 8) ;
+		BitBufferWrite (bitstream, (pbFactor << 5) | numV, 8) ;
+		for (indx = 0 ; indx < numV ; indx++)
+			BitBufferWrite (bitstream, coefsV [numV - 1][indx], 16) ;
+
+		// if shift active, write the interleaved shift buffers
+		if (bytesShifted != 0)
+		{
+			uint32_t		bitShift = bytesShifted * 8 ;
+
+			//Assert (bitShift <= 16) ;
+
+			for (indx = 0 ; indx < (numSamples * 2) ; indx += 2)
+			{
+				uint32_t			shiftedVal ;
+
+				shiftedVal = ((uint32_t) p->mShiftBufferUV [indx + 0] << bitShift) | (uint32_t) p->mShiftBufferUV [indx + 1] ;
+				BitBufferWrite (bitstream, shiftedVal, bitShift * 2) ;
+			}
+		}
+
+		// run the dynamic predictor and lossless compression for the "left" channel
+		// - note: to avoid allocating more buffers, we're mixing and matching between the available buffers instead
+		//		   of only using "U" buffers for the U-channel and "V" buffers for the V-channel
+		if (mode == 0)
+		{
+			pc_block (p->mMixBufferU, p->mPredictorU, numSamples, coefsU [numU - 1], numU, chanBits, DENSHIFT_DEFAULT) ;
+		}
+		else
+		{
+			pc_block (p->mMixBufferU, p->mPredictorV, numSamples, coefsU [numU - 1], numU, chanBits, DENSHIFT_DEFAULT) ;
+			pc_block (p->mPredictorV, p->mPredictorU, numSamples, NULL, 31, chanBits, 0) ;
+		}
+
+		set_ag_params (&agParams, MB0, (pbFactor * PB0) / 4, KB0, numSamples, numSamples, MAX_RUN_DEFAULT) ;
+		status = dyn_comp (&agParams, p->mPredictorU, bitstream, numSamples, chanBits, &bits1) ;
+		RequireNoErr (status, goto Exit ;) ;
+
+		// run the dynamic predictor and lossless compression for the "right" channel
+		if (mode == 0)
+		{
+			pc_block (p->mMixBufferV, p->mPredictorV, numSamples, coefsV [numV - 1], numV, chanBits, DENSHIFT_DEFAULT) ;
+		}
+		else
+		{
+			pc_block (p->mMixBufferV, p->mPredictorU, numSamples, coefsV [numV - 1], numV, chanBits, DENSHIFT_DEFAULT) ;
+			pc_block (p->mPredictorU, p->mPredictorV, numSamples, NULL, 31, chanBits, 0) ;
+		}
+
+		set_ag_params (&agParams, MB0, (pbFactor * PB0) / 4, KB0, numSamples, numSamples, MAX_RUN_DEFAULT) ;
+		status = dyn_comp (&agParams, p->mPredictorV, bitstream, numSamples, chanBits, &bits2) ;
+		RequireNoErr (status, goto Exit ;) ;
+
+		/*	if we happened to create a compressed packet that was actually bigger than an escape packet would be,
+			chuck it and do an escape packet
+		*/
+		minBits = BitBufferGetPosition (bitstream) - BitBufferGetPosition (&startBits) ;
+		if (minBits >= escapeBits)
+		{
+			*bitstream = startBits ;		// reset bitstream state
+			doEscape = true ;
+			printf ("compressed frame too big: %u vs. %u \n", minBits, escapeBits) ;
+		}
+	}
+
+	if (doEscape == true)
+	{
+		/* escape */
+		status = EncodeStereoEscape (p, bitstream, inputBuffer, stride, numSamples) ;
+
+#if VERBOSE_DEBUG
+		DebugMsg ("escape!: %u vs %u\n", minBits, escapeBits) ;
+#endif
+	}
+
+Exit:
+	return status ;
+}
+
+/*
+	EncodeStereoFast ()
+	- encode a channel pair without the search loop for maximum possible speed
+*/
+static int32_t
+EncodeStereoFast (ALAC_ENCODER *p, struct BitBuffer * bitstream, const int32_t * inputBuffer, uint32_t stride, uint32_t channelIndex, uint32_t numSamples)
+{
+	BitBuffer		startBits = *bitstream ;			// squirrel away current bit position in case we decide to use escape hatch
+	AGParamRec		agParams ;
+	uint32_t		bits1, bits2 ;
+	int32_t			mixBits, mixRes ;
+	uint32_t		minBits, minBits1, minBits2 ;
+	uint32_t		numU, numV ;
+	uint32_t		mode ;
+	uint32_t		pbFactor ;
+	uint32_t		chanBits ;
+	uint8_t			bytesShifted ;
+	SearchCoefs		coefsU ;
+	SearchCoefs		coefsV ;
+	uint32_t		indx ;
+	uint8_t			partialFrame ;
+	uint32_t		escapeBits ;
+	bool			doEscape ;
+	int32_t			status ;
+
+	// make sure we handle this bit-depth before we get going
+	RequireAction ((p->mBitDepth == 16) || (p->mBitDepth == 20) || (p->mBitDepth == 24) || (p->mBitDepth == 32), return kALAC_ParamError ;) ;
+
+	// reload coefs pointers for this channel pair
+	// - note that, while you might think they should be re-initialized per block, retaining state across blocks
+	//	 actually results in better overall compression
+	// - strangely, re-using the same coefs for the different passes of the "mixRes" search loop instead of using
+	//	 different coefs for the different passes of "mixRes" results in even better compression
+	coefsU = (SearchCoefs) p->mCoefsU [channelIndex] ;
+	coefsV = (SearchCoefs) p->mCoefsV [channelIndex] ;
+
+	// matrix encoding adds an extra bit but 32-bit inputs cannot be matrixed b/c 33 is too many
+	// so enable 16-bit "shift off" and encode in 17-bit mode
+	// - in addition, 24-bit mode really improves with one byte shifted off
+	if (p->mBitDepth == 32)
+		bytesShifted = 2 ;
+	else if (p->mBitDepth >= 24)
+		bytesShifted = 1 ;
+	else
+		bytesShifted = 0 ;
+
+	chanBits = p->mBitDepth - (bytesShifted * 8) + 1 ;
+
+	// flag whether or not this is a partial frame
+	partialFrame = (numSamples == p->mFrameSize) ? 0 : 1 ;
+
+	// set up default encoding parameters for "fast" mode
+	mixBits		= kDefaultMixBits ;
+	mixRes		= kDefaultMixRes ;
+	numU = numV = kDefaultNumUV ;
+	mode		= 0 ;
+	pbFactor	= 4 ;
+
+	minBits	= minBits1 = minBits2 = 1ul << 31 ;
+
+	// mix the stereo inputs with default mixBits/mixRes
+	switch (p->mBitDepth)
+	{
+		case 16:
+			mix16 (inputBuffer, stride, p->mMixBufferU, p->mMixBufferV, numSamples, mixBits, mixRes) ;
+			break ;
+		case 20:
+			mix20 (inputBuffer, stride, p->mMixBufferU, p->mMixBufferV, numSamples, mixBits, mixRes) ;
+			break ;
+		case 24:
+			// also extracts the shifted off bytes into the shift buffers
+			mix24 (inputBuffer, stride, p->mMixBufferU, p->mMixBufferV, numSamples,
+					mixBits, mixRes, p->mShiftBufferUV, bytesShifted) ;
+			break ;
+		case 32:
+			// also extracts the shifted off bytes into the shift buffers
+			mix32 (inputBuffer, stride, p->mMixBufferU, p->mMixBufferV, numSamples,
+					mixBits, mixRes, p->mShiftBufferUV, bytesShifted) ;
+			break ;
+	}
+
+	/* speculatively write the bitstream assuming the compressed version will be smaller */
+
+	// write bitstream header and coefs
+	BitBufferWrite (bitstream, 0, 12) ;
+	BitBufferWrite (bitstream, (partialFrame << 3) | (bytesShifted << 1), 4) ;
+	if (partialFrame)
+		BitBufferWrite (bitstream, numSamples, 32) ;
+	BitBufferWrite (bitstream, mixBits, 8) ;
+	BitBufferWrite (bitstream, mixRes, 8) ;
+
+	//Assert ((mode < 16) && (DENSHIFT_DEFAULT < 16)) ;
+	//Assert ((pbFactor < 8) && (numU < 32)) ;
+	//Assert ((pbFactor < 8) && (numV < 32)) ;
+
+	BitBufferWrite (bitstream, (mode << 4) | DENSHIFT_DEFAULT, 8) ;
+	BitBufferWrite (bitstream, (pbFactor << 5) | numU, 8) ;
+	for (indx = 0 ; indx < numU ; indx++)
+		BitBufferWrite (bitstream, coefsU [numU - 1][indx], 16) ;
+
+	BitBufferWrite (bitstream, (mode << 4) | DENSHIFT_DEFAULT, 8) ;
+	BitBufferWrite (bitstream, (pbFactor << 5) | numV, 8) ;
+	for (indx = 0 ; indx < numV ; indx++)
+		BitBufferWrite (bitstream, coefsV [numV - 1][indx], 16) ;
+
+	// if shift active, write the interleaved shift buffers
+	if (bytesShifted != 0)
+	{
+		uint32_t		bitShift = bytesShifted * 8 ;
+
+		//Assert (bitShift <= 16) ;
+
+		for (indx = 0 ; indx < (numSamples * 2) ; indx += 2)
+		{
+			uint32_t			shiftedVal ;
+
+			shiftedVal = ((uint32_t) p->mShiftBufferUV [indx + 0] << bitShift) | (uint32_t) p->mShiftBufferUV [indx + 1] ;
+			BitBufferWrite (bitstream, shiftedVal, bitShift * 2) ;
+		}
+	}
+
+	// run the dynamic predictor and lossless compression for the "left" channel
+	// - note: we always use mode 0 in the "fast" path so we don't need the code for mode != 0
+	pc_block (p->mMixBufferU, p->mPredictorU, numSamples, coefsU [numU - 1], numU, chanBits, DENSHIFT_DEFAULT) ;
+
+	set_ag_params (&agParams, MB0, (pbFactor * PB0) / 4, KB0, numSamples, numSamples, MAX_RUN_DEFAULT) ;
+	status = dyn_comp (&agParams, p->mPredictorU, bitstream, numSamples, chanBits, &bits1) ;
+	RequireNoErr (status, goto Exit ;) ;
+
+	// run the dynamic predictor and lossless compression for the "right" channel
+	pc_block (p->mMixBufferV, p->mPredictorV, numSamples, coefsV [numV - 1], numV, chanBits, DENSHIFT_DEFAULT) ;
+
+	set_ag_params (&agParams, MB0, (pbFactor * PB0) / 4, KB0, numSamples, numSamples, MAX_RUN_DEFAULT) ;
+	status = dyn_comp (&agParams, p->mPredictorV, bitstream, numSamples, chanBits, &bits2) ;
+	RequireNoErr (status, goto Exit ;) ;
+
+	// do bit requirement calculations
+	minBits1 = bits1 + (numU * sizeof (int16_t) * 8) ;
+	minBits2 = bits2 + (numV * sizeof (int16_t) * 8) ;
+
+	// test for escape hatch if best calculated compressed size turns out to be more than the input size
+	minBits = minBits1 + minBits2 + (8 /* mixRes/maxRes/etc. */ * 8) + ((partialFrame == true) ? 32 : 0) ;
+	if (bytesShifted != 0)
+		minBits += (numSamples * (bytesShifted * 8) * 2) ;
+
+	escapeBits = (numSamples * p->mBitDepth * 2) + ((partialFrame == true) ? 32 : 0) + (2 * 8) ;	/* 2 common header bytes */
+
+	doEscape = (minBits >= escapeBits) ? true : false ;
+
+	if (doEscape == false)
+	{
+		/*	if we happened to create a compressed packet that was actually bigger than an escape packet would be,
+			chuck it and do an escape packet
+		*/
+		minBits = BitBufferGetPosition (bitstream) - BitBufferGetPosition (&startBits) ;
+		if (minBits >= escapeBits)
+		{
+			doEscape = true ;
+			printf ("compressed frame too big: %u vs. %u\n", minBits, escapeBits) ;
+		}
+
+	}
+
+	if (doEscape == true)
+	{
+		/* escape */
+
+		// reset bitstream position since we speculatively wrote the compressed version
+		*bitstream = startBits ;
+
+		// write escape frame
+		status = EncodeStereoEscape (p, bitstream, inputBuffer, stride, numSamples) ;
+
+#if VERBOSE_DEBUG
+		DebugMsg ("escape!: %u vs %u\n", minBits, (numSamples * p->mBitDepth * 2)) ;
+#endif
+	}
+
+Exit:
+	return status ;
+}
+
+/*
+	EncodeStereoEscape ()
+	- encode stereo escape frame
+*/
+static int32_t
+EncodeStereoEscape (ALAC_ENCODER *p, struct BitBuffer * bitstream, const int32_t * inputBuffer, uint32_t stride, uint32_t numSamples)
+{
+	uint8_t			partialFrame ;
+	uint32_t		indx ;
+
+	// flag whether or not this is a partial frame
+	partialFrame = (numSamples == p->mFrameSize) ? 0 : 1 ;
+
+	// write bitstream header
+	BitBufferWrite (bitstream, 0, 12) ;
+	BitBufferWrite (bitstream, (partialFrame << 3) | 1, 4) ;	// LSB = 1 means "frame not compressed"
+	if (partialFrame)
+		BitBufferWrite (bitstream, numSamples, 32) ;
+
+	// just copy the input data to the output buffer
+	switch (p->mBitDepth)
+	{
+		case 16:
+			for (indx = 0 ; indx < (numSamples * stride) ; indx += stride)
+			{
+				BitBufferWrite (bitstream, inputBuffer [indx + 0] >> 16, 16) ;
+				BitBufferWrite (bitstream, inputBuffer [indx + 1] >> 16, 16) ;
+			}
+			break ;
+		case 20:
+			for (indx = 0 ; indx < (numSamples * stride) ; indx += stride)
+			{
+				BitBufferWrite (bitstream, inputBuffer [indx + 0] >> 12, 16) ;
+				BitBufferWrite (bitstream, inputBuffer [indx + 1] >> 12, 16) ;
+			}
+			break ;
+		case 24:
+			// mix24 () with mixres param = 0 means de-interleave so use it to simplify things
+			mix24 (inputBuffer, stride, p->mMixBufferU, p->mMixBufferV, numSamples, 0, 0, p->mShiftBufferUV, 0) ;
+			for (indx = 0 ; indx < numSamples ; indx++)
+			{
+				BitBufferWrite (bitstream, p->mMixBufferU [indx] >> 8, 24) ;
+				BitBufferWrite (bitstream, p->mMixBufferV [indx] >> 8, 24) ;
+			}
+			break ;
+		case 32:
+			for (indx = 0 ; indx < (numSamples * stride) ; indx += stride)
+			{
+				BitBufferWrite (bitstream, inputBuffer [indx + 0], 32) ;
+				BitBufferWrite (bitstream, inputBuffer [indx + 1], 32) ;
+			}
+			break ;
+	}
+
+	return ALAC_noErr ;
+}
+
+/*
+	EncodeMono ()
+	- encode a mono input buffer
+*/
+static int32_t
+EncodeMono (ALAC_ENCODER *p, struct BitBuffer * bitstream, const int32_t * inputBuffer, uint32_t stride, uint32_t channelIndex, uint32_t numSamples)
+{
+	BitBuffer		startBits = *bitstream ;			// squirrel away copy of current state in case we need to go back and do an escape packet
+	AGParamRec		agParams ;
+	uint32_t		bits1 ;
+	uint32_t		numU ;
+	SearchCoefs		coefsU ;
+	uint32_t		dilate ;
+	uint32_t		minBits, bestU ;
+	uint32_t		minU, maxU ;
+	uint32_t		indx, indx2 ;
+	uint8_t			bytesShifted ;
+	uint32_t		shift ;
+	uint32_t		mask ;
+	uint32_t		chanBits ;
+	uint8_t			pbFactor ;
+	uint8_t			partialFrame ;
+	uint32_t		escapeBits ;
+	bool			doEscape ;
+	int32_t			status = ALAC_noErr ;
+
+
+	// make sure we handle this bit-depth before we get going
+	RequireAction ((p->mBitDepth == 16) || (p->mBitDepth == 20) || (p->mBitDepth == 24) || (p->mBitDepth == 32), return kALAC_ParamError ;) ;
+
+	// reload coefs array from previous frame
+	coefsU = (SearchCoefs) p->mCoefsU [channelIndex] ;
+
+	// pick bit depth for actual encoding
+	// - we lop off the lower byte (s) for 24-/32-bit encodings
+	if (p->mBitDepth == 32)
+		bytesShifted = 2 ;
+	else if (p->mBitDepth >= 24)
+		bytesShifted = 1 ;
+	else
+		bytesShifted = 0 ;
+
+	shift = bytesShifted * 8 ;
+	mask = (1ul << shift) - 1 ;
+	chanBits = p->mBitDepth - (bytesShifted * 8) ;
+
+	// flag whether or not this is a partial frame
+	partialFrame = (numSamples == p->mFrameSize) ? 0 : 1 ;
+
+	// convert N-bit data to 32-bit for predictor
+	switch (p->mBitDepth)
+	{
+		case 16:
+			// convert 16-bit data to 32-bit for predictor
+			for (indx = 0, indx2 = 0 ; indx < numSamples ; indx++, indx2 += stride)
+				p->mMixBufferU [indx] = inputBuffer [indx2] >> 16 ;
+			break ;
+
+		case 20:
+			// convert 20-bit data to 32-bit for predictor
+			for (indx = 0, indx2 = 0 ; indx < numSamples ; indx++, indx2 += stride)
+				p->mMixBufferU [indx] = inputBuffer [indx2] >> 12 ;
+			break ;
+		case 24:
+			// convert 24-bit data to 32-bit for the predictor and extract the shifted off byte (s)
+			for (indx = 0, indx2 = 0 ; indx < numSamples ; indx++, indx2 += stride)
+			{
+				p->mMixBufferU [indx] = inputBuffer [indx2] >> 8 ;
+				p->mShiftBufferUV [indx] = (uint16_t) (p->mMixBufferU [indx] & mask) ;
+				p->mMixBufferU [indx] >>= shift ;
+			}
+
+			break ;
+		case 32:
+			// just copy the 32-bit input data for the predictor and extract the shifted off byte (s)
+			for (indx = 0, indx2 = 0 ; indx < numSamples ; indx++, indx2 += stride)
+			{
+				p->mShiftBufferUV [indx] = (uint16_t) (inputBuffer [indx2] & mask) ;
+				p->mMixBufferU [indx] = inputBuffer [indx2] >> shift ;
+			}
+			break ;
+	}
+
+	// brute-force encode optimization loop (implied "encode depth" of 0 if comparing to cmd line tool)
+	// - run over variations of the encoding params to find the best choice
+	minU		= 4 ;
+	maxU		= 8 ;
+	minBits		= 1ul << 31 ;
+	pbFactor	= 4 ;
+
+	bestU	= minU ;
+
+	for (numU = minU ; numU <= maxU ; numU += 4)
+	{
+		BitBuffer		workBits ;
+		uint32_t		numBits ;
+
+		BitBufferInit (&workBits, p->mWorkBuffer, p->mMaxOutputBytes) ;
+
+		dilate = 32 ;
+		for (uint32_t converge = 0 ; converge < 7 ; converge++)
+			pc_block (p->mMixBufferU, p->mPredictorU, numSamples / dilate, coefsU [numU - 1], numU, chanBits, DENSHIFT_DEFAULT) ;
+
+		dilate = 8 ;
+		pc_block (p->mMixBufferU, p->mPredictorU, numSamples / dilate, coefsU [numU - 1], numU, chanBits, DENSHIFT_DEFAULT) ;
+
+		set_ag_params (&agParams, MB0, (pbFactor * PB0) / 4, KB0, numSamples / dilate, numSamples / dilate, MAX_RUN_DEFAULT) ;
+		status = dyn_comp (&agParams, p->mPredictorU, &workBits, numSamples / dilate, chanBits, &bits1) ;
+		RequireNoErr (status, goto Exit ;) ;
+
+		numBits = (dilate * bits1) + (16 * numU) ;
+		if (numBits < minBits)
+		{
+			bestU	= numU ;
+			minBits = numBits ;
+		}
+	}
+
+	// test for escape hatch if best calculated compressed size turns out to be more than the input size
+	// - first, add bits for the header bytes mixRes/maxRes/shiftU/filterU
+	minBits += (4 /* mixRes/maxRes/etc. */ * 8) + ((partialFrame == true) ? 32 : 0) ;
+	if (bytesShifted != 0)
+		minBits += (numSamples * (bytesShifted * 8)) ;
+
+	escapeBits = (numSamples * p->mBitDepth) + ((partialFrame == true) ? 32 : 0) + (2 * 8) ;	/* 2 common header bytes */
+
+	doEscape = (minBits >= escapeBits) ? true : false ;
+
+	if (doEscape == false)
+	{
+		// write bitstream header
+		BitBufferWrite (bitstream, 0, 12) ;
+		BitBufferWrite (bitstream, (partialFrame << 3) | (bytesShifted << 1), 4) ;
+		if (partialFrame)
+			BitBufferWrite (bitstream, numSamples, 32) ;
+		BitBufferWrite (bitstream, 0, 16) ;								// mixBits = mixRes = 0
+
+		// write the params and predictor coefs
+		numU = bestU ;
+		BitBufferWrite (bitstream, (0 << 4) | DENSHIFT_DEFAULT, 8) ;	// modeU = 0
+		BitBufferWrite (bitstream, (pbFactor << 5) | numU, 8) ;
+		for (indx = 0 ; indx < numU ; indx++)
+			BitBufferWrite (bitstream, coefsU [numU-1][indx], 16) ;
+
+		// if shift active, write the interleaved shift buffers
+		if (bytesShifted != 0)
+		{
+			for (indx = 0 ; indx < numSamples ; indx++)
+				BitBufferWrite (bitstream, p->mShiftBufferUV [indx], shift) ;
+		}
+
+		// run the dynamic predictor with the best result
+		pc_block (p->mMixBufferU, p->mPredictorU, numSamples, coefsU [numU-1], numU, chanBits, DENSHIFT_DEFAULT) ;
+
+		// do lossless compression
+		set_standard_ag_params (&agParams, numSamples, numSamples) ;
+		status = dyn_comp (&agParams, p->mPredictorU, bitstream, numSamples, chanBits, &bits1) ;
+		//AssertNoErr (status) ;
+
+
+		/*	if we happened to create a compressed packet that was actually bigger than an escape packet would be,
+			chuck it and do an escape packet
+		*/
+		minBits = BitBufferGetPosition (bitstream) - BitBufferGetPosition (&startBits) ;
+		if (minBits >= escapeBits)
+		{
+			*bitstream = startBits ;		// reset bitstream state
+			doEscape = true ;
+			printf ("compressed frame too big: %u vs. %u\n", minBits, escapeBits) ;
+		}
+	}
+
+	if (doEscape == true)
+	{
+		// write bitstream header and coefs
+		BitBufferWrite (bitstream, 0, 12) ;
+		BitBufferWrite (bitstream, (partialFrame << 3) | 1, 4) ;	// LSB = 1 means "frame not compressed"
+		if (partialFrame)
+			BitBufferWrite (bitstream, numSamples, 32) ;
+
+		// just copy the input data to the output buffer
+		switch (p->mBitDepth)
+		{
+			case 16:
+				for (indx = 0 ; indx < (numSamples * stride) ; indx += stride)
+					BitBufferWrite (bitstream, inputBuffer [indx] >> 16, 16) ;
+				break ;
+			case 20:
+				// convert 20-bit data to 32-bit for simplicity
+				for (indx = 0 ; indx < (numSamples * stride) ; indx += stride)
+					BitBufferWrite (bitstream, inputBuffer [indx] >> 12, 20) ;
+				break ;
+			case 24:
+				// convert 24-bit data to 32-bit for simplicity
+				for (indx = 0, indx2 = 0 ; indx < numSamples ; indx++, indx2 += stride)
+				{
+					p->mMixBufferU [indx] = inputBuffer [indx2] >> 8 ;
+					BitBufferWrite (bitstream, p->mMixBufferU [indx], 24) ;
+				}
+				break ;
+			case 32:
+				for (indx = 0 ; indx < (numSamples * stride) ; indx += stride)
+					BitBufferWrite (bitstream, inputBuffer [indx], 32) ;
+				break ;
+		}
+#if VERBOSE_DEBUG
+		DebugMsg ("escape!: %u vs %u\n", minBits, (numSamples * p->mBitDepth)) ;
+#endif
+	}
+
+Exit:
+	return status ;
+}
+
+#if PRAGMA_MARK
+#pragma mark -
+#endif
+
+/*
+	Encode ()
+	- encode the next block of samples
+*/
+int32_t
+alac_encode (ALAC_ENCODER *p, uint32_t numSamples,
+			const int32_t * theReadBuffer, unsigned char * theWriteBuffer, uint32_t * ioNumBytes)
+{
+	uint32_t		outputSize ;
+	BitBuffer		bitstream ;
+	int32_t			status ;
+	uint32_t 		numChannels = p->mNumChannels ;
+
+	// make sure we handle this bit-depth before we get going
+	RequireAction ((p->mBitDepth == 16) || (p->mBitDepth == 20) || (p->mBitDepth == 24) || (p->mBitDepth == 32), return kALAC_ParamError ;) ;
+
+	// create a bit buffer structure pointing to our output buffer
+	BitBufferInit (&bitstream, theWriteBuffer, p->mMaxOutputBytes) ;
+
+	if (numChannels == 2)
+	{
+		// add 3-bit frame start tag ID_CPE = channel pair & 4-bit element instance tag = 0
+		BitBufferWrite (&bitstream, ID_CPE, 3) ;
+		BitBufferWrite (&bitstream, 0, 4) ;
+
+		// encode stereo input buffer
+		if (p->mFastMode == false)
+			status = EncodeStereo (p, &bitstream, theReadBuffer, 2, 0, numSamples) ;
+		else
+			status = EncodeStereoFast (p, &bitstream, theReadBuffer, 2, 0, numSamples) ;
+		RequireNoErr (status, goto Exit ;) ;
+	}
+	else if (numChannels == 1)
+	{
+		// add 3-bit frame start tag ID_SCE = mono channel & 4-bit element instance tag = 0
+		BitBufferWrite (&bitstream, ID_SCE, 3) ;
+		BitBufferWrite (&bitstream, 0, 4) ;
+
+		// encode mono input buffer
+		status = EncodeMono (p, &bitstream, theReadBuffer, 1, 0, numSamples) ;
+		RequireNoErr (status, goto Exit ;) ;
+	}
+	else
+	{
+		const int32_t *		inputBuffer ;
+		uint32_t			tag ;
+		uint32_t			channelIndex ;
+		uint8_t				stereoElementTag ;
+		uint8_t				monoElementTag ;
+		uint8_t				lfeElementTag ;
+
+		inputBuffer		= theReadBuffer ;
+
+		stereoElementTag	= 0 ;
+		monoElementTag		= 0 ;
+		lfeElementTag		= 0 ;
+
+		for (channelIndex = 0 ; channelIndex < numChannels ;)
+		{
+			tag = (sChannelMaps [numChannels - 1] & (0x7ul << (channelIndex * 3))) >> (channelIndex * 3) ;
+
+			BitBufferWrite (&bitstream, tag, 3) ;
+			switch (tag)
+			{
+				case ID_SCE:
+					// mono
+					BitBufferWrite (&bitstream, monoElementTag, 4) ;
+
+					status = EncodeMono (p, &bitstream, inputBuffer, numChannels, channelIndex, numSamples) ;
+
+					inputBuffer += 1 ;
+					channelIndex++ ;
+					monoElementTag++ ;
+					break ;
+
+				case ID_CPE:
+					// stereo
+					BitBufferWrite (&bitstream, stereoElementTag, 4) ;
+
+					status = EncodeStereo (p, &bitstream, inputBuffer, numChannels, channelIndex, numSamples) ;
+
+					inputBuffer += 2 ;
+					channelIndex += 2 ;
+					stereoElementTag++ ;
+					break ;
+
+				case ID_LFE:
+					// LFE channel (subwoofer)
+					BitBufferWrite (&bitstream, lfeElementTag, 4) ;
+
+					status = EncodeMono (p, &bitstream, inputBuffer, numChannels, channelIndex, numSamples) ;
+
+					inputBuffer += 1 ;
+					channelIndex++ ;
+					lfeElementTag++ ;
+					break ;
+
+				default:
+					printf ("That ain't right! (%u)\n", tag) ;
+					status = kALAC_ParamError ;
+					goto Exit ;
+			}
+
+			RequireNoErr (status, goto Exit ;) ;
+		}
+	}
+
+#if VERBOSE_DEBUG
+{
+	// if there is room left in the output buffer, add some random fill data to test decoder
+	int32_t			bitsLeft ;
+	int32_t			bytesLeft ;
+
+	bitsLeft = BitBufferGetPosition (&bitstream) - 3 ;	// - 3 for ID_END tag
+	bytesLeft = bitstream.byteSize - ((bitsLeft + 7) / 8) ;
+
+	if ((bytesLeft > 20) && ((bytesLeft & 0x4u) != 0))
+		AddFiller (&bitstream, bytesLeft) ;
+}
+#endif
+
+	// add 3-bit frame end tag: ID_END
+	BitBufferWrite (&bitstream, ID_END, 3) ;
+
+	// byte-align the output data
+	BitBufferByteAlign (&bitstream, true) ;
+
+	outputSize = BitBufferGetPosition (&bitstream) / 8 ;
+	//Assert (outputSize <= mMaxOutputBytes) ;
+
+
+	// all good, let iTunes know what happened and remember the total number of input sample frames
+	*ioNumBytes = outputSize ;
+	//mEncodedFrames		   	   += encodeMsg->numInputSamples ;
+
+	// gather encoding stats
+	p->mTotalBytesGenerated += outputSize ;
+	p->mMaxFrameBytes = MAX (p->mMaxFrameBytes, outputSize) ;
+
+	status = ALAC_noErr ;
+
+Exit:
+	return status ;
+}
+
+
+#if PRAGMA_MARK
+#pragma mark -
+#endif
+
+/*
+	GetConfig ()
+*/
+void
+GetConfig (ALAC_ENCODER *p, ALACSpecificConfig * config)
+{
+	config->frameLength			= Swap32NtoB (p->mFrameSize) ;
+	config->compatibleVersion	= (uint8_t) kALACCompatibleVersion ;
+	config->bitDepth			= (uint8_t) p->mBitDepth ;
+	config->pb					= (uint8_t) PB0 ;
+	config->kb					= (uint8_t) KB0 ;
+	config->mb					= (uint8_t) MB0 ;
+	config->numChannels			= (uint8_t) p->mNumChannels ;
+	config->maxRun				= Swap16NtoB ((uint16_t) MAX_RUN_DEFAULT) ;
+	config->maxFrameBytes		= Swap32NtoB (p->mMaxFrameBytes) ;
+	config->avgBitRate			= Swap32NtoB (p->mAvgBitRate) ;
+	config->sampleRate			= Swap32NtoB (p->mOutputSampleRate) ;
+}
+
+uint32_t
+alac_get_magic_cookie_size (uint32_t inNumChannels)
+{
+	if (inNumChannels > 2)
+	{
+		return sizeof (ALACSpecificConfig) + kChannelAtomSize + sizeof (ALACAudioChannelLayout) ;
+	}
+	else
+	{
+		return sizeof (ALACSpecificConfig) ;
+	}
+}
+
+void
+alac_get_magic_cookie (ALAC_ENCODER *p, void * outCookie, uint32_t * ioSize)
+{
+	ALACSpecificConfig theConfig = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } ;
+	ALACAudioChannelLayout theChannelLayout = { 0, 0, 0 } ;
+	uint8_t theChannelAtom [kChannelAtomSize] = { 0, 0, 0, 0, 'c', 'h', 'a', 'n', 0, 0, 0, 0 } ;
+	uint32_t theCookieSize = sizeof (ALACSpecificConfig) ;
+	uint8_t * theCookiePointer = (uint8_t *) outCookie ;
+
+	GetConfig (p, &theConfig) ;
+	if (theConfig.numChannels > 2)
+	{
+		theChannelLayout.mChannelLayoutTag = Swap32NtoB (ALACChannelLayoutTags [theConfig.numChannels - 1]) ;
+		theCookieSize += (sizeof (ALACAudioChannelLayout) + kChannelAtomSize) ;
+	}
+	if (*ioSize >= theCookieSize)
+	{
+		memcpy (theCookiePointer, &theConfig, sizeof (ALACSpecificConfig)) ;
+		theChannelAtom [3] = (sizeof (ALACAudioChannelLayout) + kChannelAtomSize) ;
+		if (theConfig.numChannels > 2)
+		{
+			theCookiePointer += sizeof (ALACSpecificConfig) ;
+			memcpy (theCookiePointer, theChannelAtom, kChannelAtomSize) ;
+			theCookiePointer += kChannelAtomSize ;
+			memcpy (theCookiePointer, &theChannelLayout, sizeof (ALACAudioChannelLayout)) ;
+		}
+		*ioSize = theCookieSize ;
+	}
+	else
+	{
+		*ioSize = 0 ; // no incomplete cookies
+	}
+}
+
+/*
+	alac_encoder_init ()
+	- initialize the encoder component with the current config
+*/
+int32_t
+alac_encoder_init (ALAC_ENCODER *p, uint32_t samplerate, uint32_t channels, uint32_t format_flags, uint32_t frameSize)
+{
+	int32_t			status ;
+
+	p->mFrameSize = (frameSize > 0 && frameSize <= ALAC_FRAME_LENGTH) ? frameSize : ALAC_FRAME_LENGTH ;
+
+	p->mOutputSampleRate = samplerate ;
+	p->mNumChannels = channels ;
+	switch (format_flags)
+	{
+		case 1:
+			p->mBitDepth = 16 ;
+			break ;
+		case 2:
+			p->mBitDepth = 20 ;
+			break ;
+		case 3:
+			p->mBitDepth = 24 ;
+			break ;
+		case 4:
+			p->mBitDepth = 32 ;
+			break ;
+		default:
+			break ;
+	}
+
+	// set up default encoding parameters and state
+	// - note: mFrameSize is set in the constructor or via alac_set_frame_size () which must be called before this routine
+	for (uint32_t indx = 0 ; indx < kALACMaxChannels ; indx++)
+		p->mLastMixRes [indx] = kDefaultMixRes ;
+
+	// the maximum output frame size can be no bigger than (samplesPerBlock * numChannels * ((10 + sampleSize)/8) + 1)
+	// but note that this can be bigger than the input size!
+	// - since we don't yet know what our input format will be, use our max allowed sample size in the calculation
+	p->mMaxOutputBytes = p->mFrameSize * p->mNumChannels * ((10 + kMaxSampleSize) / 8) + 1 ;
+
+	status = ALAC_noErr ;
+
+	// initialize coefs arrays once b/c retaining state across blocks actually improves the encode ratio
+	for (int32_t channel = 0 ; channel < (int32_t) p->mNumChannels ; channel++)
+	{
+		for (int32_t search = 0 ; search < kALACMaxSearches ; search++)
+		{
+			init_coefs (p->mCoefsU [channel][search], DENSHIFT_DEFAULT, kALACMaxCoefs) ;
+			init_coefs (p->mCoefsV [channel][search], DENSHIFT_DEFAULT, kALACMaxCoefs) ;
+		}
+	}
+
+	return status ;
+}
+
+/*
+	alac_get_source_format ()
+	- given the input format, return one of our supported formats
+*/
+void
+alac_get_source_format (ALAC_ENCODER *p, const AudioFormatDescription * source, AudioFormatDescription * output)
+{
+	(void) output ;
+	// default is 16-bit native endian
+	// - note: for float input we assume that's coming from one of our decoders (mp3, aac) so it only makes sense
+	//		   to encode to 16-bit since the source was lossy in the first place
+	// - note: if not a supported bit depth, find the closest supported bit depth to the input one
+	if ((source->mFormatID != kALACFormatLinearPCM) || ((source->mFormatFlags & kALACFormatFlagIsFloat) != 0) || (source->mBitsPerChannel <= 16))
+		p->mBitDepth = 16 ;
+	else if (source->mBitsPerChannel <= 20)
+		p->mBitDepth = 20 ;
+	else if (source->mBitsPerChannel <= 24)
+		p->mBitDepth = 24 ;
+	else
+		p->mBitDepth = 32 ;
+
+	// we support 16/20/24/32-bit integer data at any sample rate and our target number of channels
+	// and sample rate were specified when we were configured
+	/*
+	MakeUncompressedAudioFormat (mNumChannels, (float) mOutputSampleRate, mBitDepth, kAudioFormatFlagsNativeIntegerPacked, output) ;
+	*/
+}
+
+
+
+#if VERBOSE_DEBUG
+
+#if PRAGMA_MARK
+#pragma mark -
+#endif
+
+/*
+	AddFiller ()
+	- add fill and data stream elements to the bitstream to test the decoder
+*/
+static void AddFiller (BitBuffer * bits, int32_t numBytes)
+{
+	uint8_t		tag ;
+	int32_t		indx ;
+
+	// out of lameness, subtract 6 bytes to deal with header + alignment as required for fill/data elements
+	numBytes -= 6 ;
+	if (numBytes <= 0)
+		return ;
+
+	// randomly pick Fill or Data Stream Element based on numBytes requested
+	tag = (numBytes & 0x8) ? ID_FIL : ID_DSE ;
+
+	BitBufferWrite (bits, tag, 3) ;
+	if (tag == ID_FIL)
+	{
+		// can't write more than 269 bytes in a fill element
+		numBytes = (numBytes > 269) ? 269 : numBytes ;
+
+		// fill element = 4-bit size unless >= 15 then 4-bit size + 8-bit extension size
+		if (numBytes >= 15)
+		{
+			uint16_t			extensionSize ;
+
+			BitBufferWrite (bits, 15, 4) ;
+
+			// 8-bit extension count field is "extra + 1" which is weird but I didn't define the syntax
+			// - otherwise, there's no way to represent 15
+			// - for example, to really mean 15 bytes you must encode extensionSize = 1
+			// - why it's not like data stream elements I have no idea
+			extensionSize = (numBytes - 15) + 1 ;
+			//Assert (extensionSize <= 255) ;
+			BitBufferWrite (bits, extensionSize, 8) ;
+		}
+		else
+			BitBufferWrite (bits, numBytes, 4) ;
+
+		BitBufferWrite (bits, 0x10, 8) ;		// extension_type = FILL_DATA = b0001 or'ed with fill_nibble = b0000
+		for (indx = 0 ; indx < (numBytes - 1) ; indx++)
+			BitBufferWrite (bits, 0xa5, 8) ;	// fill_byte = b10100101 = 0xa5
+	}
+	else
+	{
+		// can't write more than 510 bytes in a data stream element
+		numBytes = (numBytes > 510) ? 510 : numBytes ;
+
+		BitBufferWrite (bits, 0, 4) ;			// element instance tag
+		BitBufferWrite (bits, 1, 1) ;			// byte-align flag = true
+
+		// data stream element = 8-bit size unless >= 255 then 8-bit size + 8-bit size
+		if (numBytes >= 255)
+		{
+			BitBufferWrite (bits, 255, 8) ;
+			BitBufferWrite (bits, numBytes - 255, 8) ;
+		}
+		else
+			BitBufferWrite (bits, numBytes, 8) ;
+
+		BitBufferByteAlign (bits, true) ;		// byte-align with zeros
+
+		for (indx = 0 ; indx < numBytes ; indx++)
+			BitBufferWrite (bits, 0x5a, 8) ;
+	}
+}
+
+#endif	/* VERBOSE_DEBUG */
diff --git a/src/ALAC/dp_dec.c b/src/ALAC/dp_dec.c
new file mode 100644
index 0000000..0d1c10d
--- /dev/null
+++ b/src/ALAC/dp_dec.c
@@ -0,0 +1,381 @@
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	 http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+	File:		dp_dec.c
+
+	Contains:	Dynamic Predictor decode routines
+
+	Copyright:	(c) 2001-2011 Apple, Inc.
+*/
+
+
+#include <string.h>
+
+#include "dplib.h"
+#include "shift.h"
+
+#if __GNUC__
+#define ALWAYS_INLINE		__attribute__ ((always_inline))
+#elif defined _MSC_VER
+#define ALWAYS_INLINE		__forceinline
+#else
+#define ALWAYS_INLINE
+#endif
+
+#define LOOP_ALIGN
+
+static inline int32_t ALWAYS_INLINE
+sign_of_int (int32_t i)
+{
+	int32_t negishift ;
+
+	negishift = ((uint32_t) - i) >> 31 ;
+	return negishift | (i >> 31) ;
+}
+
+void
+unpc_block (const int32_t * pc1, int32_t * out, int32_t num, int16_t * coefs, int32_t numactive, uint32_t chanbits, uint32_t denshift)
+{
+	register int16_t	a0, a1, a2, a3 ;
+	register int32_t	b0, b1, b2, b3 ;
+	int32_t					j, k, lim ;
+	int32_t				sum1, sg, sgn, top, dd ;
+	int32_t *			pout ;
+	int32_t				del, del0 ;
+	uint32_t			chanshift = 32 - chanbits ;
+	int32_t				denhalf = 1 << (denshift - 1) ;
+
+	out [0] = pc1 [0] ;
+	if (numactive == 0)
+	{
+		// just copy if numactive == 0 (but don't bother if in/out pointers the same)
+		if ((num > 1) && (pc1 != out))
+			memcpy (&out [1], &pc1 [1], (num - 1) * sizeof (int32_t)) ;
+		return ;
+	}
+	if (numactive == 31)
+	{
+		// short-circuit if numactive == 31
+		int32_t		prev ;
+
+		/*	this code is written such that the in/out buffers can be the same
+			to conserve buffer space on embedded devices like the iPod
+
+			(original code)
+			for (j = 1 ; j < num ; j++)
+				del = pc1 [j] + out [j-1] ;
+				out [j] = (del << chanshift) >> chanshift ;
+		*/
+		prev = out [0] ;
+		for (j = 1 ; j < num ; j++)
+		{
+			del = pc1 [j] + prev ;
+			prev = (del << chanshift) >> chanshift ;
+			out [j] = prev ;
+		}
+		return ;
+	}
+
+	for (j = 1 ; j <= numactive ; j++)
+	{
+		del = pc1 [j] + out [j-1] ;
+		out [j] = arith_shift_left (del, chanshift) >> chanshift ;
+	}
+
+	lim = numactive + 1 ;
+
+	if (numactive == 4)
+	{
+		// optimization for numactive == 4
+		register int16_t	ia0, ia1, ia2, ia3 ;
+		register int32_t	ib0, ib1, ib2, ib3 ;
+
+		ia0 = coefs [0] ;
+		ia1 = coefs [1] ;
+		ia2 = coefs [2] ;
+		ia3 = coefs [3] ;
+
+		for (j = lim ; j < num ; j++)
+		{
+			LOOP_ALIGN
+
+			top = out [j - lim] ;
+			pout = out + j - 1 ;
+
+			ib0 = top - pout [0] ;
+			ib1 = top - pout [-1] ;
+			ib2 = top - pout [-2] ;
+			ib3 = top - pout [-3] ;
+
+			sum1 = (denhalf - ia0 * ib0 - ia1 * ib1 - ia2 * ib2 - ia3 * ib3) >> denshift ;
+
+			del = pc1 [j] ;
+			del0 = del ;
+			sg = sign_of_int (del) ;
+			del += top + sum1 ;
+
+			out [j] = arith_shift_left (del, chanshift) >> chanshift ;
+
+			if (sg > 0)
+			{
+				sgn = sign_of_int (ib3) ;
+				ia3 -= sgn ;
+				del0 -= (4 - 3) * ((sgn * ib3) >> denshift) ;
+				if (del0 <= 0)
+					continue ;
+
+				sgn = sign_of_int (ib2) ;
+				ia2 -= sgn ;
+				del0 -= (4 - 2) * ((sgn * ib2) >> denshift) ;
+				if (del0 <= 0)
+					continue ;
+
+				sgn = sign_of_int (ib1) ;
+				ia1 -= sgn ;
+				del0 -= (4 - 1) * ((sgn * ib1) >> denshift) ;
+				if (del0 <= 0)
+					continue ;
+
+				ia0 -= sign_of_int (ib0) ;
+			}
+			else if (sg < 0)
+			{
+				// note: to avoid unnecessary negations, we flip the value of "sgn"
+				sgn = -sign_of_int (ib3) ;
+				ia3 -= sgn ;
+				del0 -= (4 - 3) * ((sgn * ib3) >> denshift) ;
+				if (del0 >= 0)
+					continue ;
+
+				sgn = -sign_of_int (ib2) ;
+				ia2 -= sgn ;
+				del0 -= (4 - 2) * ((sgn * ib2) >> denshift) ;
+				if (del0 >= 0)
+					continue ;
+
+				sgn = -sign_of_int (ib1) ;
+				ia1 -= sgn ;
+				del0 -= (4 - 1) * ((sgn * ib1) >> denshift) ;
+				if (del0 >= 0)
+					continue ;
+
+				ia0 += sign_of_int (ib0) ;
+			}
+		}
+
+		coefs [0] = ia0 ;
+		coefs [1] = ia1 ;
+		coefs [2] = ia2 ;
+		coefs [3] = ia3 ;
+	}
+	else if (numactive == 8)
+	{
+		register int16_t	a4, a5, a6, a7 ;
+		register int32_t	b4, b5, b6, b7 ;
+
+		// optimization for numactive == 8
+		a0 = coefs [0] ;
+		a1 = coefs [1] ;
+		a2 = coefs [2] ;
+		a3 = coefs [3] ;
+		a4 = coefs [4] ;
+		a5 = coefs [5] ;
+		a6 = coefs [6] ;
+		a7 = coefs [7] ;
+
+		for (j = lim ; j < num ; j++)
+		{
+			LOOP_ALIGN
+
+			top = out [j - lim] ;
+			pout = out + j - 1 ;
+
+			b0 = top - (*pout--) ;
+			b1 = top - (*pout--) ;
+			b2 = top - (*pout--) ;
+			b3 = top - (*pout--) ;
+			b4 = top - (*pout--) ;
+			b5 = top - (*pout--) ;
+			b6 = top - (*pout--) ;
+			b7 = top - (*pout) ;
+			pout += 8 ;
+
+			sum1 = (denhalf - a0 * b0 - a1 * b1 - a2 * b2 - a3 * b3
+					- a4 * b4 - a5 * b5 - a6 * b6 - a7 * b7) >> denshift ;
+
+			del = pc1 [j] ;
+			del0 = del ;
+			sg = sign_of_int (del) ;
+			del += top + sum1 ;
+
+			out [j] = arith_shift_left (del, chanshift) >> chanshift ;
+
+			if (sg > 0)
+			{
+				sgn = sign_of_int (b7) ;
+				a7 -= sgn ;
+				del0 -= 1 * ((sgn * b7) >> denshift) ;
+				if (del0 <= 0)
+					continue ;
+
+				sgn = sign_of_int (b6) ;
+				a6 -= sgn ;
+				del0 -= 2 * ((sgn * b6) >> denshift) ;
+				if (del0 <= 0)
+					continue ;
+
+				sgn = sign_of_int (b5) ;
+				a5 -= sgn ;
+				del0 -= 3 * ((sgn * b5) >> denshift) ;
+				if (del0 <= 0)
+					continue ;
+
+				sgn = sign_of_int (b4) ;
+				a4 -= sgn ;
+				del0 -= 4 * ((sgn * b4) >> denshift) ;
+				if (del0 <= 0)
+					continue ;
+
+				sgn = sign_of_int (b3) ;
+				a3 -= sgn ;
+				del0 -= 5 * ((sgn * b3) >> denshift) ;
+				if (del0 <= 0)
+					continue ;
+
+				sgn = sign_of_int (b2) ;
+				a2 -= sgn ;
+				del0 -= 6 * ((sgn * b2) >> denshift) ;
+				if (del0 <= 0)
+					continue ;
+
+				sgn = sign_of_int (b1) ;
+				a1 -= sgn ;
+				del0 -= 7 * ((sgn * b1) >> denshift) ;
+				if (del0 <= 0)
+					continue ;
+
+				a0 -= sign_of_int (b0) ;
+			}
+			else if (sg < 0)
+			{
+				// note: to avoid unnecessary negations, we flip the value of "sgn"
+				sgn = -sign_of_int (b7) ;
+				a7 -= sgn ;
+				del0 -= 1 * ((sgn * b7) >> denshift) ;
+				if (del0 >= 0)
+					continue ;
+
+				sgn = -sign_of_int (b6) ;
+				a6 -= sgn ;
+				del0 -= 2 * ((sgn * b6) >> denshift) ;
+				if (del0 >= 0)
+					continue ;
+
+				sgn = -sign_of_int (b5) ;
+				a5 -= sgn ;
+				del0 -= 3 * ((sgn * b5) >> denshift) ;
+				if (del0 >= 0)
+					continue ;
+
+				sgn = -sign_of_int (b4) ;
+				a4 -= sgn ;
+				del0 -= 4 * ((sgn * b4) >> denshift) ;
+				if (del0 >= 0)
+					continue ;
+
+				sgn = -sign_of_int (b3) ;
+				a3 -= sgn ;
+				del0 -= 5 * ((sgn * b3) >> denshift) ;
+				if (del0 >= 0)
+					continue ;
+
+				sgn = -sign_of_int (b2) ;
+				a2 -= sgn ;
+				del0 -= 6 * ((sgn * b2) >> denshift) ;
+				if (del0 >= 0)
+					continue ;
+
+				sgn = -sign_of_int (b1) ;
+				a1 -= sgn ;
+				del0 -= 7 * ((sgn * b1) >> denshift) ;
+				if (del0 >= 0)
+					continue ;
+
+				a0 += sign_of_int (b0) ;
+			}
+		}
+
+		coefs [0] = a0 ;
+		coefs [1] = a1 ;
+		coefs [2] = a2 ;
+		coefs [3] = a3 ;
+		coefs [4] = a4 ;
+		coefs [5] = a5 ;
+		coefs [6] = a6 ;
+		coefs [7] = a7 ;
+	}
+	else
+	{
+		// general case
+		for (j = lim ; j < num ; j++)
+		{
+			LOOP_ALIGN
+
+			sum1 = 0 ;
+			pout = out + j - 1 ;
+			top = out [j-lim] ;
+
+			for (k = 0 ; k < numactive ; k++)
+				sum1 += coefs [k] * (pout [-k] - top) ;
+
+			del = pc1 [j] ;
+			del0 = del ;
+			sg = sign_of_int (del) ;
+			del += top + ((sum1 + denhalf) >> denshift) ;
+			out [j] = (del << chanshift) >> chanshift ;
+
+			if (sg > 0)
+			{
+				for (k = (numactive - 1) ; k >= 0 ; k--)
+				{
+					dd = top - pout [-k] ;
+					sgn = sign_of_int (dd) ;
+					coefs [k] -= sgn ;
+					del0 -= (numactive - k) * ((sgn * dd) >> denshift) ;
+					if (del0 <= 0)
+						break ;
+				}
+			}
+			else if (sg < 0)
+			{
+				for (k = (numactive - 1) ; k >= 0 ; k--)
+				{
+					dd = top - pout [-k] ;
+					sgn = sign_of_int (dd) ;
+					coefs [k] += sgn ;
+					del0 -= (numactive - k) * ((-sgn * dd) >> denshift) ;
+					if (del0 >= 0)
+						break ;
+				}
+			}
+		}
+	}
+}
diff --git a/src/ALAC/dp_enc.c b/src/ALAC/dp_enc.c
new file mode 100644
index 0000000..01db8d4
--- /dev/null
+++ b/src/ALAC/dp_enc.c
@@ -0,0 +1,387 @@
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	 http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+	File:		dp_enc.c
+
+	Contains:	Dynamic Predictor encode routines
+
+	Copyright:	(c) 2001-2011 Apple, Inc.
+*/
+
+#include <string.h>
+
+#include "dplib.h"
+#include "shift.h"
+
+#if __GNUC__
+#define ALWAYS_INLINE		__attribute__ ((always_inline))
+#elif defined _MSC_VER
+#define ALWAYS_INLINE		__forceinline
+#else
+#define ALWAYS_INLINE
+#endif
+
+#define LOOP_ALIGN
+
+void
+init_coefs (int16_t * coefs, uint32_t denshift, int32_t numPairs)
+{
+	int32_t		k ;
+	int32_t		den = 1 << denshift ;
+
+	coefs [0] = (AINIT * den) >> 4 ;
+	coefs [1] = (BINIT * den) >> 4 ;
+	coefs [2] = (CINIT * den) >> 4 ;
+	for (k = 3 ; k < numPairs ; k++)
+		coefs [k] = 0 ;
+}
+
+void
+copy_coefs (const int16_t * srcCoefs, int16_t * dstCoefs, int32_t numPairs)
+{
+	int32_t k ;
+
+	for (k = 0 ; k < numPairs ; k++)
+		dstCoefs [k] = srcCoefs [k] ;
+}
+
+static inline int32_t ALWAYS_INLINE sign_of_int (int32_t i)
+{
+	int32_t negishift ;
+
+	negishift = ((uint32_t) - i) >> 31 ;
+	return negishift | (i >> 31) ;
+}
+
+void
+pc_block (int32_t * in, int32_t * pc1, int32_t num, int16_t * coefs, int32_t numactive, uint32_t chanbits, uint32_t denshift)
+{
+	register int16_t	a0, a1, a2, a3 ;
+	register int32_t	b0, b1, b2, b3 ;
+	int32_t					j, k, lim ;
+	int32_t *			pin ;
+	int32_t				sum1, dd ;
+	int32_t				sg, sgn ;
+	int32_t				top ;
+	int32_t				del, del0 ;
+	uint32_t			chanshift = 32 - chanbits ;
+	int32_t				denhalf = 1 << (denshift - 1) ;
+
+	pc1 [0] = in [0] ;
+	if (numactive == 0)
+	{
+		// just copy if numactive == 0 (but don't bother if in/out pointers the same)
+		if ((num > 1) && (in != pc1))
+			memcpy (&pc1 [1], &in [1], (num - 1) * sizeof (int32_t)) ;
+		return ;
+	}
+	if (numactive == 31)
+	{
+		// short-circuit if numactive == 31
+		for (j = 1 ; j < num ; j++)
+		{
+			del = in [j] - in [j-1] ;
+			pc1 [j] = (del << chanshift) >> chanshift ;
+		}
+		return ;
+	}
+
+	for (j = 1 ; j <= numactive ; j++)
+	{
+		del = in [j] - in [j-1] ;
+		pc1 [j] = arith_shift_left (del, chanshift) >> chanshift ;
+	}
+
+	lim = numactive + 1 ;
+
+	if (numactive == 4)
+	{
+		// optimization for numactive == 4
+		a0 = coefs [0] ;
+		a1 = coefs [1] ;
+		a2 = coefs [2] ;
+		a3 = coefs [3] ;
+
+		for (j = lim ; j < num ; j++)
+		{
+			LOOP_ALIGN
+
+			top = in [j - lim] ;
+			pin = in + j - 1 ;
+
+			b0 = top - pin [0] ;
+			b1 = top - pin [-1] ;
+			b2 = top - pin [-2] ;
+			b3 = top - pin [-3] ;
+
+			sum1 = (denhalf - a0 * b0 - a1 * b1 - a2 * b2 - a3 * b3) >> denshift ;
+
+			del = in [j] - top - sum1 ;
+			del = arith_shift_left (del, chanshift) >> chanshift ;
+			pc1 [j] = del ;
+			del0 = del ;
+
+			sg = sign_of_int (del) ;
+			if (sg > 0)
+			{
+				sgn = sign_of_int (b3) ;
+				a3 -= sgn ;
+				del0 -= (4 - 3) * ((sgn * b3) >> denshift) ;
+				if (del0 <= 0)
+					continue ;
+
+				sgn = sign_of_int (b2) ;
+				a2 -= sgn ;
+				del0 -= (4 - 2) * ((sgn * b2) >> denshift) ;
+				if (del0 <= 0)
+					continue ;
+
+				sgn = sign_of_int (b1) ;
+				a1 -= sgn ;
+				del0 -= (4 - 1) * ((sgn * b1) >> denshift) ;
+				if (del0 <= 0)
+					continue ;
+
+				a0 -= sign_of_int (b0) ;
+			}
+			else if (sg < 0)
+			{
+				// note: to avoid unnecessary negations, we flip the value of "sgn"
+				sgn = -sign_of_int (b3) ;
+				a3 -= sgn ;
+				del0 -= (4 - 3) * ((sgn * b3) >> denshift) ;
+				if (del0 >= 0)
+					continue ;
+
+				sgn = -sign_of_int (b2) ;
+				a2 -= sgn ;
+				del0 -= (4 - 2) * ((sgn * b2) >> denshift) ;
+				if (del0 >= 0)
+					continue ;
+
+				sgn = -sign_of_int (b1) ;
+				a1 -= sgn ;
+				del0 -= (4 - 1) * ((sgn * b1) >> denshift) ;
+				if (del0 >= 0)
+					continue ;
+
+				a0 += sign_of_int (b0) ;
+			}
+		}
+
+		coefs [0] = a0 ;
+		coefs [1] = a1 ;
+		coefs [2] = a2 ;
+		coefs [3] = a3 ;
+	}
+	else if (numactive == 8)
+	{
+		// optimization for numactive == 8
+		register int16_t	a4, a5, a6, a7 ;
+		register int32_t	b4, b5, b6, b7 ;
+
+		a0 = coefs [0] ;
+		a1 = coefs [1] ;
+		a2 = coefs [2] ;
+		a3 = coefs [3] ;
+		a4 = coefs [4] ;
+		a5 = coefs [5] ;
+		a6 = coefs [6] ;
+		a7 = coefs [7] ;
+
+		for (j = lim ; j < num ; j++)
+		{
+			LOOP_ALIGN
+
+			top = in [j - lim] ;
+			pin = in + j - 1 ;
+
+			b0 = top - (*pin--) ;
+			b1 = top - (*pin--) ;
+			b2 = top - (*pin--) ;
+			b3 = top - (*pin--) ;
+			b4 = top - (*pin--) ;
+			b5 = top - (*pin--) ;
+			b6 = top - (*pin--) ;
+			b7 = top - (*pin) ;
+			pin += 8 ;
+
+			sum1 = (denhalf - a0 * b0 - a1 * b1 - a2 * b2 - a3 * b3
+					- a4 * b4 - a5 * b5 - a6 * b6 - a7 * b7) >> denshift ;
+
+			del = in [j] - top - sum1 ;
+			del = arith_shift_left (del, chanshift) >> chanshift ;
+			pc1 [j] = del ;
+			del0 = del ;
+
+			sg = sign_of_int (del) ;
+			if (sg > 0)
+			{
+				sgn = sign_of_int (b7) ;
+				a7 -= sgn ;
+				del0 -= 1 * ((sgn * b7) >> denshift) ;
+				if (del0 <= 0)
+					continue ;
+
+				sgn = sign_of_int (b6) ;
+				a6 -= sgn ;
+				del0 -= 2 * ((sgn * b6) >> denshift) ;
+				if (del0 <= 0)
+					continue ;
+
+				sgn = sign_of_int (b5) ;
+				a5 -= sgn ;
+				del0 -= 3 * ((sgn * b5) >> denshift) ;
+				if (del0 <= 0)
+					continue ;
+
+				sgn = sign_of_int (b4) ;
+				a4 -= sgn ;
+				del0 -= 4 * ((sgn * b4) >> denshift) ;
+				if (del0 <= 0)
+					continue ;
+
+				sgn = sign_of_int (b3) ;
+				a3 -= sgn ;
+				del0 -= 5 * ((sgn * b3) >> denshift) ;
+				if (del0 <= 0)
+					continue ;
+
+				sgn = sign_of_int (b2) ;
+				a2 -= sgn ;
+				del0 -= 6 * ((sgn * b2) >> denshift) ;
+				if (del0 <= 0)
+					continue ;
+
+				sgn = sign_of_int (b1) ;
+				a1 -= sgn ;
+				del0 -= 7 * ((sgn * b1) >> denshift) ;
+				if (del0 <= 0)
+					continue ;
+
+				a0 -= sign_of_int (b0) ;
+			}
+			else if (sg < 0)
+			{
+				// note: to avoid unnecessary negations, we flip the value of "sgn"
+				sgn = -sign_of_int (b7) ;
+				a7 -= sgn ;
+				del0 -= 1 * ((sgn * b7) >> denshift) ;
+				if (del0 >= 0)
+					continue ;
+
+				sgn = -sign_of_int (b6) ;
+				a6 -= sgn ;
+				del0 -= 2 * ((sgn * b6) >> denshift) ;
+				if (del0 >= 0)
+					continue ;
+
+				sgn = -sign_of_int (b5) ;
+				a5 -= sgn ;
+				del0 -= 3 * ((sgn * b5) >> denshift) ;
+				if (del0 >= 0)
+					continue ;
+
+				sgn = -sign_of_int (b4) ;
+				a4 -= sgn ;
+				del0 -= 4 * ((sgn * b4) >> denshift) ;
+				if (del0 >= 0)
+					continue ;
+
+				sgn = -sign_of_int (b3) ;
+				a3 -= sgn ;
+				del0 -= 5 * ((sgn * b3) >> denshift) ;
+				if (del0 >= 0)
+					continue ;
+
+				sgn = -sign_of_int (b2) ;
+				a2 -= sgn ;
+				del0 -= 6 * ((sgn * b2) >> denshift) ;
+				if (del0 >= 0)
+					continue ;
+
+				sgn = -sign_of_int (b1) ;
+				a1 -= sgn ;
+				del0 -= 7 * ((sgn * b1) >> denshift) ;
+				if (del0 >= 0)
+					continue ;
+
+				a0 += sign_of_int (b0) ;
+			}
+		}
+
+		coefs [0] = a0 ;
+		coefs [1] = a1 ;
+		coefs [2] = a2 ;
+		coefs [3] = a3 ;
+		coefs [4] = a4 ;
+		coefs [5] = a5 ;
+		coefs [6] = a6 ;
+		coefs [7] = a7 ;
+	}
+	else
+	{
+//pc_block_general:
+		// general case
+		for (j = lim ; j < num ; j++)
+		{
+			LOOP_ALIGN
+
+			top = in [j - lim] ;
+			pin = in + j - 1 ;
+
+			sum1 = 0 ;
+			for (k = 0 ; k < numactive ; k++)
+				sum1 -= coefs [k] * (top - pin [-k]) ;
+
+			del = in [j] - top - ((sum1 + denhalf) >> denshift) ;
+			del = (del << chanshift) >> chanshift ;
+			pc1 [j] = del ;
+			del0 = del ;
+
+			sg = sign_of_int (del) ;
+			if (sg > 0)
+			{
+				for (k = (numactive - 1) ; k >= 0 ; k--)
+				{
+					dd = top - pin [-k] ;
+					sgn = sign_of_int (dd) ;
+					coefs [k] -= sgn ;
+					del0 -= (numactive - k) * ((sgn * dd) >> denshift) ;
+					if (del0 <= 0)
+						break ;
+				}
+			}
+			else if (sg < 0)
+			{
+				for (k = (numactive - 1) ; k >= 0 ; k--)
+				{
+					dd = top - pin [-k] ;
+					sgn = sign_of_int (dd) ;
+					coefs [k] += sgn ;
+					del0 -= (numactive - k) * ((-sgn * dd) >> denshift) ;
+					if (del0 >= 0)
+						break ;
+				}
+			}
+		}
+	}
+}
diff --git a/src/ALAC/dplib.h b/src/ALAC/dplib.h
new file mode 100644
index 0000000..43ae721
--- /dev/null
+++ b/src/ALAC/dplib.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+	File:		dplib.h
+
+	Contains:	Dynamic Predictor routines
+
+	Copyright:	Copyright (C) 2001-2011 Apple, Inc.
+*/
+
+#ifndef __DPLIB_H__
+#define __DPLIB_H__
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// defines
+
+#define DENSHIFT_MAX	15
+#define DENSHIFT_DEFAULT 9
+#define AINIT 38
+#define BINIT (-29)
+#define CINIT (-2)
+#define NUMCOEPAIRS 16
+
+// prototypes
+
+void init_coefs (int16_t * coefs, uint32_t denshift, int32_t numPairs) ;
+void copy_coefs (const int16_t * srcCoefs, int16_t * dstCoefs, int32_t numPairs) ;
+
+// NOTE: these routines read at least "numactive" samples so the i/o buffers must be at least that big
+
+void pc_block (int32_t * in, int32_t * pc, int32_t num, int16_t * coefs, int32_t numactive, uint32_t chanbits, uint32_t denshift) ;
+void unpc_block (const int32_t * pc, int32_t * out, int32_t num, int16_t * coefs, int32_t numactive, uint32_t chanbits, uint32_t denshift) ;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* __DPLIB_H__ */
diff --git a/src/ALAC/matrix_dec.c b/src/ALAC/matrix_dec.c
new file mode 100644
index 0000000..6d0b401
--- /dev/null
+++ b/src/ALAC/matrix_dec.c
@@ -0,0 +1,330 @@
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+	File:		matrix_dec.c
+
+	Contains:	ALAC mixing/matrixing decode routines.
+
+	Copyright:	(c) 2004-2011 Apple, Inc.
+*/
+
+#include "matrixlib.h"
+#include "ALACAudioTypes.h"
+#include "shift.h"
+
+// up to 24-bit "offset" macros for the individual bytes of a 20/24-bit word
+#if TARGET_RT_BIG_ENDIAN
+	#define LBYTE	2
+	#define MBYTE	1
+	#define HBYTE	0
+#else
+	#define LBYTE	0
+	#define MBYTE	1
+	#define HBYTE	2
+#endif
+
+/*
+    There is no plain middle-side option ; instead there are various mixing
+    modes including middle-side, each lossless, as embodied in the mix ()
+    and unmix () functions.  These functions exploit a generalized middle-side
+    transformation:
+
+    u := [(rL + (m-r)R)/m] ;
+    v := L - R ;
+
+    where [ ] denotes integer floor.  The (lossless) inverse is
+
+    L = u + v - [rV/m] ;
+    R = L - v ;
+*/
+
+// 16-bit routines
+
+void
+unmix16 (const int32_t * u, int32_t * v, int32_t * out, uint32_t stride, int32_t numSamples, int32_t mixbits, int32_t mixres)
+{
+	int32_t 	j ;
+
+	if (mixres != 0)
+	{
+		/* matrixed stereo */
+		for (j = 0 ; j < numSamples ; j++)
+		{
+			int32_t		l, r ;
+
+			l = u [j] + v [j] - ((mixres * v [j]) >> mixbits) ;
+			r = l - v [j] ;
+
+			out [0] = arith_shift_left (l, 16) ;
+			out [1] = arith_shift_left (r, 16) ;
+			out += stride ;
+		}
+	}
+	else
+	{
+		/* Conventional separated stereo. */
+		for (j = 0 ; j < numSamples ; j++)
+		{
+			out [0] = u [j] << 16 ;
+			out [1] = v [j] << 16 ;
+			out += stride ;
+		}
+	}
+}
+
+// 20-bit routines
+// - the 20 bits of data are left-justified in 3 bytes of storage but right-aligned for input/output predictor buffers
+
+void
+unmix20 (const int32_t * u, int32_t * v, int32_t * out, uint32_t stride, int32_t numSamples, int32_t mixbits, int32_t mixres)
+{
+	int32_t 	j ;
+
+	if (mixres != 0)
+	{
+		/* matrixed stereo */
+		for (j = 0 ; j < numSamples ; j++)
+		{
+			int32_t		l, r ;
+
+			l = u [j] + v [j] - ((mixres * v [j]) >> mixbits) ;
+			r = l - v [j] ;
+
+			out [0] = arith_shift_left (l, 12) ;
+			out [1] = arith_shift_left (r, 12) ;
+			out += stride ;
+		}
+	}
+	else
+	{
+		/* Conventional separated stereo. */
+		for (j = 0 ; j < numSamples ; j++)
+		{
+			out [0] = arith_shift_left (u [j], 12) ;
+			out [1] = arith_shift_left (v [j], 12) ;
+			out += stride ;
+		}
+	}
+}
+
+// 24-bit routines
+// - the 24 bits of data are right-justified in the input/output predictor buffers
+
+void
+unmix24 (const int32_t * u, int32_t * v, int32_t * out, uint32_t stride, int32_t numSamples,
+				int32_t mixbits, int32_t mixres, uint16_t * shiftUV, int32_t bytesShifted)
+{
+	int32_t		shift = bytesShifted * 8 ;
+	int32_t		l, r ;
+	int32_t 		j, k ;
+
+	if (mixres != 0)
+	{
+		/* matrixed stereo */
+		if (bytesShifted != 0)
+		{
+			for (j = 0, k = 0 ; j < numSamples ; j++, k += 2)
+			{
+				l = u [j] + v [j] - ((mixres * v [j]) >> mixbits) ;
+				r = l - v [j] ;
+
+				l = arith_shift_left (l, shift) | (uint32_t) shiftUV [k + 0] ;
+				r = arith_shift_left (r, shift) | (uint32_t) shiftUV [k + 1] ;
+
+				out [0] = arith_shift_left (l, 8) ;
+				out [1] = arith_shift_left (r, 8) ;
+				out += stride ;
+			}
+		}
+		else
+		{
+			for (j = 0 ; j < numSamples ; j++)
+			{
+				l = u [j] + v [j] - ((mixres * v [j]) >> mixbits) ;
+				r = l - v [j] ;
+
+				out [0] = l << 8 ;
+				out [1] = r << 8 ;
+				out += stride ;
+			}
+		}
+	}
+	else
+	{
+		/* Conventional separated stereo. */
+		if (bytesShifted != 0)
+		{
+			for (j = 0, k = 0 ; j < numSamples ; j++, k += 2)
+			{
+				l = u [j] ;
+				r = v [j] ;
+
+				l = (l << shift) | (uint32_t) shiftUV [k + 0] ;
+				r = (r << shift) | (uint32_t) shiftUV [k + 1] ;
+
+				out [0] = l << 8 ;
+				out [1] = r << 8 ;
+				out += stride ;
+			}
+		}
+		else
+		{
+			for (j = 0 ; j < numSamples ; j++)
+			{
+				out [0] = u [j] << 8 ;
+				out [1] = v [j] << 8 ;
+				out += stride ;
+			}
+		}
+	}
+}
+
+// 32-bit routines
+// - note that these really expect the internal data width to be < 32 but the arrays are 32-bit
+// - otherwise, the calculations might overflow into the 33rd bit and be lost
+// - therefore, these routines deal with the specified "unused lower" bytes in the "shift" buffers
+
+void
+unmix32 (const int32_t * u, int32_t * v, int32_t * out, uint32_t stride, int32_t numSamples,
+				int32_t mixbits, int32_t mixres, uint16_t * shiftUV, int32_t bytesShifted)
+{
+	int32_t		shift = bytesShifted * 8 ;
+	int32_t		l, r ;
+	int32_t 	j, k ;
+
+	if (mixres != 0)
+	{
+		//Assert (bytesShifted != 0) ;
+
+		/* matrixed stereo with shift */
+		for (j = 0, k = 0 ; j < numSamples ; j++, k += 2)
+		{
+			int32_t		lt, rt ;
+
+			lt = u [j] ;
+			rt = v [j] ;
+
+			l = lt + rt - ((mixres * rt) >> mixbits) ;
+			r = l - rt ;
+
+			out [0] = arith_shift_left (l, shift) | (uint32_t) shiftUV [k + 0] ;
+			out [1] = arith_shift_left (r, shift) | (uint32_t) shiftUV [k + 1] ;
+			out += stride ;
+		}
+	}
+	else
+	{
+		if (bytesShifted == 0)
+		{
+			/* interleaving w/o shift */
+			for (j = 0 ; j < numSamples ; j++)
+			{
+				out [0] = u [j] ;
+				out [1] = v [j] ;
+				out += stride ;
+			}
+		}
+		else
+		{
+			/* interleaving with shift */
+			for (j = 0, k = 0 ; j < numSamples ; j++, k += 2)
+			{
+				out [0] = (u [j] << shift) | (uint32_t) shiftUV [k + 0] ;
+				out [1] = (v [j] << shift) | (uint32_t) shiftUV [k + 1] ;
+				out += stride ;
+			}
+		}
+	}
+}
+
+// 20/24-bit <-> 32-bit helper routines (not really matrixing but convenient to put here)
+
+void
+copyPredictorTo24 (const int32_t * in, int32_t * out, uint32_t stride, int32_t numSamples)
+{
+	int32_t		j ;
+
+	for (j = 0 ; j < numSamples ; j++)
+	{
+		out [0] = in [j] << 8 ;
+		out += stride ;
+	}
+}
+
+void
+copyPredictorTo24Shift (const int32_t * in, uint16_t * shift, int32_t * out, uint32_t stride, int32_t numSamples, int32_t bytesShifted)
+{
+	int32_t		shiftVal = bytesShifted * 8 ;
+	int32_t		j ;
+
+	//Assert (bytesShifted != 0) ;
+
+	for (j = 0 ; j < numSamples ; j++)
+	{
+		int32_t		val = in [j] ;
+
+		val = arith_shift_left (val, shiftVal) | (uint32_t) shift [j] ;
+		out [0] = arith_shift_left (val, 8) ;
+		out += stride ;
+	}
+}
+
+void
+copyPredictorTo20 (const int32_t * in, int32_t * out, uint32_t stride, int32_t numSamples)
+{
+	int32_t		j ;
+
+	// 32-bit predictor values are right-aligned but 20-bit output values should be left-aligned
+	// in the 24-bit output buffer
+	for (j = 0 ; j < numSamples ; j++)
+	{
+		out [0] = arith_shift_left (in [j], 12) ;
+		out += stride ;
+	}
+}
+
+void
+copyPredictorTo32 (const int32_t * in, int32_t * out, uint32_t stride, int32_t numSamples)
+{
+	int32_t			i, j ;
+
+	// this is only a subroutine to abstract the "iPod can only output 16-bit data" problem
+	for (i = 0, j = 0 ; i < numSamples ; i++, j += stride)
+		out [j] = arith_shift_left (in [i], 8) ;
+}
+
+void
+copyPredictorTo32Shift (const int32_t * in, uint16_t * shift, int32_t * out, uint32_t stride, int32_t numSamples, int32_t bytesShifted)
+{
+	int32_t *		op = out ;
+	uint32_t		shiftVal = bytesShifted * 8 ;
+	int32_t			j ;
+
+	//Assert (bytesShifted != 0) ;
+
+	// this is only a subroutine to abstract the "iPod can only output 16-bit data" problem
+	for (j = 0 ; j < numSamples ; j++)
+	{
+		op [0] = arith_shift_left (in [j], shiftVal) | (uint32_t) shift [j] ;
+		op += stride ;
+	}
+}
diff --git a/src/ALAC/matrix_enc.c b/src/ALAC/matrix_enc.c
new file mode 100644
index 0000000..b50f83b
--- /dev/null
+++ b/src/ALAC/matrix_enc.c
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+	File:		matrix_enc.c
+
+	Contains:	ALAC mixing/matrixing encode routines.
+
+	Copyright:	(c) 2004-2011 Apple, Inc.
+*/
+
+#include "matrixlib.h"
+#include "ALACAudioTypes.h"
+
+/*
+    There is no plain middle-side option ; instead there are various mixing
+    modes including middle-side, each lossless, as embodied in the mix ()
+    and unmix () functions.  These functions exploit a generalized middle-side
+    transformation:
+
+    u := [(rL + (m-r)R)/m] ;
+    v := L - R ;
+
+    where [ ] denotes integer floor.  The (lossless) inverse is
+
+    L = u + v - [rV/m] ;
+    R = L - v ;
+*/
+
+// 16-bit routines
+
+void
+mix16 (const int32_t * in, uint32_t stride, int32_t * u, int32_t * v, int32_t numSamples, int32_t mixbits, int32_t mixres)
+{
+	int32_t		j ;
+
+	if (mixres != 0)
+	{
+		int32_t		mod = 1 << mixbits ;
+		int32_t		m2 ;
+
+		/* matrixed stereo */
+		m2 = mod - mixres ;
+		for (j = 0 ; j < numSamples ; j++)
+		{
+			int32_t		l, r ;
+
+			l = in [0] >> 16 ;
+			r = in [1] >> 16 ;
+			in += stride ;
+			u [j] = (mixres * l + m2 * r) >> mixbits ;
+			v [j] = l - r ;
+		}
+	}
+	else
+	{
+		/* Conventional separated stereo. */
+		for (j = 0 ; j < numSamples ; j++)
+		{
+			u [j] = in [0] >> 16 ;
+			v [j] = in [1] >> 16 ;
+			in += stride ;
+		}
+	}
+}
+
+// 20-bit routines
+// - the 20 bits of data are left-justified in 3 bytes of storage but right-aligned for input/output predictor buffers
+
+void
+mix20 (const int32_t * in, uint32_t stride, int32_t * u, int32_t * v, int32_t numSamples, int32_t mixbits, int32_t mixres)
+{
+	int32_t		l, r ;
+	int32_t		j ;
+
+	if (mixres != 0)
+	{
+		/* matrixed stereo */
+		int32_t		mod = 1 << mixbits ;
+		int32_t		m2 = mod - mixres ;
+
+		for (j = 0 ; j < numSamples ; j++)
+		{
+			l = in [0] >> 12 ;
+			r = in [1] >> 12 ;
+			in += stride ;
+
+			u [j] = (mixres * l + m2 * r) >> mixbits ;
+			v [j] = l - r ;
+		}
+	}
+	else
+	{
+		/* Conventional separated stereo. */
+		for (j = 0 ; j < numSamples ; j++)
+		{
+			u [j] = in [0] >> 12 ;
+			v [j] = in [1] >> 12 ;
+			in += stride ;
+		}
+	}
+}
+
+// 24-bit routines
+// - the 24 bits of data are right-justified in the input/output predictor buffers
+
+void
+mix24 (const int32_t * in, uint32_t stride, int32_t * u, int32_t * v, int32_t numSamples,
+			int32_t mixbits, int32_t mixres, uint16_t * shiftUV, int32_t bytesShifted)
+{
+	int32_t		l, r ;
+	int32_t		shift = bytesShifted * 8 ;
+	uint32_t	mask = (1ul << shift) - 1 ;
+	int32_t		j, k ;
+
+	if (mixres != 0)
+	{
+		/* matrixed stereo */
+		int32_t		mod = 1 << mixbits ;
+		int32_t		m2 = mod - mixres ;
+
+		if (bytesShifted != 0)
+		{
+			for (j = 0, k = 0 ; j < numSamples ; j++, k += 2)
+			{
+				l = in [0] >> 8 ;
+				r = in [1] >> 8 ;
+				in += stride ;
+
+				shiftUV [k + 0] = (uint16_t) (l & mask) ;
+				shiftUV [k + 1] = (uint16_t) (r & mask) ;
+
+				l >>= shift ;
+				r >>= shift ;
+
+				u [j] = (mixres * l + m2 * r) >> mixbits ;
+				v [j] = l - r ;
+			}
+		}
+		else
+		{
+			for (j = 0 ; j < numSamples ; j++)
+			{
+				l = in [0] >> 8 ;
+				r = in [1] >> 8 ;
+				in += stride ;
+
+				u [j] = (mixres * l + m2 * r) >> mixbits ;
+				v [j] = l - r ;
+			}
+		}
+	}
+	else
+	{
+		/* Conventional separated stereo. */
+		if (bytesShifted != 0)
+		{
+			for (j = 0, k = 0 ; j < numSamples ; j++, k += 2)
+			{
+				l = in [0] >> 8 ;
+				r = in [1] >> 8 ;
+				in += stride ;
+
+				shiftUV [k + 0] = (uint16_t) (l & mask) ;
+				shiftUV [k + 1] = (uint16_t) (r & mask) ;
+
+				l >>= shift ;
+				r >>= shift ;
+
+				u [j] = l ;
+				v [j] = r ;
+			}
+		}
+		else
+		{
+			for (j = 0 ; j < numSamples ; j++)
+			{
+				l = in [0] >> 8 ;
+				r = in [1] >> 8 ;
+				in += stride ;
+			}
+		}
+	}
+}
+
+// 32-bit routines
+// - note that these really expect the internal data width to be < 32 but the arrays are 32-bit
+// - otherwise, the calculations might overflow into the 33rd bit and be lost
+// - therefore, these routines deal with the specified "unused lower" bytes in the "shift" buffers
+
+void
+mix32 (const int32_t * in, uint32_t stride, int32_t * u, int32_t * v, int32_t numSamples,
+			int32_t mixbits, int32_t mixres, uint16_t * shiftUV, int32_t bytesShifted)
+{
+	int32_t		shift = bytesShifted * 8 ;
+	uint32_t	mask = (1ul << shift) - 1 ;
+	int32_t		l, r ;
+	int32_t		j, k ;
+
+	if (mixres != 0)
+	{
+		int32_t		mod = 1 << mixbits ;
+		int32_t		m2 ;
+
+		//Assert (bytesShifted != 0) ;
+
+		/* matrixed stereo with shift */
+		m2 = mod - mixres ;
+		for (j = 0, k = 0 ; j < numSamples ; j++, k += 2)
+		{
+			l = in [0] ;
+			r = in [1] ;
+			in += stride ;
+
+			shiftUV [k + 0] = (uint16_t) (l & mask) ;
+			shiftUV [k + 1] = (uint16_t) (r & mask) ;
+
+			l >>= shift ;
+			r >>= shift ;
+
+			u [j] = (mixres * l + m2 * r) >> mixbits ;
+			v [j] = l - r ;
+		}
+	}
+	else
+	{
+		if (bytesShifted == 0)
+		{
+			/* de-interleaving w/o shift */
+			for (j = 0 ; j < numSamples ; j++)
+			{
+				u [j] = in [0] ;
+				v [j] = in [1] ;
+				in += stride ;
+			}
+		}
+		else
+		{
+			/* de-interleaving with shift */
+			for (j = 0, k = 0 ; j < numSamples ; j++, k += 2)
+			{
+				l = in [0] ;
+				r = in [1] ;
+				in += stride ;
+
+				shiftUV [k + 0] = (uint16_t) (l & mask) ;
+				shiftUV [k + 1] = (uint16_t) (r & mask) ;
+
+				l >>= shift ;
+				r >>= shift ;
+
+				u [j] = l ;
+				v [j] = r ;
+			}
+		}
+	}
+}
diff --git a/src/ALAC/matrixlib.h b/src/ALAC/matrixlib.h
new file mode 100644
index 0000000..d9be5fe
--- /dev/null
+++ b/src/ALAC/matrixlib.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") ;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+	File:		matrixlib.h
+
+	Contains:	ALAC mixing/matrixing routines to/from 32-bit predictor buffers.
+
+	Copyright:	Copyright (C) 2004 to 2011 Apple, Inc.
+*/
+
+#ifndef __MATRIXLIB_H
+#define __MATRIXLIB_H
+
+#pragma once
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// 16-bit routines
+void	mix16 (const int32_t * in, uint32_t stride, int32_t * u, int32_t * v, int32_t numSamples, int32_t mixbits, int32_t mixres) ;
+void	unmix16 (const int32_t * u, int32_t * v, int32_t * out, uint32_t stride, int32_t numSamples, int32_t mixbits, int32_t mixres) ;
+
+// 20-bit routines
+void	mix20 (const int32_t * in, uint32_t stride, int32_t * u, int32_t * v, int32_t numSamples, int32_t mixbits, int32_t mixres) ;
+void	unmix20 (const int32_t * u, int32_t * v, int32_t * out, uint32_t stride, int32_t numSamples, int32_t mixbits, int32_t mixres) ;
+
+// 24-bit routines
+// - 24-bit data sometimes compresses better by shifting off the bottom byte so these routines deal with
+//	 the specified "unused lower bytes" in the combined "shift" buffer
+void	mix24 (const int32_t * in, uint32_t stride, int32_t * u, int32_t * v, int32_t numSamples,
+				int32_t mixbits, int32_t mixres, uint16_t * shiftUV, int32_t bytesShifted) ;
+void	unmix24 (const int32_t * u, int32_t * v, int32_t * out, uint32_t stride, int32_t numSamples,
+					int32_t mixbits, int32_t mixres, uint16_t * shiftUV, int32_t bytesShifted) ;
+
+// 32-bit routines
+// - note that these really expect the internal data width to be < 32-bit but the arrays are 32-bit
+// - otherwise, the calculations might overflow into the 33rd bit and be lost
+// - therefore, these routines deal with the specified "unused lower" bytes in the combined "shift" buffer
+void	mix32 (const int32_t * in, uint32_t stride, int32_t * u, int32_t * v, int32_t numSamples,
+				int32_t mixbits, int32_t mixres, uint16_t * shiftUV, int32_t bytesShifted) ;
+void	unmix32 (const int32_t * u, int32_t * v, int32_t * out, uint32_t stride, int32_t numSamples,
+				int32_t mixbits, int32_t mixres, uint16_t * shiftUV, int32_t bytesShifted) ;
+
+// 20/24/32-bit <-> 32-bit helper routines (not really matrixing but convenient to put here)
+void	copy20ToPredictor (const int32_t * in, uint32_t stride, int32_t * out, int32_t numSamples) ;
+void	copy24ToPredictor (const int32_t * in, uint32_t stride, int32_t * out, int32_t numSamples) ;
+
+void	copyPredictorTo24 (const int32_t * in, int32_t * out, uint32_t stride, int32_t numSamples) ;
+void	copyPredictorTo24Shift (const int32_t * in, uint16_t * shift, int32_t * out, uint32_t stride, int32_t numSamples, int32_t bytesShifted) ;
+void	copyPredictorTo20 (const int32_t * in, int32_t * out, uint32_t stride, int32_t numSamples) ;
+
+void	copyPredictorTo32 (const int32_t * in, int32_t * out, uint32_t stride, int32_t numSamples) ;
+void	copyPredictorTo32Shift (const int32_t * in, uint16_t * shift, int32_t * out, uint32_t stride, int32_t numSamples, int32_t bytesShifted) ;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* __MATRIXLIB_H */
diff --git a/src/ALAC/shift.h b/src/ALAC/shift.h
new file mode 100644
index 0000000..bce404d
--- /dev/null
+++ b/src/ALAC/shift.h
@@ -0,0 +1,31 @@
+/*
+** Copyright (C) 2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#if __GNUC__
+#define ALWAYS_INLINE		__attribute__ ((always_inline))
+#elif defined _MSC_VER
+#define ALWAYS_INLINE		__forceinline
+#else
+#define ALWAYS_INLINE
+#endif
+
+
+static inline int32_t ALWAYS_INLINE
+arith_shift_left (int32_t x, int shift)
+{	return (int32_t) (((uint32_t) x) << shift) ;
+} /* arith_shift_left */
diff --git a/src/G72x/ChangeLog b/src/G72x/ChangeLog
new file mode 100644
index 0000000..aa108df
--- /dev/null
+++ b/src/G72x/ChangeLog
@@ -0,0 +1,50 @@
+2001-06-05  Erik de Castro Lopo  <erikd@mega-nerd.com>
+
+	* g72x.c
+	Added {} in function update () to prevent 'ambiguous else' warning messages.
+
+2000-07-14  Erik de Castro Lopo  <erikd@mega-nerd.com>
+
+	* g72x.c
+	Modified g72x_init_state () to fit in with the new structure of the code.
+	Implemented g72x_encode_block () and g72x_decode_block ().
+
+2000-07-12  Erik de Castro Lopo  <erikd@mega-nerd.com>
+
+    * g72x.h
+    Moved nearly all definitions and function prototypes from this file have been 
+    moved to private.h.
+    Added an enum defining the 4 different G72x ADPCM codecs.
+    Added new function prototypes to define a cleaner interface to the encoder 
+    and decoder. This new interface also allows samples to be processed in blocks
+    rather than on a sample by sample basis like the original code.
+    
+    * private.h
+    Added prototypes moved from g72x.h.
+    Changed struct g72x_state to a typedef struct { .. } G72x_PRIVATE.
+    Added fields to G72x_PRIVATE required for working on blocks of samples.
+
+2000-06-07  Erik de Castro Lopo  <erikd@mega-nerd.com>
+
+    * g72x.c
+    Fixed all compiler warnings.
+    Removed functions tandem_adjust() which is not required by libsndfile.
+    
+    * g721.c
+    Fixed all compiler warnings.
+    Removed functions tandem_adjust_alaw() and tandem_adjust_ulaw () which are not 
+    required by libsndfile.
+    Removed second parameter to g721_encoder () which is not required.
+
+    * g72x.h
+    Removed in_coding and out_coding parameters from all functions. These allowed
+    g72x encoding/decoding to/from A-law or u-law and are not required by libsndfile.
+    Removed unneeded defines for A-law, u-law and linear encoding.
+
+    * g723_16.c
+    Removed second parameter (in_coding) for g723_16_encoder().    
+    Removed second parameter (out_coding) for g723_16_decoder().
+    
+    * private.h
+    New file containing prototypes and tyepdefs private to G72x code.
+    
diff --git a/src/G72x/README b/src/G72x/README
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/G72x/README
diff --git a/src/G72x/README.original b/src/G72x/README.original
new file mode 100644
index 0000000..23b0e7d
--- /dev/null
+++ b/src/G72x/README.original
@@ -0,0 +1,94 @@
+The files in this directory comprise ANSI-C language reference implementations
+of the CCITT (International Telegraph and Telephone Consultative Committee)
+G.711, G.721 and G.723 voice compressions.  They have been tested on Sun
+SPARCstations and passed 82 out of 84 test vectors published by CCITT
+(Dec. 20, 1988) for G.721 and G.723.  [The two remaining test vectors,
+which the G.721 decoder implementation for u-law samples did not pass,
+may be in error because they are identical to two other vectors for G.723_40.]
+
+This source code is released by Sun Microsystems, Inc. to the public domain.
+Please give your acknowledgement in product literature if this code is used
+in your product implementation.
+
+Sun Microsystems supports some CCITT audio formats in Solaris 2.0 system
+software.  However, Sun's implementations have been optimized for higher
+performance on SPARCstations.
+
+
+The source files for CCITT conversion routines in this directory are:
+
+	g72x.h		header file for g721.c, g723_24.c and g723_40.c
+	g711.c		CCITT G.711 u-law and A-law compression
+	g72x.c		common denominator of G.721 and G.723 ADPCM codes
+	g721.c		CCITT G.721 32Kbps ADPCM coder (with g72x.c)
+	g723_24.c	CCITT G.723 24Kbps ADPCM coder (with g72x.c)
+	g723_40.c	CCITT G.723 40Kbps ADPCM coder (with g72x.c)
+
+
+Simple conversions between u-law, A-law, and 16-bit linear PCM are invoked
+as follows:
+
+	unsigned char		ucode, acode;
+	short			pcm_val;
+
+	ucode = linear2ulaw(pcm_val);
+	ucode = alaw2ulaw(acode);
+
+	acode = linear2alaw(pcm_val);
+	acode = ulaw2alaw(ucode);
+
+	pcm_val = ulaw2linear(ucode);
+	pcm_val = alaw2linear(acode);
+
+
+The other CCITT compression routines are invoked as follows:
+
+	#include "g72x.h"
+
+	struct g72x_state	state;
+	int			sample, code;
+
+	g72x_init_state(&state);
+	code = {g721,g723_24,g723_40}_encoder(sample, coding, &state);
+	sample = {g721,g723_24,g723_40}_decoder(code, coding, &state);
+
+where
+	coding = AUDIO_ENCODING_ULAW	for 8-bit u-law samples
+		 AUDIO_ENCODING_ALAW	for 8-bit A-law samples
+		 AUDIO_ENCODING_LINEAR	for 16-bit linear PCM samples
+
+
+
+This directory also includes the following sample programs:
+
+	encode.c	CCITT ADPCM encoder
+	decode.c	CCITT ADPCM decoder
+	Makefile	makefile for the sample programs
+
+
+The sample programs contain examples of how to call the various compression
+routines and pack/unpack the bits.  The sample programs read byte streams from
+stdin and write to stdout.  The input/output data is raw data (no file header
+or other identifying information is embedded).  The sample programs are
+invoked as follows:
+
+	encode [-3|4|5] [-a|u|l] <infile >outfile
+	decode [-3|4|5] [-a|u|l] <infile >outfile
+where:
+	-3	encode to (decode from) G.723 24kbps (3-bit) data
+	-4	encode to (decode from) G.721 32kbps (4-bit) data [the default]
+	-5	encode to (decode from) G.723 40kbps (5-bit) data
+	-a	encode from (decode to) A-law data
+	-u	encode from (decode to) u-law data [the default]
+	-l	encode from (decode to) 16-bit linear data
+
+Examples:
+	# Read 16-bit linear and output G.721
+	encode -4 -l <pcmfile >g721file
+
+	# Read 40Kbps G.723 and output A-law
+	decode -5 -a <g723file >alawfile
+
+	# Compress and then decompress u-law data using 24Kbps G.723
+	encode -3 <ulawin | deoced -3 >ulawout
+
diff --git a/src/G72x/g721.c b/src/G72x/g721.c
new file mode 100644
index 0000000..826d42c
--- /dev/null
+++ b/src/G72x/g721.c
@@ -0,0 +1,155 @@
+/*
+ * This source code is a product of Sun Microsystems, Inc. and is provided
+ * for unrestricted use.  Users may copy or modify this source code without
+ * charge.
+ *
+ * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
+ * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun source code is provided with no support and without any obligation on
+ * the part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+
+/*
+ * g721.c
+ *
+ * Description:
+ *
+ * g721_encoder (), g721_decoder ()
+ *
+ * These routines comprise an implementation of the CCITT G.721 ADPCM
+ * coding algorithm.  Essentially, this implementation is identical to
+ * the bit level description except for a few deviations which
+ * take advantage of work station attributes, such as hardware 2's
+ * complement arithmetic and large memory.  Specifically, certain time
+ * consuming operations such as multiplications are replaced
+ * with lookup tables and software 2's complement operations are
+ * replaced with hardware 2's complement.
+ *
+ * The deviation from the bit level specification (lookup tables)
+ * preserves the bit level performance specifications.
+ *
+ * As outlined in the G.721 Recommendation, the algorithm is broken
+ * down into modules.  Each section of code below is preceded by
+ * the name of the module which it is implementing.
+ *
+ */
+
+#include "g72x.h"
+#include "g72x_priv.h"
+
+static short qtab_721 [7] = { -124, 80, 178, 246, 300, 349, 400 } ;
+/*
+ * Maps G.721 code word to reconstructed scale factor normalized log
+ * magnitude values.
+ */
+static short _dqlntab [16] = { -2048, 4, 135, 213, 273, 323, 373, 425,
+							425, 373, 323, 273, 213, 135, 4, -2048 } ;
+
+/* Maps G.721 code word to log of scale factor multiplier. */
+static short _witab [16] = { -12, 18, 41, 64, 112, 198, 355, 1122,
+							1122, 355, 198, 112, 64, 41, 18, -12 } ;
+/*
+ * Maps G.721 code words to a set of values whose long and short
+ * term averages are computed and then compared to give an indication
+ * how stationary (steady state) the signal is.
+ */
+static short _fitab [16] = { 0, 0, 0, 0x200, 0x200, 0x200, 0x600, 0xE00,
+							0xE00, 0x600, 0x200, 0x200, 0x200, 0, 0, 0 } ;
+
+/*
+ * g721_encoder ()
+ *
+ * Encodes the input vale of linear PCM, A-law or u-law data sl and returns
+ * the resulting code. -1 is returned for unknown input coding value.
+ */
+int
+g721_encoder (
+	int		sl,
+	G72x_STATE *state_ptr)
+{
+	short		sezi, se, sez ;		/* ACCUM */
+	short		d ;			/* SUBTA */
+	short		sr ;			/* ADDB */
+	short		y ;			/* MIX */
+	short		dqsez ;			/* ADDC */
+	short		dq, i ;
+
+	/* linearize input sample to 14-bit PCM */
+	sl >>= 2 ;			/* 14-bit dynamic range */
+
+	sezi = predictor_zero (state_ptr) ;
+	sez = sezi >> 1 ;
+	se = (sezi + predictor_pole (state_ptr)) >> 1 ;	/* estimated signal */
+
+	d = sl - se ;				/* estimation difference */
+
+	/* quantize the prediction difference */
+	y = step_size (state_ptr) ;		/* quantizer step size */
+	i = quantize (d, y, qtab_721, 7) ;	/* i = ADPCM code */
+
+	dq = reconstruct (i & 8, _dqlntab [i], y) ;	/* quantized est diff */
+
+	sr = (dq < 0) ? se - (dq & 0x3FFF) : se + dq ;	/* reconst. signal */
+
+	dqsez = sr + sez - se ;			/* pole prediction diff. */
+
+	update (4, y, arith_shift_left (_witab [i], 5), _fitab [i], dq, sr, dqsez, state_ptr) ;
+
+	return i ;
+}
+
+/*
+ * g721_decoder ()
+ *
+ * Description:
+ *
+ * Decodes a 4-bit code of G.721 encoded data of i and
+ * returns the resulting linear PCM, A-law or u-law value.
+ * return -1 for unknown out_coding value.
+ */
+int
+g721_decoder (
+	int		i,
+	G72x_STATE *state_ptr)
+{
+	short		sezi, sei, sez, se ;	/* ACCUM */
+	short		y ;			/* MIX */
+	short		sr ;			/* ADDB */
+	short		dq ;
+	short		dqsez ;
+
+	i &= 0x0f ;			/* mask to get proper bits */
+	sezi = predictor_zero (state_ptr) ;
+	sez = sezi >> 1 ;
+	sei = sezi + predictor_pole (state_ptr) ;
+	se = sei >> 1 ;			/* se = estimated signal */
+
+	y = step_size (state_ptr) ;	/* dynamic quantizer step size */
+
+	dq = reconstruct (i & 0x08, _dqlntab [i], y) ; /* quantized diff. */
+
+	sr = (dq < 0) ? (se - (dq & 0x3FFF)) : se + dq ;	/* reconst. signal */
+
+	dqsez = sr - se + sez ;			/* pole prediction diff. */
+
+	update (4, y, arith_shift_left (_witab [i], 5), _fitab [i], dq, sr, dqsez, state_ptr) ;
+
+	/* sr was 14-bit dynamic range */
+	return arith_shift_left (sr, 2) ;
+}
+
diff --git a/src/G72x/g723_16.c b/src/G72x/g723_16.c
new file mode 100644
index 0000000..c377e63
--- /dev/null
+++ b/src/G72x/g723_16.c
@@ -0,0 +1,162 @@
+/*
+ * This source code is a product of Sun Microsystems, Inc. and is provided
+ * for unrestricted use.  Users may copy or modify this source code without
+ * charge.
+ *
+ * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
+ * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun source code is provided with no support and without any obligation on
+ * the part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+/* 16kbps version created, used 24kbps code and changing as little as possible.
+ * G.726 specs are available from ITU's gopher or WWW site (http://www.itu.ch)
+ * If any errors are found, please contact me at mrand@tamu.edu
+ *      -Marc Randolph
+ */
+
+/*
+ * g723_16.c
+ *
+ * Description:
+ *
+ * g723_16_encoder (), g723_16_decoder ()
+ *
+ * These routines comprise an implementation of the CCITT G.726 16 Kbps
+ * ADPCM coding algorithm.  Essentially, this implementation is identical to
+ * the bit level description except for a few deviations which take advantage
+ * of workstation attributes, such as hardware 2's complement arithmetic.
+ *
+ */
+
+#include "g72x.h"
+#include "g72x_priv.h"
+
+/*
+ * Maps G.723_16 code word to reconstructed scale factor normalized log
+ * magnitude values.  Comes from Table 11/G.726
+ */
+static short _dqlntab [4] = { 116, 365, 365, 116 } ;
+
+/* Maps G.723_16 code word to log of scale factor multiplier.
+ *
+ * _witab [4] is actually {-22 , 439, 439, -22}, but FILTD wants it
+ * as WI << 5  (multiplied by 32), so we'll do that here
+ */
+static short _witab [4] = { -704, 14048, 14048, -704 } ;
+
+/*
+ * Maps G.723_16 code words to a set of values whose long and short
+ * term averages are computed and then compared to give an indication
+ * how stationary (steady state) the signal is.
+ */
+
+/* Comes from FUNCTF */
+static short _fitab [4] = { 0, 0xE00, 0xE00, 0 } ;
+
+/* Comes from quantizer decision level tables (Table 7/G.726)
+ */
+static short qtab_723_16 [1] = { 261 } ;
+
+
+/*
+ * g723_16_encoder ()
+ *
+ * Encodes a linear PCM, A-law or u-law input sample and returns its 2-bit code.
+ * Returns -1 if invalid input coding value.
+ */
+int
+g723_16_encoder (
+	int			sl,
+	G72x_STATE *state_ptr)
+{
+	short sei, sezi, se, sez ;	/* ACCUM */
+	short d ;					/* SUBTA */
+	short y ;					/* MIX */
+	short sr ;					/* ADDB */
+	short dqsez ;				/* ADDC */
+	short dq, i ;
+
+	/* linearize input sample to 14-bit PCM */
+	sl >>= 2 ;	/* sl of 14-bit dynamic range */
+
+	sezi = predictor_zero (state_ptr) ;
+	sez = sezi >> 1 ;
+	sei = sezi + predictor_pole (state_ptr) ;
+	se = sei >> 1 ;	/* se = estimated signal */
+
+	d = sl - se ;	/* d = estimation diff. */
+
+	/* quantize prediction difference d */
+	y = step_size (state_ptr) ;				/* quantizer step size */
+	i = quantize (d, y, qtab_723_16, 1) ;	/* i = ADPCM code */
+
+	/* Since quantize () only produces a three level output
+	 * (1, 2, or 3), we must create the fourth one on our own
+	 */
+	if (i == 3)					/* i code for the zero region */
+		if ((d & 0x8000) == 0)	/* If d > 0, i=3 isn't right... */
+			i = 0 ;
+
+	dq = reconstruct (i & 2, _dqlntab [i], y) ; /* quantized diff. */
+
+	sr = (dq < 0) ? se - (dq & 0x3FFF) : se + dq ; /* reconstructed signal */
+
+	dqsez = sr + sez - se ;		/* pole prediction diff. */
+
+	update (2, y, _witab [i], _fitab [i], dq, sr, dqsez, state_ptr) ;
+
+	return i ;
+}
+
+/*
+ * g723_16_decoder ()
+ *
+ * Decodes a 2-bit CCITT G.723_16 ADPCM code and returns
+ * the resulting 16-bit linear PCM, A-law or u-law sample value.
+ * -1 is returned if the output coding is unknown.
+ */
+int
+g723_16_decoder (
+	int			i,
+	G72x_STATE *state_ptr)
+{
+	short sezi, sei, sez, se ;	/* ACCUM */
+	short y ;					/* MIX */
+	short sr ;					/* ADDB */
+	short dq ;
+	short dqsez ;
+
+	i &= 0x03 ;			/* mask to get proper bits */
+	sezi = predictor_zero (state_ptr) ;
+	sez = sezi >> 1 ;
+	sei = sezi + predictor_pole (state_ptr) ;
+	se = sei >> 1 ;		/* se = estimated signal */
+
+	y = step_size (state_ptr) ;	/* adaptive quantizer step size */
+	dq = reconstruct (i & 0x02, _dqlntab [i], y) ; /* unquantize pred diff */
+
+	sr = (dq < 0) ? (se - (dq & 0x3FFF)) : (se + dq) ; /* reconst. signal */
+
+	dqsez = sr - se + sez ;	/* pole prediction diff. */
+
+	update (2, y, _witab [i], _fitab [i], dq, sr, dqsez, state_ptr) ;
+
+	/* sr was of 14-bit dynamic range */
+	return (sr << 2) ;
+}
+
diff --git a/src/G72x/g723_24.c b/src/G72x/g723_24.c
new file mode 100644
index 0000000..9cc2f6d
--- /dev/null
+++ b/src/G72x/g723_24.c
@@ -0,0 +1,139 @@
+/*
+ * This source code is a product of Sun Microsystems, Inc. and is provided
+ * for unrestricted use.  Users may copy or modify this source code without
+ * charge.
+ *
+ * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
+ * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun source code is provided with no support and without any obligation on
+ * the part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+
+/*
+ * g723_24.c
+ *
+ * Description:
+ *
+ * g723_24_encoder (), g723_24_decoder ()
+ *
+ * These routines comprise an implementation of the CCITT G.723 24 Kbps
+ * ADPCM coding algorithm.  Essentially, this implementation is identical to
+ * the bit level description except for a few deviations which take advantage
+ * of workstation attributes, such as hardware 2's complement arithmetic.
+ *
+ */
+
+#include "g72x.h"
+#include "g72x_priv.h"
+
+/*
+ * Maps G.723_24 code word to reconstructed scale factor normalized log
+ * magnitude values.
+ */
+static short _dqlntab [8] = { -2048, 135, 273, 373, 373, 273, 135, -2048 } ;
+
+/* Maps G.723_24 code word to log of scale factor multiplier. */
+static short _witab [8] = { -128, 960, 4384, 18624, 18624, 4384, 960, -128 } ;
+
+/*
+ * Maps G.723_24 code words to a set of values whose long and short
+ * term averages are computed and then compared to give an indication
+ * how stationary (steady state) the signal is.
+ */
+static short _fitab [8] = { 0, 0x200, 0x400, 0xE00, 0xE00, 0x400, 0x200, 0 } ;
+
+static short qtab_723_24 [3] = { 8, 218, 331 } ;
+
+/*
+ * g723_24_encoder ()
+ *
+ * Encodes a linear PCM, A-law or u-law input sample and returns its 3-bit code.
+ * Returns -1 if invalid input coding value.
+ */
+int
+g723_24_encoder (
+	int		sl,
+	G72x_STATE *state_ptr)
+{
+	short		sei, sezi, se, sez ;	/* ACCUM */
+	short		d ;			/* SUBTA */
+	short		y ;			/* MIX */
+	short		sr ;			/* ADDB */
+	short		dqsez ;			/* ADDC */
+	short		dq, i ;
+
+	/* linearize input sample to 14-bit PCM */
+	sl >>= 2 ;		/* sl of 14-bit dynamic range */
+
+	sezi = predictor_zero (state_ptr) ;
+	sez = sezi >> 1 ;
+	sei = sezi + predictor_pole (state_ptr) ;
+	se = sei >> 1 ;			/* se = estimated signal */
+
+	d = sl - se ;			/* d = estimation diff. */
+
+	/* quantize prediction difference d */
+	y = step_size (state_ptr) ;	/* quantizer step size */
+	i = quantize (d, y, qtab_723_24, 3) ;	/* i = ADPCM code */
+	dq = reconstruct (i & 4, _dqlntab [i], y) ; /* quantized diff. */
+
+	sr = (dq < 0) ? se - (dq & 0x3FFF) : se + dq ; /* reconstructed signal */
+
+	dqsez = sr + sez - se ;		/* pole prediction diff. */
+
+	update (3, y, _witab [i], _fitab [i], dq, sr, dqsez, state_ptr) ;
+
+	return i ;
+}
+
+/*
+ * g723_24_decoder ()
+ *
+ * Decodes a 3-bit CCITT G.723_24 ADPCM code and returns
+ * the resulting 16-bit linear PCM, A-law or u-law sample value.
+ * -1 is returned if the output coding is unknown.
+ */
+int
+g723_24_decoder (
+	int		i,
+	G72x_STATE *state_ptr)
+{
+	short		sezi, sei, sez, se ;	/* ACCUM */
+	short		y ;			/* MIX */
+	short		sr ;			/* ADDB */
+	short		dq ;
+	short		dqsez ;
+
+	i &= 0x07 ;			/* mask to get proper bits */
+	sezi = predictor_zero (state_ptr) ;
+	sez = sezi >> 1 ;
+	sei = sezi + predictor_pole (state_ptr) ;
+	se = sei >> 1 ;			/* se = estimated signal */
+
+	y = step_size (state_ptr) ;	/* adaptive quantizer step size */
+	dq = reconstruct (i & 0x04, _dqlntab [i], y) ; /* unquantize pred diff */
+
+	sr = (dq < 0) ? (se - (dq & 0x3FFF)) : (se + dq) ; /* reconst. signal */
+
+	dqsez = sr - se + sez ;			/* pole prediction diff. */
+
+	update (3, y, _witab [i], _fitab [i], dq, sr, dqsez, state_ptr) ;
+
+	return arith_shift_left (sr, 2) ;	/* sr was of 14-bit dynamic range */
+}
+
diff --git a/src/G72x/g723_40.c b/src/G72x/g723_40.c
new file mode 100644
index 0000000..f7f8f74
--- /dev/null
+++ b/src/G72x/g723_40.c
@@ -0,0 +1,153 @@
+/*
+ * This source code is a product of Sun Microsystems, Inc. and is provided
+ * for unrestricted use.  Users may copy or modify this source code without
+ * charge.
+ *
+ * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
+ * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun source code is provided with no support and without any obligation on
+ * the part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+
+/*
+ * g723_40.c
+ *
+ * Description:
+ *
+ * g723_40_encoder (), g723_40_decoder ()
+ *
+ * These routines comprise an implementation of the CCITT G.723 40Kbps
+ * ADPCM coding algorithm.  Essentially, this implementation is identical to
+ * the bit level description except for a few deviations which
+ * take advantage of workstation attributes, such as hardware 2's
+ * complement arithmetic.
+ *
+ * The deviation from the bit level specification (lookup tables),
+ * preserves the bit level performance specifications.
+ *
+ * As outlined in the G.723 Recommendation, the algorithm is broken
+ * down into modules.  Each section of code below is preceded by
+ * the name of the module which it is implementing.
+ *
+ */
+
+#include "g72x.h"
+#include "g72x_priv.h"
+
+/*
+ * Maps G.723_40 code word to ructeconstructed scale factor normalized log
+ * magnitude values.
+ */
+static short	_dqlntab [32] = { -2048, -66, 28, 104, 169, 224, 274, 318,
+				358, 395, 429, 459, 488, 514, 539, 566,
+				566, 539, 514, 488, 459, 429, 395, 358,
+				318, 274, 224, 169, 104, 28, -66, -2048 } ;
+
+/* Maps G.723_40 code word to log of scale factor multiplier. */
+static short	_witab [32] = { 448, 448, 768, 1248, 1280, 1312, 1856, 3200,
+			4512, 5728, 7008, 8960, 11456, 14080, 16928, 22272,
+			22272, 16928, 14080, 11456, 8960, 7008, 5728, 4512,
+			3200, 1856, 1312, 1280, 1248, 768, 448, 448 } ;
+
+/*
+ * Maps G.723_40 code words to a set of values whose long and short
+ * term averages are computed and then compared to give an indication
+ * how stationary (steady state) the signal is.
+ */
+static short	_fitab [32] = { 0, 0, 0, 0, 0, 0x200, 0x200, 0x200,
+			0x200, 0x200, 0x400, 0x600, 0x800, 0xA00, 0xC00, 0xC00,
+			0xC00, 0xC00, 0xA00, 0x800, 0x600, 0x400, 0x200, 0x200,
+			0x200, 0x200, 0x200, 0, 0, 0, 0, 0 } ;
+
+static short qtab_723_40 [15] = { -122, -16, 68, 139, 198, 250, 298, 339,
+				378, 413, 445, 475, 502, 528, 553 } ;
+
+/*
+ * g723_40_encoder ()
+ *
+ * Encodes a 16-bit linear PCM, A-law or u-law input sample and retuens
+ * the resulting 5-bit CCITT G.723 40Kbps code.
+ * Returns -1 if the input coding value is invalid.
+ */
+int	g723_40_encoder (int sl, G72x_STATE *state_ptr)
+{
+	short		sei, sezi, se, sez ;	/* ACCUM */
+	short		d ;			/* SUBTA */
+	short		y ;			/* MIX */
+	short		sr ;			/* ADDB */
+	short		dqsez ;			/* ADDC */
+	short		dq, i ;
+
+	/* linearize input sample to 14-bit PCM */
+	sl >>= 2 ;		/* sl of 14-bit dynamic range */
+
+	sezi = predictor_zero (state_ptr) ;
+	sez = sezi >> 1 ;
+	sei = sezi + predictor_pole (state_ptr) ;
+	se = sei >> 1 ;			/* se = estimated signal */
+
+	d = sl - se ;			/* d = estimation difference */
+
+	/* quantize prediction difference */
+	y = step_size (state_ptr) ;	/* adaptive quantizer step size */
+	i = quantize (d, y, qtab_723_40, 15) ;	/* i = ADPCM code */
+
+	dq = reconstruct (i & 0x10, _dqlntab [i], y) ;	/* quantized diff */
+
+	sr = (dq < 0) ? se - (dq & 0x7FFF) : se + dq ; /* reconstructed signal */
+
+	dqsez = sr + sez - se ;		/* dqsez = pole prediction diff. */
+
+	update (5, y, _witab [i], _fitab [i], dq, sr, dqsez, state_ptr) ;
+
+	return i ;
+}
+
+/*
+ * g723_40_decoder ()
+ *
+ * Decodes a 5-bit CCITT G.723 40Kbps code and returns
+ * the resulting 16-bit linear PCM, A-law or u-law sample value.
+ * -1 is returned if the output coding is unknown.
+ */
+int	g723_40_decoder	(int i, G72x_STATE *state_ptr)
+{
+	short		sezi, sei, sez, se ;	/* ACCUM */
+	short		y ;			/* MIX */
+	short		sr ;			/* ADDB */
+	short		dq ;
+	short		dqsez ;
+
+	i &= 0x1f ;			/* mask to get proper bits */
+	sezi = predictor_zero (state_ptr) ;
+	sez = sezi >> 1 ;
+	sei = sezi + predictor_pole (state_ptr) ;
+	se = sei >> 1 ;			/* se = estimated signal */
+
+	y = step_size (state_ptr) ;	/* adaptive quantizer step size */
+	dq = reconstruct (i & 0x10, _dqlntab [i], y) ;	/* estimation diff. */
+
+	sr = (dq < 0) ? (se - (dq & 0x7FFF)) : (se + dq) ; /* reconst. signal */
+
+	dqsez = sr - se + sez ;		/* pole prediction diff. */
+
+	update (5, y, _witab [i], _fitab [i], dq, sr, dqsez, state_ptr) ;
+
+	return arith_shift_left (sr, 2) ;	/* sr was of 14-bit dynamic range */
+}
+
diff --git a/src/G72x/g72x.c b/src/G72x/g72x.c
new file mode 100644
index 0000000..2d6469c
--- /dev/null
+++ b/src/G72x/g72x.c
@@ -0,0 +1,645 @@
+/*
+ * This source code is a product of Sun Microsystems, Inc. and is provided
+ * for unrestricted use.  Users may copy or modify this source code without
+ * charge.
+ *
+ * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
+ * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun source code is provided with no support and without any obligation on
+ * the part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+
+/*
+ * g72x.c
+ *
+ * Common routines for G.721 and G.723 conversions.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "g72x.h"
+#include "g72x_priv.h"
+
+static G72x_STATE * g72x_state_new (void) ;
+static int unpack_bytes (int bits, int blocksize, const unsigned char * block, short * samples) ;
+static int pack_bytes (int bits, const short * samples, unsigned char * block) ;
+
+static
+short power2 [15] =
+{	1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80,
+	0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000
+} ;
+
+/*
+ * quan ()
+ *
+ * quantizes the input val against the table of size short integers.
+ * It returns i if table [i - 1] <= val < table [i].
+ *
+ * Using linear search for simple coding.
+ */
+static
+int quan (int val, short *table, int size)
+{
+	int		i ;
+
+	for (i = 0 ; i < size ; i++)
+		if (val < *table++)
+			break ;
+	return i ;
+}
+
+/*
+ * fmult ()
+ *
+ * returns the integer product of the 14-bit integer "an" and
+ * "floating point" representation (4-bit exponent, 6-bit mantessa) "srn".
+ */
+static
+int fmult (int an, int srn)
+{
+	short		anmag, anexp, anmant ;
+	short		wanexp, wanmant ;
+	short		retval ;
+
+	anmag = (an > 0) ? an : ((-an) & 0x1FFF) ;
+	anexp = quan (anmag, power2, 15) - 6 ;
+	anmant = (anmag == 0) ? 32 :
+				(anexp >= 0) ? anmag >> anexp : anmag << -anexp ;
+	wanexp = anexp + ((srn >> 6) & 0xF) - 13 ;
+
+	/*
+	** The original was :
+	**		wanmant = (anmant * (srn & 0x3F) + 0x30) >> 4 ;
+	** but could see no valid reason for the + 0x30.
+	** Removed it and it improved the SNR of the codec.
+	*/
+
+	wanmant = (anmant * (srn & 0x3F)) >> 4 ;
+
+	retval = (wanexp >= 0) ? ((wanmant << wanexp) & 0x7FFF) : (wanmant >> -wanexp) ;
+
+	return (((an ^ srn) < 0) ? -retval : retval) ;
+}
+
+static G72x_STATE * g72x_state_new (void)
+{	return calloc (1, sizeof (G72x_STATE)) ;
+}
+
+/*
+ * private_init_state ()
+ *
+ * This routine initializes and/or resets the G72x_PRIVATE structure
+ * pointed to by 'state_ptr'.
+ * All the initial state values are specified in the CCITT G.721 document.
+ */
+void private_init_state (G72x_STATE *state_ptr)
+{
+	int		cnta ;
+
+	state_ptr->yl = 34816 ;
+	state_ptr->yu = 544 ;
+	state_ptr->dms = 0 ;
+	state_ptr->dml = 0 ;
+	state_ptr->ap = 0 ;
+	for (cnta = 0 ; cnta < 2 ; cnta++)
+	{	state_ptr->a [cnta] = 0 ;
+		state_ptr->pk [cnta] = 0 ;
+		state_ptr->sr [cnta] = 32 ;
+		}
+	for (cnta = 0 ; cnta < 6 ; cnta++)
+	{	state_ptr->b [cnta] = 0 ;
+		state_ptr->dq [cnta] = 32 ;
+		}
+	state_ptr->td = 0 ;
+}	/* private_init_state */
+
+struct g72x_state * g72x_reader_init (int codec, int *blocksize, int *samplesperblock)
+{	G72x_STATE *pstate ;
+
+	if ((pstate = g72x_state_new ()) == NULL)
+		return NULL ;
+
+	private_init_state (pstate) ;
+
+	pstate->encoder = NULL ;
+
+	switch (codec)
+	{	case G723_16_BITS_PER_SAMPLE : /* 2 bits per sample. */
+				pstate->decoder = g723_16_decoder ;
+				*blocksize = G723_16_BYTES_PER_BLOCK ;
+				*samplesperblock = G723_16_SAMPLES_PER_BLOCK ;
+				pstate->codec_bits = 2 ;
+				pstate->blocksize = G723_16_BYTES_PER_BLOCK ;
+				pstate->samplesperblock = G723_16_SAMPLES_PER_BLOCK ;
+				break ;
+
+		case G723_24_BITS_PER_SAMPLE : /* 3 bits per sample. */
+				pstate->decoder = g723_24_decoder ;
+				*blocksize = G723_24_BYTES_PER_BLOCK ;
+				*samplesperblock = G723_24_SAMPLES_PER_BLOCK ;
+				pstate->codec_bits = 3 ;
+				pstate->blocksize = G723_24_BYTES_PER_BLOCK ;
+				pstate->samplesperblock = G723_24_SAMPLES_PER_BLOCK ;
+				break ;
+
+		case G721_32_BITS_PER_SAMPLE : /* 4 bits per sample. */
+				pstate->decoder = g721_decoder ;
+				*blocksize = G721_32_BYTES_PER_BLOCK ;
+				*samplesperblock = G721_32_SAMPLES_PER_BLOCK ;
+				pstate->codec_bits = 4 ;
+				pstate->blocksize = G721_32_BYTES_PER_BLOCK ;
+				pstate->samplesperblock = G721_32_SAMPLES_PER_BLOCK ;
+				break ;
+
+		case G721_40_BITS_PER_SAMPLE : /* 5 bits per sample. */
+				pstate->decoder = g723_40_decoder ;
+				*blocksize = G721_40_BYTES_PER_BLOCK ;
+				*samplesperblock = G721_40_SAMPLES_PER_BLOCK ;
+				pstate->codec_bits = 5 ;
+				pstate->blocksize = G721_40_BYTES_PER_BLOCK ;
+				pstate->samplesperblock = G721_40_SAMPLES_PER_BLOCK ;
+				break ;
+
+		default :
+				free (pstate) ;
+				return NULL ;
+		} ;
+
+	return pstate ;
+}	/* g72x_reader_init */
+
+struct g72x_state * g72x_writer_init (int codec, int *blocksize, int *samplesperblock)
+{	G72x_STATE *pstate ;
+
+	if ((pstate = g72x_state_new ()) == NULL)
+		return NULL ;
+
+	private_init_state (pstate) ;
+	pstate->decoder = NULL ;
+
+	switch (codec)
+	{	case G723_16_BITS_PER_SAMPLE : /* 2 bits per sample. */
+				pstate->encoder = g723_16_encoder ;
+				*blocksize = G723_16_BYTES_PER_BLOCK ;
+				*samplesperblock = G723_16_SAMPLES_PER_BLOCK ;
+				pstate->codec_bits = 2 ;
+				pstate->blocksize = G723_16_BYTES_PER_BLOCK ;
+				pstate->samplesperblock = G723_16_SAMPLES_PER_BLOCK ;
+				break ;
+
+		case G723_24_BITS_PER_SAMPLE : /* 3 bits per sample. */
+				pstate->encoder = g723_24_encoder ;
+				*blocksize = G723_24_BYTES_PER_BLOCK ;
+				*samplesperblock = G723_24_SAMPLES_PER_BLOCK ;
+				pstate->codec_bits = 3 ;
+				pstate->blocksize = G723_24_BYTES_PER_BLOCK ;
+				pstate->samplesperblock = G723_24_SAMPLES_PER_BLOCK ;
+				break ;
+
+		case G721_32_BITS_PER_SAMPLE : /* 4 bits per sample. */
+				pstate->encoder = g721_encoder ;
+				*blocksize = G721_32_BYTES_PER_BLOCK ;
+				*samplesperblock = G721_32_SAMPLES_PER_BLOCK ;
+				pstate->codec_bits = 4 ;
+				pstate->blocksize = G721_32_BYTES_PER_BLOCK ;
+				pstate->samplesperblock = G721_32_SAMPLES_PER_BLOCK ;
+				break ;
+
+		case G721_40_BITS_PER_SAMPLE : /* 5 bits per sample. */
+				pstate->encoder = g723_40_encoder ;
+				*blocksize = G721_40_BYTES_PER_BLOCK ;
+				*samplesperblock = G721_40_SAMPLES_PER_BLOCK ;
+				pstate->codec_bits = 5 ;
+				pstate->blocksize = G721_40_BYTES_PER_BLOCK ;
+				pstate->samplesperblock = G721_40_SAMPLES_PER_BLOCK ;
+				break ;
+
+		default :
+				free (pstate) ;
+				return NULL ;
+		} ;
+
+	return pstate ;
+}	/* g72x_writer_init */
+
+int g72x_decode_block (G72x_STATE *pstate, const unsigned char *block, short *samples)
+{	int	k, count ;
+
+	count = unpack_bytes (pstate->codec_bits, pstate->blocksize, block, samples) ;
+
+	for (k = 0 ; k < count ; k++)
+		samples [k] = pstate->decoder (samples [k], pstate) ;
+
+	return 0 ;
+}	/* g72x_decode_block */
+
+int g72x_encode_block (G72x_STATE *pstate, short *samples, unsigned char *block)
+{	int k, count ;
+
+	for (k = 0 ; k < pstate->samplesperblock ; k++)
+		samples [k] = pstate->encoder (samples [k], pstate) ;
+
+	count = pack_bytes (pstate->codec_bits, samples, block) ;
+
+	return count ;
+}	/* g72x_encode_block */
+
+/*
+ * predictor_zero ()
+ *
+ * computes the estimated signal from 6-zero predictor.
+ *
+ */
+int predictor_zero (G72x_STATE *state_ptr)
+{
+	int		i ;
+	int		sezi ;
+
+	sezi = fmult (state_ptr->b [0] >> 2, state_ptr->dq [0]) ;
+	for (i = 1 ; i < 6 ; i++)			/* ACCUM */
+		sezi += fmult (state_ptr->b [i] >> 2, state_ptr->dq [i]) ;
+	return sezi ;
+}
+/*
+ * predictor_pole ()
+ *
+ * computes the estimated signal from 2-pole predictor.
+ *
+ */
+int predictor_pole (G72x_STATE *state_ptr)
+{
+	return (fmult (state_ptr->a [1] >> 2, state_ptr->sr [1]) +
+			fmult (state_ptr->a [0] >> 2, state_ptr->sr [0])) ;
+}
+/*
+ * step_size ()
+ *
+ * computes the quantization step size of the adaptive quantizer.
+ *
+ */
+int step_size (G72x_STATE *state_ptr)
+{
+	int		y ;
+	int		dif ;
+	int		al ;
+
+	if (state_ptr->ap >= 256)
+		return (state_ptr->yu) ;
+	else {
+		y = state_ptr->yl >> 6 ;
+		dif = state_ptr->yu - y ;
+		al = state_ptr->ap >> 2 ;
+		if (dif > 0)
+			y += (dif * al) >> 6 ;
+		else if (dif < 0)
+			y += (dif * al + 0x3F) >> 6 ;
+		return y ;
+	}
+}
+
+/*
+ * quantize ()
+ *
+ * Given a raw sample, 'd', of the difference signal and a
+ * quantization step size scale factor, 'y', this routine returns the
+ * ADPCM codeword to which that sample gets quantized.  The step
+ * size scale factor division operation is done in the log base 2 domain
+ * as a subtraction.
+ */
+int quantize (
+	int		d,	/* Raw difference signal sample */
+	int		y,	/* Step size multiplier */
+	short	*table,	/* quantization table */
+	int		size)	/* table size of short integers */
+{
+	short		dqm ;	/* Magnitude of 'd' */
+	short		expon ;	/* Integer part of base 2 log of 'd' */
+	short		mant ;	/* Fractional part of base 2 log */
+	short		dl ;	/* Log of magnitude of 'd' */
+	short		dln ;	/* Step size scale factor normalized log */
+	int		i ;
+
+	/*
+	 * LOG
+	 *
+	 * Compute base 2 log of 'd', and store in 'dl'.
+	 */
+	dqm = abs (d) ;
+	expon = quan (dqm >> 1, power2, 15) ;
+	mant = ((dqm << 7) >> expon) & 0x7F ;	/* Fractional portion. */
+	dl = (expon << 7) + mant ;
+
+	/*
+	 * SUBTB
+	 *
+	 * "Divide" by step size multiplier.
+	 */
+	dln = dl - (y >> 2) ;
+
+	/*
+	 * QUAN
+	 *
+	 * Obtain codword i for 'd'.
+	 */
+	i = quan (dln, table, size) ;
+	if (d < 0)			/* take 1's complement of i */
+		return ((size << 1) + 1 - i) ;
+	else if (i == 0)		/* take 1's complement of 0 */
+		return ((size << 1) + 1) ; /* new in 1988 */
+
+	return i ;
+}
+/*
+ * reconstruct ()
+ *
+ * Returns reconstructed difference signal 'dq' obtained from
+ * codeword 'i' and quantization step size scale factor 'y'.
+ * Multiplication is performed in log base 2 domain as addition.
+ */
+int
+reconstruct (
+	int		sign,	/* 0 for non-negative value */
+	int		dqln,	/* G.72x codeword */
+	int		y)	/* Step size multiplier */
+{
+	short		dql ;	/* Log of 'dq' magnitude */
+	short		dex ;	/* Integer part of log */
+	short		dqt ;
+	short		dq ;	/* Reconstructed difference signal sample */
+
+	dql = dqln + (y >> 2) ;	/* ADDA */
+
+	if (dql < 0)
+		return ((sign) ? -0x8000 : 0) ;
+	else		/* ANTILOG */
+	{	dex = (dql >> 7) & 15 ;
+		dqt = 128 + (dql & 127) ;
+		dq = (dqt << 7) >> (14 - dex) ;
+		return ((sign) ? (dq - 0x8000) : dq) ;
+		}
+}
+
+
+/*
+ * update ()
+ *
+ * updates the state variables for each output code
+ */
+void
+update (
+	int		code_size,	/* distinguish 723_40 with others */
+	int		y,		/* quantizer step size */
+	int		wi,		/* scale factor multiplier */
+	int		fi,		/* for long/short term energies */
+	int		dq,		/* quantized prediction difference */
+	int		sr,		/* reconstructed signal */
+	int		dqsez,		/* difference from 2-pole predictor */
+	G72x_STATE *state_ptr)	/* coder state pointer */
+{
+	int		cnt ;
+	short		mag, expon ;	/* Adaptive predictor, FLOAT A */
+	short		a2p = 0 ;	/* LIMC */
+	short		a1ul ;		/* UPA1 */
+	short		pks1 ;		/* UPA2 */
+	short		fa1 ;
+	char		tr ;		/* tone/transition detector */
+	short		ylint, thr2, dqthr ;
+	short		ylfrac, thr1 ;
+	short		pk0 ;
+
+	pk0 = (dqsez < 0) ? 1 : 0 ;	/* needed in updating predictor poles */
+
+	mag = dq & 0x7FFF ;		/* prediction difference magnitude */
+	/* TRANS */
+	ylint = state_ptr->yl >> 15 ;	/* exponent part of yl */
+	ylfrac = (state_ptr->yl >> 10) & 0x1F ;	/* fractional part of yl */
+	thr1 = (32 + ylfrac) << ylint ;		/* threshold */
+	thr2 = (ylint > 9) ? 31 << 10 : thr1 ;	/* limit thr2 to 31 << 10 */
+	dqthr = (thr2 + (thr2 >> 1)) >> 1 ;	/* dqthr = 0.75 * thr2 */
+	if (state_ptr->td == 0)		/* signal supposed voice */
+		tr = 0 ;
+	else if (mag <= dqthr)		/* supposed data, but small mag */
+		tr = 0 ;			/* treated as voice */
+	else				/* signal is data (modem) */
+		tr = 1 ;
+
+	/*
+	 * Quantizer scale factor adaptation.
+	 */
+
+	/* FUNCTW & FILTD & DELAY */
+	/* update non-steady state step size multiplier */
+	state_ptr->yu = y + ((wi - y) >> 5) ;
+
+	/* LIMB */
+	if (state_ptr->yu < 544)	/* 544 <= yu <= 5120 */
+		state_ptr->yu = 544 ;
+	else if (state_ptr->yu > 5120)
+		state_ptr->yu = 5120 ;
+
+	/* FILTE & DELAY */
+	/* update steady state step size multiplier */
+	state_ptr->yl += state_ptr->yu + ((-state_ptr->yl) >> 6) ;
+
+	/*
+	 * Adaptive predictor coefficients.
+	 */
+	if (tr == 1) {			/* reset a's and b's for modem signal */
+		state_ptr->a [0] = 0 ;
+		state_ptr->a [1] = 0 ;
+		state_ptr->b [0] = 0 ;
+		state_ptr->b [1] = 0 ;
+		state_ptr->b [2] = 0 ;
+		state_ptr->b [3] = 0 ;
+		state_ptr->b [4] = 0 ;
+		state_ptr->b [5] = 0 ;
+		}
+	else			/* update a's and b's */
+	{	pks1 = pk0 ^ state_ptr->pk [0] ;		/* UPA2 */
+
+		/* update predictor pole a [1] */
+		a2p = state_ptr->a [1] - (state_ptr->a [1] >> 7) ;
+		if (dqsez != 0)
+		{	fa1 = (pks1) ? state_ptr->a [0] : -state_ptr->a [0] ;
+			if (fa1 < -8191)	/* a2p = function of fa1 */
+				a2p -= 0x100 ;
+			else if (fa1 > 8191)
+				a2p += 0xFF ;
+			else
+				a2p += fa1 >> 5 ;
+
+			if (pk0 ^ state_ptr->pk [1])
+			{	/* LIMC */
+				if (a2p <= -12160)
+					a2p = -12288 ;
+				else if (a2p >= 12416)
+					a2p = 12288 ;
+				else
+					a2p -= 0x80 ;
+				}
+			else if (a2p <= -12416)
+				a2p = -12288 ;
+			else if (a2p >= 12160)
+				a2p = 12288 ;
+			else
+				a2p += 0x80 ;
+		}
+
+		/* TRIGB & DELAY */
+		state_ptr->a [1] = a2p ;
+
+		/* UPA1 */
+		/* update predictor pole a [0] */
+		state_ptr->a [0] -= state_ptr->a [0] >> 8 ;
+		if (dqsez != 0)
+		{	if (pks1 == 0)
+				state_ptr->a [0] += 192 ;
+			else
+				state_ptr->a [0] -= 192 ;
+			} ;
+
+		/* LIMD */
+		a1ul = 15360 - a2p ;
+		if (state_ptr->a [0] < -a1ul)
+			state_ptr->a [0] = -a1ul ;
+		else if (state_ptr->a [0] > a1ul)
+			state_ptr->a [0] = a1ul ;
+
+		/* UPB : update predictor zeros b [6] */
+		for (cnt = 0 ; cnt < 6 ; cnt++)
+		{	if (code_size == 5)		/* for 40Kbps G.723 */
+				state_ptr->b [cnt] -= state_ptr->b [cnt] >> 9 ;
+			else			/* for G.721 and 24Kbps G.723 */
+				state_ptr->b [cnt] -= state_ptr->b [cnt] >> 8 ;
+			if (dq & 0x7FFF)			/* XOR */
+			{	if ((dq ^ state_ptr->dq [cnt]) >= 0)
+					state_ptr->b [cnt] += 128 ;
+				else
+					state_ptr->b [cnt] -= 128 ;
+				}
+			}
+		}
+
+	for (cnt = 5 ; cnt > 0 ; cnt--)
+		state_ptr->dq [cnt] = state_ptr->dq [cnt - 1] ;
+	/* FLOAT A : convert dq [0] to 4-bit exp, 6-bit mantissa f.p. */
+	if (mag == 0)
+		state_ptr->dq [0] = (dq >= 0) ? 0x20 : 0xFC20 ;
+	else
+	{	expon = quan (mag, power2, 15) ;
+		state_ptr->dq [0] = (dq >= 0) ?
+			(expon << 6) + ((mag << 6) >> expon) :
+			(expon << 6) + ((mag << 6) >> expon) - 0x400 ;
+		}
+
+	state_ptr->sr [1] = state_ptr->sr [0] ;
+	/* FLOAT B : convert sr to 4-bit exp., 6-bit mantissa f.p. */
+	if (sr == 0)
+		state_ptr->sr [0] = 0x20 ;
+	else if (sr > 0)
+	{	expon = quan (sr, power2, 15) ;
+		state_ptr->sr [0] = (expon << 6) + ((sr << 6) >> expon) ;
+		}
+	else if (sr > -32768)
+	{	mag = -sr ;
+		expon = quan (mag, power2, 15) ;
+		state_ptr->sr [0] = (expon << 6) + ((mag << 6) >> expon) - 0x400 ;
+		}
+	else
+		state_ptr->sr [0] = (short) 0xFC20 ;
+
+	/* DELAY A */
+	state_ptr->pk [1] = state_ptr->pk [0] ;
+	state_ptr->pk [0] = pk0 ;
+
+	/* TONE */
+	if (tr == 1)		/* this sample has been treated as data */
+		state_ptr->td = 0 ;	/* next one will be treated as voice */
+	else if (a2p < -11776)	/* small sample-to-sample correlation */
+		state_ptr->td = 1 ;	/* signal may be data */
+	else				/* signal is voice */
+		state_ptr->td = 0 ;
+
+	/*
+	 * Adaptation speed control.
+	 */
+	state_ptr->dms += (fi - state_ptr->dms) >> 5 ;		/* FILTA */
+	state_ptr->dml += (((fi << 2) - state_ptr->dml) >> 7) ;	/* FILTB */
+
+	if (tr == 1)
+		state_ptr->ap = 256 ;
+	else if (y < 1536)					/* SUBTC */
+		state_ptr->ap += (0x200 - state_ptr->ap) >> 4 ;
+	else if (state_ptr->td == 1)
+		state_ptr->ap += (0x200 - state_ptr->ap) >> 4 ;
+	else if (abs ((state_ptr->dms << 2) - state_ptr->dml) >= (state_ptr->dml >> 3))
+		state_ptr->ap += (0x200 - state_ptr->ap) >> 4 ;
+	else
+		state_ptr->ap += (-state_ptr->ap) >> 4 ;
+
+	return ;
+} /* update */
+
+/*------------------------------------------------------------------------------
+*/
+
+static int
+unpack_bytes (int bits, int blocksize, const unsigned char * block, short * samples)
+{	unsigned int	in_buffer = 0 ;
+	unsigned char	in_byte ;
+	int				k, in_bits = 0, bindex = 0 ;
+
+	for (k = 0 ; bindex <= blocksize && k < G72x_BLOCK_SIZE ; k++)
+	{	if (in_bits < bits)
+		{	in_byte = block [bindex++] ;
+
+			in_buffer |= (in_byte << in_bits) ;
+			in_bits += 8 ;
+			}
+		samples [k] = in_buffer & ((1 << bits) - 1) ;
+		in_buffer >>= bits ;
+		in_bits -= bits ;
+		} ;
+
+	return k ;
+} /* unpack_bytes */
+
+static int
+pack_bytes (int bits, const short * samples, unsigned char * block)
+{
+	unsigned int	out_buffer = 0 ;
+	int				k, bindex = 0, out_bits = 0 ;
+	unsigned char	out_byte ;
+
+	for (k = 0 ; k < G72x_BLOCK_SIZE ; k++)
+	{	out_buffer |= (samples [k] << out_bits) ;
+		out_bits += bits ;
+		if (out_bits >= 8)
+		{	out_byte = out_buffer & 0xFF ;
+			out_bits -= 8 ;
+			out_buffer >>= 8 ;
+			block [bindex++] = out_byte ;
+			}
+		} ;
+
+	return bindex ;
+} /* pack_bytes */
+
diff --git a/src/G72x/g72x.h b/src/G72x/g72x.h
new file mode 100644
index 0000000..d7631e6
--- /dev/null
+++ b/src/G72x/g72x.h
@@ -0,0 +1,91 @@
+/*
+** Copyright (C) 1999-2011 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*
+** This file is not the same as the original file from Sun Microsystems. Nearly
+** all the original definitions and function prototypes that were in the file
+** of this name have been moved to g72x_priv.h.
+*/
+
+#ifndef G72X_HEADER_FILE
+#define	G72X_HEADER_FILE
+
+/*
+** Number of samples per block to process.
+** Must be a common multiple of possible bits per sample : 2, 3, 4, 5 and 8.
+*/
+#define	G72x_BLOCK_SIZE		(3 * 5 * 8)
+
+/*
+**	Identifiers for the differing kinds of G72x ADPCM codecs.
+**	The identifiers also define the number of encoded bits per sample.
+*/
+
+enum
+{	G723_16_BITS_PER_SAMPLE = 2,
+	G723_24_BITS_PER_SAMPLE = 3,
+	G723_40_BITS_PER_SAMPLE = 5,
+
+	G721_32_BITS_PER_SAMPLE = 4,
+	G721_40_BITS_PER_SAMPLE = 5,
+
+	G723_16_SAMPLES_PER_BLOCK = G72x_BLOCK_SIZE,
+	G723_24_SAMPLES_PER_BLOCK = G723_24_BITS_PER_SAMPLE * (G72x_BLOCK_SIZE / G723_24_BITS_PER_SAMPLE),
+	G723_40_SAMPLES_PER_BLOCK = G723_40_BITS_PER_SAMPLE * (G72x_BLOCK_SIZE / G723_40_BITS_PER_SAMPLE),
+
+	G721_32_SAMPLES_PER_BLOCK = G72x_BLOCK_SIZE,
+	G721_40_SAMPLES_PER_BLOCK = G721_40_BITS_PER_SAMPLE * (G72x_BLOCK_SIZE / G721_40_BITS_PER_SAMPLE),
+
+	G723_16_BYTES_PER_BLOCK = (G723_16_BITS_PER_SAMPLE * G72x_BLOCK_SIZE) / 8,
+	G723_24_BYTES_PER_BLOCK = (G723_24_BITS_PER_SAMPLE * G72x_BLOCK_SIZE) / 8,
+	G723_40_BYTES_PER_BLOCK = (G723_40_BITS_PER_SAMPLE * G72x_BLOCK_SIZE) / 8,
+
+	G721_32_BYTES_PER_BLOCK = (G721_32_BITS_PER_SAMPLE * G72x_BLOCK_SIZE) / 8,
+	G721_40_BYTES_PER_BLOCK = (G721_40_BITS_PER_SAMPLE * G72x_BLOCK_SIZE) / 8
+} ;
+
+/* Forward declaration of of g72x_state. */
+
+struct g72x_state ;
+
+/* External function definitions. */
+
+struct g72x_state * g72x_reader_init (int codec, int *blocksize, int *samplesperblock) ;
+struct g72x_state * g72x_writer_init (int codec, int *blocksize, int *samplesperblock) ;
+/*
+**	Initialize the ADPCM state table for the given codec.
+**	Return 0 on success, 1 on fail.
+*/
+
+int g72x_decode_block (struct g72x_state *pstate, const unsigned char *block, short *samples) ;
+/*
+**	The caller fills data->block with data->bytes bytes before calling the
+**	function. The value data->bytes must be an integer multiple of
+**	data->blocksize and be <= data->max_bytes.
+**	When it returns, the caller can read out data->samples samples.
+*/
+
+int g72x_encode_block (struct g72x_state *pstate, short *samples, unsigned char *block) ;
+/*
+**	The caller fills state->samples some integer multiple data->samples_per_block
+**	(up to G72x_BLOCK_SIZE) samples before calling the function.
+**	When it returns, the caller can read out bytes encoded bytes.
+*/
+
+#endif /* !G72X_HEADER_FILE */
+
diff --git a/src/G72x/g72x_priv.h b/src/G72x/g72x_priv.h
new file mode 100644
index 0000000..6a194ec
--- /dev/null
+++ b/src/G72x/g72x_priv.h
@@ -0,0 +1,129 @@
+/*
+ * This source code is a product of Sun Microsystems, Inc. and is provided
+ * for unrestricted use.  Users may copy or modify this source code without
+ * charge.
+ *
+ * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
+ * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun source code is provided with no support and without any obligation on
+ * the part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+
+#ifndef G72X_PRIVATE_H
+#define G72X_PRIVATE_H
+
+#ifdef __cplusplus
+#error "This code is not designed to be compiled with a C++ compiler."
+#endif
+
+/*
+** The following is the definition of the state structure used by the
+** G.721/G.723 encoder and decoder to preserve their internal state
+** between successive calls.  The meanings of the majority of the state
+** structure fields are explained in detail in the CCITT Recommendation
+** G.721.  The field names are essentially identical to variable names
+** in the bit level description of the coding algorithm included in this
+** Recommendation.
+*/
+
+struct g72x_state
+{	long yl ;	/* Locked or steady state step size multiplier. */
+	short yu ;	/* Unlocked or non-steady state step size multiplier. */
+	short dms ;	/* Short term energy estimate. */
+	short dml ;	/* Long term energy estimate. */
+	short ap ;	/* Linear weighting coefficient of 'yl' and 'yu'. */
+
+	short a [2] ;	/* Coefficients of pole portion of prediction filter. */
+	short b [6] ;	/* Coefficients of zero portion of prediction filter. */
+	short pk [2] ;	/*
+					** Signs of previous two samples of a partially
+					** reconstructed signal.
+					**/
+	short dq [6] ;	/*
+					** Previous 6 samples of the quantized difference
+					** signal represented in an internal floating point
+					** format.
+					**/
+	short sr [2] ;	/*
+			 		** Previous 2 samples of the quantized difference
+					** signal represented in an internal floating point
+					** format.
+					*/
+	char td ;	/* delayed tone detect, new in 1988 version */
+
+	/*	The following struct members were added for libsndfile. The original
+	**	code worked by calling a set of functions on a sample by sample basis
+	**	which is slow on architectures like Intel x86. For libsndfile, this
+	**	was changed so that the encoding and decoding routines could work on
+	**	a block of samples at a time to reduce the function call overhead.
+	*/
+	int		(*encoder) (int, struct g72x_state* state) ;
+	int		(*decoder) (int, struct g72x_state* state) ;
+
+	int		codec_bits, blocksize, samplesperblock ;
+} ;
+
+typedef struct g72x_state G72x_STATE ;
+
+int	predictor_zero (G72x_STATE *state_ptr) ;
+
+int	predictor_pole (G72x_STATE *state_ptr) ;
+
+int	step_size (G72x_STATE *state_ptr) ;
+
+int	quantize (int d, int	y, short *table, int size) ;
+
+int	reconstruct (int sign, int dqln,	int y) ;
+
+void update (int code_size, int y, int wi, int fi, int dq, int sr, int dqsez, G72x_STATE *state_ptr) ;
+
+int g721_encoder	(int sample, G72x_STATE *state_ptr) ;
+int g721_decoder	(int code, G72x_STATE *state_ptr) ;
+
+int g723_16_encoder	(int sample, G72x_STATE *state_ptr) ;
+int g723_16_decoder	(int code, G72x_STATE *state_ptr) ;
+
+int g723_24_encoder	(int sample, G72x_STATE *state_ptr) ;
+int g723_24_decoder	(int code, G72x_STATE *state_ptr) ;
+
+int g723_40_encoder	(int sample, G72x_STATE *state_ptr) ;
+int g723_40_decoder	(int code, G72x_STATE *state_ptr) ;
+
+void private_init_state (G72x_STATE *state_ptr) ;
+
+#if __GNUC__
+#define ALWAYS_INLINE		__attribute__ ((always_inline))
+#elif defined _MSC_VER
+#define ALWAYS_INLINE		__forceinline
+#else
+#define ALWAYS_INLINE
+#endif
+
+static inline int ALWAYS_INLINE
+arith_shift_left (int x, int shift)
+{	return (int) (((unsigned int) x) << shift) ;
+} /* arith_shift_left */
+
+static inline int ALWAYS_INLINE
+arith_shift_right (int x, int shift)
+{	if (x >= 0)
+		return x << shift ;
+	return ~ ((~x) << shift) ;
+} /* arith_shift_right */
+
+#endif /* G72X_PRIVATE_H */
diff --git a/src/GSM610/COPYRIGHT b/src/GSM610/COPYRIGHT
new file mode 100644
index 0000000..eba0e52
--- /dev/null
+++ b/src/GSM610/COPYRIGHT
@@ -0,0 +1,16 @@
+Copyright 1992, 1993, 1994 by Jutta Degener and Carsten Bormann,
+Technische Universitaet Berlin
+
+Any use of this software is permitted provided that this notice is not
+removed and that neither the authors nor the Technische Universitaet Berlin
+are deemed to have made any representations as to the suitability of this
+software for any purpose nor are held responsible for any defects of
+this software.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+
+As a matter of courtesy, the authors request to be informed about uses
+this software has found, about bugs in this software, and about any
+improvements that may be of general interest.
+
+Berlin, 28.11.1994
+Jutta Degener
+Carsten Bormann
diff --git a/src/GSM610/ChangeLog b/src/GSM610/ChangeLog
new file mode 100644
index 0000000..24f5248
--- /dev/null
+++ b/src/GSM610/ChangeLog
@@ -0,0 +1,56 @@
+2004-05-12  Erik de Castro Lopo  <erikd AT mega-nerd DOT com>
+
+    * gsm610_priv.h
+    Replace ugly macros with inline functions.
+
+    * *.c
+    Remove temporary variables used by macros and other minor fixes required by
+    above change.
+
+2003-06-02  Erik de Castro Lopo  <erikd AT mega-nerd DOT com>
+
+    * rpe.c
+	Renamed variables "exp" to "expon" to avoid shadowed parameter warnigns.
+
+2002-06-08  Erik de Castro Lopo  <erikd AT mega-nerd DOT com>
+
+    * long_term.c
+	Changes tp removed compiler warnings about shadowed parameters.
+
+2002-06-08  Erik de Castro Lopo  <erikd AT mega-nerd DOT com>
+
+    * private.h
+	Made declarations of gsm_A, gsm_B, gsm_MIC etc extern. This fixed a compile
+	problem on MacOSX.
+
+2002-05-10  Erik de Castro Lopo  <erikd AT mega-nerd DOT com>
+
+	* *.[ch]
+	Removed all pre-ANSI prototype kludges. Removed proto.h and unproto.h. 
+	Started work on making GSM 6.10 files seekable. Currently they are not.
+
+    * code.c private.h
+	Function Gsm_Coder () used a statically defined array. This was obviously
+	not re-entrant so moved it to struct gsm_state.
+
+2001-09-16  Erik de Castro Lopo  <erikd AT mega-nerd DOT com>
+
+    * code.c
+    Added #includes for string.h and stdlib.h.
+
+2000-10-27  Erik de Castro Lopo  <erikd AT mega-nerd DOT com>
+
+    * config.h
+    Removed some commented out #defines (ie //*efine) which were causing problems on
+	the Sun cc compiler.
+
+2000-02-29  Erik de Castro Lopo  <erikd AT mega-nerd DOT com>
+
+    * private.h
+    Added #defines to emulate normal compile time options.
+
+2000-02-28  Erik de Castro Lopo  <erikd AT mega-nerd DOT com>
+
+    * everthing
+    Created this directory and copied files from libgsm.
+    http://kbs.cs.tu-berlin.de/~jutta/toast.html
diff --git a/src/GSM610/README b/src/GSM610/README
new file mode 100644
index 0000000..b57132b
--- /dev/null
+++ b/src/GSM610/README
@@ -0,0 +1,36 @@
+GSM 06.10 13 kbit/s RPE/LTP speech codec
+----------------------------------------
+
+All the file in this directory were written by Jutta Degener
+and Carsten Borman for The Communications and Operating Systems 
+Research Group (KBS) at the Technische Universitaet Berlin.
+
+Their work was released under the following license which is 
+assumed to be compatible with The GNU Lesser General Public License.
+
+----------------------------------------------------------------------------
+
+Copyright 1992, 1993, 1994 by Jutta Degener and Carsten Bormann,
+Technische Universitaet Berlin
+
+Any use of this software is permitted provided that this notice is not
+removed and that neither the authors nor the Technische Universitaet Berlin
+are deemed to have made any representations as to the suitability of this
+software for any purpose nor are held responsible for any defects of
+this software.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+
+As a matter of courtesy, the authors request to be informed about uses
+this software has found, about bugs in this software, and about any
+improvements that may be of general interest.
+
+Berlin, 28.11.1994
+Jutta Degener (jutta@cs.tu-berlin.de)
+Carsten Bormann (cabo@cs.tu-berlin.de)
+
+----------------------------------------------------------------------------
+
+Jutta Degener and Carsten Bormann's work can be found on their homepage
+at:
+
+    http://kbs.cs.tu-berlin.de/~jutta/toast.html
+	
diff --git a/src/GSM610/add.c b/src/GSM610/add.c
new file mode 100644
index 0000000..c943eec
--- /dev/null
+++ b/src/GSM610/add.c
@@ -0,0 +1,243 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin.  See the accompanying file "COPYRIGHT" for
+ * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/*
+ *  See private.h for the more commonly used macro versions.
+ */
+
+#include	<stdio.h>
+#include	<assert.h>
+
+#include	"gsm610_priv.h"
+
+#define	saturate(x) 	\
+	((x) < MIN_WORD ? MIN_WORD : (x) > MAX_WORD ? MAX_WORD: (x))
+
+int16_t gsm_add (int16_t a, int16_t b)
+{
+	int32_t sum = (int32_t) a + (int32_t) b ;
+	return saturate (sum) ;
+}
+
+int16_t gsm_sub (int16_t a, int16_t b)
+{
+	int32_t diff = (int32_t) a - (int32_t) b ;
+	return saturate (diff) ;
+}
+
+int16_t gsm_mult (int16_t a, int16_t b)
+{
+	if (a == MIN_WORD && b == MIN_WORD)
+		return MAX_WORD ;
+
+	return SASR_L ((int32_t) a * (int32_t) b, 15) ;
+}
+
+int16_t gsm_mult_r (int16_t a, int16_t b)
+{
+	if (b == MIN_WORD && a == MIN_WORD)
+		return MAX_WORD ;
+	else
+	{	int32_t prod = (int32_t) a * (int32_t) b + 16384 ;
+		prod >>= 15 ;
+		return prod & 0xFFFF ;
+		}
+}
+
+int16_t gsm_abs (int16_t a)
+{
+	return a < 0 ? (a == MIN_WORD ? MAX_WORD : -a) : a ;
+}
+
+int32_t gsm_L_mult (int16_t a, int16_t b)
+{
+	assert (a != MIN_WORD || b != MIN_WORD) ;
+	return ((int32_t) a * (int32_t) b) << 1 ;
+}
+
+int32_t gsm_L_add (int32_t a, int32_t b)
+{
+	if (a < 0)
+	{	if (b >= 0)
+			return a + b ;
+		else
+		{	uint32_t A = (uint32_t) - (a + 1) + (uint32_t) - (b + 1) ;
+			return A >= MAX_LONGWORD ? MIN_LONGWORD : - (int32_t) A - 2 ;
+			}
+		}
+	else if (b <= 0)
+		return a + b ;
+	else
+	{	uint32_t A = (uint32_t) a + (uint32_t) b ;
+		return A > MAX_LONGWORD ? MAX_LONGWORD : A ;
+		}
+}
+
+int32_t gsm_L_sub (int32_t a, int32_t b)
+{
+	if (a >= 0)
+	{	if (b >= 0)
+			return a - b ;
+		else
+		{	/* a>=0, b<0 */
+			uint32_t A = (uint32_t) a + - (b + 1) ;
+			return A >= MAX_LONGWORD ? MAX_LONGWORD : (A + 1) ;
+			}
+		}
+	else if (b <= 0)
+		return a - b ;
+	else
+	{	/* a<0, b>0 */
+		uint32_t A = (uint32_t) - (a + 1) + b ;
+		return A >= MAX_LONGWORD ? MIN_LONGWORD : - (int32_t) A - 1 ;
+		}
+}
+
+static unsigned char const bitoff [256] = {
+	8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
+	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+	2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+	2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+} ;
+
+int16_t gsm_norm (int32_t a)
+/*
+ * the number of left shifts needed to normalize the 32 bit
+ * variable L_var1 for positive values on the interval
+ *
+ * with minimum of
+ * minimum of 1073741824  (01000000000000000000000000000000) and
+ * maximum of 2147483647  (01111111111111111111111111111111)
+ *
+ *
+ * and for negative values on the interval with
+ * minimum of -2147483648 (-10000000000000000000000000000000) and
+ * maximum of -1073741824 (-1000000000000000000000000000000).
+ *
+ * in order to normalize the result, the following
+ * operation must be done: L_norm_var1 = L_var1 << norm (L_var1) ;
+ *
+ * (That's 'ffs', only from the left, not the right..)
+ */
+{
+	assert (a != 0) ;
+
+	if (a < 0)
+	{	if (a <= -1073741824) return 0 ;
+		a = ~a ;
+		}
+
+	return a & 0xffff0000
+		? (a & 0xff000000
+			? -1 + bitoff [0xFF & (a >> 24)]
+			: 7 + bitoff [0xFF & (a >> 16)])
+		: (a & 0xff00
+			? 15 + bitoff [0xFF & (a >> 8)]
+			: 23 + bitoff [0xFF & a]) ;
+}
+
+int32_t gsm_L_asl (int32_t a, int n)
+{
+	if (n >= 32) return 0 ;
+	if (n <= -32) return - (a < 0) ;
+	if (n < 0) return gsm_L_asr (a, -n) ;
+	return a << n ;
+}
+
+int16_t gsm_asr (int16_t a, int n)
+{
+	if (n >= 16) return - (a < 0) ;
+	if (n <= -16) return 0 ;
+	if (n < 0) return a << -n ;
+
+	return SASR_W (a, (int16_t) n) ;
+}
+
+int16_t gsm_asl (int16_t a, int n)
+{
+	if (n >= 16) return 0 ;
+	if (n <= -16) return - (a < 0) ;
+	if (n < 0) return gsm_asr (a, -n) ;
+	return a << n ;
+}
+
+int32_t gsm_L_asr (int32_t a, int n)
+{
+	if (n >= 32) return - (a < 0) ;
+	if (n <= -32) return 0 ;
+	if (n < 0) return a << -n ;
+
+	return SASR_L (a, (int16_t) n) ;
+}
+
+/*
+**	int16_t gsm_asr (int16_t a, int n)
+**	{
+**		if (n >= 16) return - (a < 0) ;
+**		if (n <= -16) return 0 ;
+**		if (n < 0) return a << -n ;
+**
+**	#	ifdef	SASR_W
+**			return a >> n ;
+**	#	else
+**			if (a >= 0) return a >> n ;
+**			else return - (int16_t) (- (uint16_t)a >> n) ;
+**	#	endif
+**	}
+**
+*/
+/*
+ *  (From p. 46, end of section 4.2.5)
+ *
+ *  NOTE: The following lines gives [sic] one correct implementation
+ *  	 of the div (num, denum) arithmetic operation.  Compute div
+ *        which is the integer division of num by denum: with denum
+ *	 >= num > 0
+ */
+
+int16_t gsm_div (int16_t num, int16_t denum)
+{
+	int32_t	L_num = num ;
+	int32_t	L_denum = denum ;
+	int16_t		div = 0 ;
+	int			k = 15 ;
+
+	/* The parameter num sometimes becomes zero.
+	* Although this is explicitly guarded against in 4.2.5,
+	* we assume that the result should then be zero as well.
+	*/
+
+	/* assert (num != 0) ; */
+
+	assert (num >= 0 && denum >= num) ;
+	if (num == 0)
+		return 0 ;
+
+	while (k--)
+	{	div <<= 1 ;
+		L_num <<= 1 ;
+
+		if (L_num >= L_denum)
+		{	L_num -= L_denum ;
+			div++ ;
+			}
+		}
+
+	return div ;
+}
+
diff --git a/src/GSM610/code.c b/src/GSM610/code.c
new file mode 100644
index 0000000..991913b
--- /dev/null
+++ b/src/GSM610/code.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin.  See the accompanying file "COPYRIGHT" for
+ * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+
+#include	<stdlib.h>
+#include	<string.h>
+
+#include	"gsm610_priv.h"
+
+/*
+ *  4.2 FIXED POINT IMPLEMENTATION OF THE RPE-LTP CODER
+ */
+
+void Gsm_Coder (
+
+	struct gsm_state	* State,
+
+	int16_t	* s,	/* [0..159] samples		  	IN	*/
+
+/*
+ * The RPE-LTD coder works on a frame by frame basis.  The length of
+ * the frame is equal to 160 samples.  Some computations are done
+ * once per frame to produce at the output of the coder the
+ * LARc [1..8] parameters which are the coded LAR coefficients and
+ * also to realize the inverse filtering operation for the entire
+ * frame (160 samples of signal d [0..159]).  These parts produce at
+ * the output of the coder:
+ */
+
+	int16_t	* LARc,	/* [0..7] LAR coefficients		OUT	*/
+
+/*
+ * Procedure 4.2.11 to 4.2.18 are to be executed four times per
+ * frame.  That means once for each sub-segment RPE-LTP analysis of
+ * 40 samples.  These parts produce at the output of the coder:
+ */
+
+	int16_t	*Nc,	/* [0..3] LTP lag			OUT 	*/
+	int16_t	*bc,	/* [0..3] coded LTP gain		OUT 	*/
+	int16_t	*Mc,	/* [0..3] RPE grid selection		OUT     */
+	int16_t	*xmaxc,	/* [0..3] Coded maximum amplitude	OUT	*/
+	int16_t	*xMc	/* [13*4] normalized RPE samples	OUT	*/
+)
+{
+	int	k ;
+	int16_t	*dp = State->dp0 + 120 ;	/* [-120...-1] */
+	int16_t	*dpp = dp ;					/* [0...39]	 */
+
+	int16_t	so [160] ;
+
+	Gsm_Preprocess (State, s, so) ;
+	Gsm_LPC_Analysis (State, so, LARc) ;
+	Gsm_Short_Term_Analysis_Filter (State, LARc, so) ;
+
+	for (k = 0 ; k <= 3 ; k++, xMc += 13)
+	{	Gsm_Long_Term_Predictor	(State,
+					so+k*40,		/* d      [0..39] IN	*/
+					dp,				/* dp  [-120..-1] IN	*/
+					State->e + 5,	/* e      [0..39] OUT	*/
+					dpp,			/* dpp    [0..39] OUT */
+					Nc++,
+					bc++) ;
+
+		Gsm_RPE_Encoding (/*-S,-*/
+					State->e + 5,	/* e	 ][0..39][IN/OUT */
+					xmaxc++, Mc++, xMc) ;
+		/*
+		 * Gsm_Update_of_reconstructed_short_time_residual_signal
+		 *			(dpp, State->e + 5, dp) ;
+		 */
+
+		{
+			register int i ;
+			for (i = 0 ; i <= 39 ; i++)
+				dp [i] = GSM_ADD (State->e [5 + i], dpp [i]) ;
+		}
+		dp += 40 ;
+		dpp += 40 ;
+
+	}
+	memcpy ((char *) State->dp0, (char *) (State->dp0 + 160),
+		120 * sizeof (*State->dp0)) ;
+}
+
diff --git a/src/GSM610/config.h b/src/GSM610/config.h
new file mode 100644
index 0000000..f3eeb82
--- /dev/null
+++ b/src/GSM610/config.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin.  See the accompanying file "COPYRIGHT" for
+ * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+#ifndef	CONFIG_H
+#define	CONFIG_H
+
+#define	HAS_STDLIB_H	1		/* /usr/include/stdlib.h	*/
+#define	HAS_FCNTL_H	1		/* /usr/include/fcntl.h		*/
+
+#define	HAS_FSTAT 	1		/* fstat syscall		*/
+#define	HAS_FCHMOD 	1		/* fchmod syscall		*/
+#define	HAS_CHMOD 	1		/* chmod syscall		*/
+#define	HAS_FCHOWN 	1		/* fchown syscall		*/
+#define	HAS_CHOWN 	1		/* chown syscall		*/
+
+#define	HAS_STRING_H 	1		/* /usr/include/string.h 	*/
+
+#define	HAS_UNISTD_H	1		/* /usr/include/unistd.h	*/
+#define	HAS_UTIME	1		/* POSIX utime(path, times)	*/
+#define	HAS_UTIME_H	1		/* UTIME header file		*/
+
+#endif	/* CONFIG_H */
+
diff --git a/src/GSM610/decode.c b/src/GSM610/decode.c
new file mode 100644
index 0000000..2a0712f
--- /dev/null
+++ b/src/GSM610/decode.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin.  See the accompanying file "COPYRIGHT" for
+ * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+
+#include	"gsm610_priv.h"
+
+/*
+ *  4.3 FIXED POINT IMPLEMENTATION OF THE RPE-LTP DECODER
+ */
+
+static void Postprocessing (
+	struct gsm_state	* S,
+	register int16_t		* s)
+{
+	register int		k ;
+	register int16_t		msr = S->msr ;
+	register int16_t		tmp ;
+
+	for (k = 160 ; k-- ; s++)
+	{	tmp = GSM_MULT_R (msr, 28180) ;
+		msr = GSM_ADD (*s, tmp) ;			/* Deemphasis 	     */
+		*s = GSM_ADD (msr, msr) & 0xFFF8 ;	/* Truncation & Upscaling */
+		}
+	S->msr = msr ;
+}
+
+void Gsm_Decoder (
+	struct gsm_state	* S,
+
+	int16_t		* LARcr,	/* [0..7]		IN	*/
+
+	int16_t		* Ncr,		/* [0..3] 		IN 	*/
+	int16_t		* bcr,		/* [0..3]		IN	*/
+	int16_t		* Mcr,		/* [0..3] 		IN 	*/
+	int16_t		* xmaxcr,	/* [0..3]		IN 	*/
+	int16_t		* xMcr,		/* [0..13*4]		IN	*/
+
+	int16_t		* s)		/* [0..159]		OUT 	*/
+{
+	int		j, k ;
+	int16_t		erp [40], wt [160] ;
+	int16_t		*drp = S->dp0 + 120 ;
+
+	for (j = 0 ; j <= 3 ; j++, xmaxcr++, bcr++, Ncr++, Mcr++, xMcr += 13)
+	{	Gsm_RPE_Decoding (/*-S,-*/ *xmaxcr, *Mcr, xMcr, erp) ;
+		Gsm_Long_Term_Synthesis_Filtering (S, *Ncr, *bcr, erp, drp) ;
+
+		for (k = 0 ; k <= 39 ; k++) wt [j * 40 + k] = drp [k] ;
+		}
+
+	Gsm_Short_Term_Synthesis_Filter (S, LARcr, wt, s) ;
+	Postprocessing (S, s) ;
+}
+
diff --git a/src/GSM610/gsm.h b/src/GSM610/gsm.h
new file mode 100644
index 0000000..6506a65
--- /dev/null
+++ b/src/GSM610/gsm.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin.  See the accompanying file "COPYRIGHT" for
+ * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+#ifndef	GSM_H
+#define	GSM_H
+
+#include	<stdio.h>		/* for FILE * 	*/
+
+/*
+ *	Interface
+ */
+
+typedef struct gsm_state * 	gsm ;
+typedef short				gsm_signal ;		/* signed 16 bit */
+typedef unsigned char		gsm_byte ;
+typedef gsm_byte 			gsm_frame [33] ;	/* 33 * 8 bits	 */
+
+#define GSM_MAGIC			0xD			/* 13 kbit/s RPE-LTP */
+
+#define	GSM_PATCHLEVEL		10
+#define	GSM_MINOR			0
+#define	GSM_MAJOR			1
+
+#define	GSM_OPT_VERBOSE		1
+#define	GSM_OPT_FAST		2
+#define	GSM_OPT_LTP_CUT		3
+#define	GSM_OPT_WAV49		4
+#define	GSM_OPT_FRAME_INDEX	5
+#define	GSM_OPT_FRAME_CHAIN	6
+
+gsm gsm_create (void) ;
+
+/* Added for libsndfile : May 6, 2002 */
+void gsm_init (gsm) ;
+
+void gsm_destroy (gsm) ;
+
+int gsm_print	(FILE *, gsm, gsm_byte *) ;
+int gsm_option	(gsm, int, int *) ;
+
+void gsm_encode	(gsm, gsm_signal *, gsm_byte *) ;
+int gsm_decode	(gsm, gsm_byte *, gsm_signal *) ;
+
+int gsm_explode (gsm, gsm_byte *, gsm_signal *) ;
+void gsm_implode (gsm, gsm_signal *, gsm_byte *) ;
+
+#endif	/* GSM_H */
+
+
diff --git a/src/GSM610/gsm610_priv.h b/src/GSM610/gsm610_priv.h
new file mode 100644
index 0000000..7ffaddc
--- /dev/null
+++ b/src/GSM610/gsm610_priv.h
@@ -0,0 +1,337 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin.  See the accompanying file "COPYRIGHT" for
+ * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+#ifndef	PRIVATE_H
+#define	PRIVATE_H
+
+#include <stdint.h>
+
+/* Added by Erik de Castro Lopo */
+#define	USE_FLOAT_MUL
+#define	FAST
+#define	WAV49
+
+#ifdef __cplusplus
+#error "This code is not designed to be compiled with a C++ compiler."
+#endif
+/* Added by Erik de Castro Lopo */
+
+struct gsm_state
+{	int16_t			dp0 [280] ;
+
+	int16_t			z1 ;			/* preprocessing.c, Offset_com. */
+	int32_t		L_z2 ;			/*                  Offset_com. */
+	int				mp ;			/*                  Preemphasis	*/
+
+	int16_t			u [8] ;			/* short_term_aly_filter.c	*/
+	int16_t			LARpp [2][8] ; 	/*                              */
+	int16_t			j ;				/*                              */
+
+	int16_t			ltp_cut ;		/* long_term.c, LTP crosscorr.  */
+	int16_t			nrp ; 			/* 40 */	/* long_term.c, synthesis	*/
+	int16_t			v [9] ;			/* short_term.c, synthesis	*/
+	int16_t			msr ;			/* decoder.c,	Postprocessing	*/
+
+	char			verbose ;		/* only used if !NDEBUG		*/
+	char			fast ;			/* only used if FAST		*/
+
+	char			wav_fmt ;		/* only used if WAV49 defined	*/
+	unsigned char	frame_index ;	/*            odd/even chaining	*/
+	unsigned char	frame_chain ;	/*   half-byte to carry forward	*/
+
+	/* Moved here from code.c where it was defined as static */
+	int16_t e [50] ;
+} ;
+
+typedef struct gsm_state GSM_STATE ;
+
+#define	MIN_WORD	(-32767 - 1)
+#define	MAX_WORD	32767
+
+#define	MIN_LONGWORD	(-2147483647 - 1)
+#define	MAX_LONGWORD	2147483647
+
+/* Signed arithmetic shift right. */
+static inline int16_t
+SASR_W (int16_t x, int16_t by)
+{	if (x >= 0)
+		return x >> by ;
+	return ~ ((~x) >> by) ;
+} /* SASR_W */
+
+static inline int32_t
+SASR_L (int32_t x, int16_t by)
+{	if (x >= 0)
+		return x >> by ;
+	return ~ ((~x) >> by) ;
+} /* SASR_L */
+
+/* Signed arithmetic shift left. */
+static inline int16_t
+SASL_W (int16_t x, int16_t by)
+{	if (x >= 0)
+		return x << by ;
+	return - ((-x) << by) ;
+} /* SASR_W */
+
+static inline int32_t
+SASL_L (int32_t x, int16_t by)
+{	if (x >= 0)
+		return x << by ;
+	return - ((-x) << by) ;
+} /* SASR_L */
+
+/*
+ *	Prototypes from add.c
+ */
+int16_t	gsm_mult 		(int16_t a, int16_t b) ;
+int32_t gsm_L_mult 	(int16_t a, int16_t b) ;
+int16_t	gsm_mult_r		(int16_t a, int16_t b) ;
+
+int16_t	gsm_div			(int16_t num, int16_t denum) ;
+
+int16_t	gsm_add			(int16_t a, int16_t b) ;
+int32_t gsm_L_add		(int32_t a, int32_t b) ;
+
+int16_t	gsm_sub			(int16_t a, int16_t b) ;
+int32_t gsm_L_sub		(int32_t a, int32_t b) ;
+
+int16_t	gsm_abs			(int16_t a) ;
+
+int16_t	gsm_norm		(int32_t a) ;
+
+int32_t gsm_L_asl		(int32_t a, int n) ;
+int16_t	gsm_asl			(int16_t a, int n) ;
+
+int32_t gsm_L_asr		(int32_t a, int n) ;
+int16_t	gsm_asr			(int16_t a, int n) ;
+
+/*
+ *  Inlined functions from add.h
+ */
+
+static inline int32_t
+GSM_MULT_R (int16_t a, int16_t b)
+{	return (((int32_t) (a)) * ((int32_t) (b)) + 16384) >> 15 ;
+} /* GSM_MULT_R */
+
+static inline int32_t
+GSM_MULT (int16_t a, int16_t b)
+{	return (((int32_t) (a)) * ((int32_t) (b))) >> 15 ;
+} /* GSM_MULT */
+
+static inline int32_t
+GSM_L_MULT (int16_t a, int16_t b)
+{	return ((int32_t) (a)) * ((int32_t) (b)) << 1 ;
+} /* GSM_L_MULT */
+
+static inline int32_t
+GSM_L_ADD (int32_t a, int32_t b)
+{	uint32_t utmp ;
+
+	if (a < 0 && b < 0)
+	{	utmp = (uint32_t) - ((a) + 1) + (uint32_t) - ((b) + 1) ;
+		return (utmp >= (uint32_t) MAX_LONGWORD) ? MIN_LONGWORD : - (int32_t) utmp - 2 ;
+		} ;
+
+	if (a > 0 && b > 0)
+	{	utmp = (uint32_t) a + (uint32_t) b ;
+		return (utmp >= (uint32_t) MAX_LONGWORD) ? MAX_LONGWORD : utmp ;
+		} ;
+
+	return a + b ;
+} /* GSM_L_ADD */
+
+static inline int32_t
+GSM_ADD (int16_t a, int16_t b)
+{	int32_t ltmp ;
+
+	ltmp = ((int32_t) a) + ((int32_t) b) ;
+
+	if (ltmp >= MAX_WORD)
+		return MAX_WORD ;
+	if (ltmp <= MIN_WORD)
+		return MIN_WORD ;
+
+	return ltmp ;
+} /* GSM_ADD */
+
+static inline int32_t
+GSM_SUB (int16_t a, int16_t b)
+{	int32_t ltmp ;
+
+	ltmp = ((int32_t) a) - ((int32_t) b) ;
+
+	if (ltmp >= MAX_WORD)
+		ltmp = MAX_WORD ;
+	else if (ltmp <= MIN_WORD)
+		ltmp = MIN_WORD ;
+
+	return ltmp ;
+} /* GSM_SUB */
+
+static inline int16_t
+GSM_ABS (int16_t a)
+{
+	if (a > 0)
+		return a ;
+	if (a == MIN_WORD)
+		return MAX_WORD ;
+	return -a ;
+} /* GSM_ADD */
+
+
+/*
+ *  More prototypes from implementations..
+ */
+void Gsm_Coder (
+		struct gsm_state	* S,
+		int16_t	* s,		/* [0..159] samples		IN	*/
+		int16_t	* LARc,		/* [0..7] LAR coefficients	OUT	*/
+		int16_t	* Nc,		/* [0..3] LTP lag		OUT 	*/
+		int16_t	* bc,		/* [0..3] coded LTP gain	OUT 	*/
+		int16_t	* Mc,		/* [0..3] RPE grid selection	OUT     */
+		int16_t	* xmaxc,	/* [0..3] Coded maximum amplitude OUT	*/
+		int16_t	* xMc) ;	/* [13*4] normalized RPE samples OUT	*/
+
+void Gsm_Long_Term_Predictor (		/* 4x for 160 samples */
+		struct gsm_state * S,
+		int16_t	* d,	/* [0..39]   residual signal	IN	*/
+		int16_t	* dp,	/* [-120..-1] d'		IN	*/
+		int16_t	* e,	/* [0..40] 			OUT	*/
+		int16_t	* dpp,	/* [0..40] 			OUT	*/
+		int16_t	* Nc,	/* correlation lag		OUT	*/
+		int16_t	* bc) ;	/* gain factor			OUT	*/
+
+void Gsm_LPC_Analysis (
+		struct gsm_state * S,
+		int16_t * s,		/* 0..159 signals	IN/OUT	*/
+		int16_t * LARc) ;	/* 0..7   LARc's	OUT	*/
+
+void Gsm_Preprocess (
+		struct gsm_state * S,
+		int16_t * s, int16_t * so) ;
+
+void Gsm_Encoding (
+		struct gsm_state * S,
+		int16_t	* e,
+		int16_t	* ep,
+		int16_t	* xmaxc,
+		int16_t	* Mc,
+		int16_t	* xMc) ;
+
+void Gsm_Short_Term_Analysis_Filter (
+		struct gsm_state * S,
+		int16_t	* LARc,	/* coded log area ratio [0..7]  IN	*/
+		int16_t	* d) ;	/* st res. signal [0..159]	IN/OUT	*/
+
+void Gsm_Decoder (
+		struct gsm_state * S,
+		int16_t	* LARcr,	/* [0..7]		IN	*/
+		int16_t	* Ncr,		/* [0..3] 		IN 	*/
+		int16_t	* bcr,		/* [0..3]		IN	*/
+		int16_t	* Mcr,		/* [0..3] 		IN 	*/
+		int16_t	* xmaxcr,	/* [0..3]		IN 	*/
+		int16_t	* xMcr,		/* [0..13*4]		IN	*/
+		int16_t	* s) ;		/* [0..159]		OUT 	*/
+
+void Gsm_Decoding (
+		struct gsm_state * S,
+		int16_t 	xmaxcr,
+		int16_t	Mcr,
+		int16_t	* xMcr,		/* [0..12]		IN	*/
+		int16_t	* erp) ;	/* [0..39]		OUT 	*/
+
+void Gsm_Long_Term_Synthesis_Filtering (
+		struct gsm_state* S,
+		int16_t	Ncr,
+		int16_t	bcr,
+		int16_t	* erp,		/* [0..39]		  IN 	*/
+		int16_t	* drp) ;	/* [-120..-1] IN, [0..40] OUT 	*/
+
+void Gsm_RPE_Decoding (
+	/*-struct gsm_state *S,-*/
+		int16_t xmaxcr,
+		int16_t Mcr,
+		int16_t * xMcr,	/* [0..12], 3 bits             IN      */
+		int16_t * erp) ;	/* [0..39]                     OUT     */
+
+void Gsm_RPE_Encoding (
+		/*-struct gsm_state * S,-*/
+		int16_t	* e,			/* -5..-1][0..39][40..44     IN/OUT  */
+		int16_t	* xmaxc,		/*                              OUT */
+		int16_t	* Mc,			/*                              OUT */
+		int16_t	* xMc) ;		/* [0..12]                      OUT */
+
+void Gsm_Short_Term_Synthesis_Filter (
+		struct gsm_state * S,
+		int16_t	* LARcr, 	/* log area ratios [0..7]  IN	*/
+		int16_t	* drp,		/* received d [0...39]	   IN	*/
+		int16_t	* s) ;		/* signal   s [0..159]	  OUT	*/
+
+void Gsm_Update_of_reconstructed_short_time_residual_signal (
+		int16_t	* dpp,		/* [0...39]	IN	*/
+		int16_t	* ep,		/* [0...39]	IN	*/
+		int16_t	* dp) ;		/* [-120...-1]  IN/OUT 	*/
+
+/*
+ *  Tables from table.c
+ */
+#ifndef	GSM_TABLE_C
+
+extern int16_t gsm_A [8], gsm_B [8], gsm_MIC [8], gsm_MAC [8] ;
+extern int16_t gsm_INVA [8] ;
+extern int16_t gsm_DLB [4], gsm_QLB [4] ;
+extern int16_t gsm_H [11] ;
+extern int16_t gsm_NRFAC [8] ;
+extern int16_t gsm_FAC [8] ;
+
+#endif	/* GSM_TABLE_C */
+
+
+#if __GNUC__
+#define ALWAYS_INLINE		__attribute__ ((always_inline))
+#elif defined _MSC_VER
+#define ALWAYS_INLINE		__forceinline
+#else
+#define ALWAYS_INLINE
+#endif
+
+
+static inline int32_t ALWAYS_INLINE
+arith_shift_left (int32_t x, int shift)
+{	return (int32_t) (((uint32_t) x) << shift) ;
+} /* arith_shift_left */
+
+static inline int32_t ALWAYS_INLINE
+arith_shift_right (int32_t x, int shift)
+{	if (x >= 0)
+		return x << shift ;
+	return ~ ((~x) << shift) ;
+} /* arith_shift_right */
+
+
+/*
+ *  Debugging
+ */
+#ifdef NDEBUG
+
+#	define	gsm_debug_int16_ts(a, b, c, d)		/* nil */
+#	define	gsm_debug_int32_ts(a, b, c, d)		/* nil */
+#	define	gsm_debug_int16_t(a, b)			/* nil */
+#	define	gsm_debug_int32_t(a, b)		/* nil */
+
+#else	/* !NDEBUG => DEBUG */
+
+	void gsm_debug_int16_ts		(char * name, int, int, int16_t *) ;
+	void gsm_debug_int32_ts	(char * name, int, int, int32_t *) ;
+	void gsm_debug_int32_t		(char * name, int32_t) ;
+	void gsm_debug_int16_t			(char * name, int16_t) ;
+
+#endif /* !NDEBUG */
+
+#endif	/* PRIVATE_H */
+
diff --git a/src/GSM610/gsm_create.c b/src/GSM610/gsm_create.c
new file mode 100644
index 0000000..05425dd
--- /dev/null
+++ b/src/GSM610/gsm_create.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin.  See the accompanying file "COPYRIGHT" for
+ * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+#include	"config.h"
+
+#include	<stdio.h>
+#include	<stdlib.h>
+#include	<string.h>
+
+
+
+#include "gsm.h"
+#include "gsm610_priv.h"
+
+gsm gsm_create (void)
+{
+	gsm r ;
+
+	r = malloc (sizeof (struct gsm_state)) ;
+	if (!r) return r ;
+
+	memset ((char *) r, 0, sizeof (struct gsm_state)) ;
+	r->nrp = 40 ;
+
+	return r ;
+}
+
+/* Added for libsndfile : May 6, 2002. Not sure if it works. */
+void gsm_init (gsm state)
+{
+	memset (state, 0, sizeof (struct gsm_state)) ;
+	state->nrp = 40 ;
+}
+
diff --git a/src/GSM610/gsm_decode.c b/src/GSM610/gsm_decode.c
new file mode 100644
index 0000000..04411be
--- /dev/null
+++ b/src/GSM610/gsm_decode.c
@@ -0,0 +1,357 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin.  See the accompanying file "COPYRIGHT" for
+ * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+#include "gsm610_priv.h"
+
+#include "gsm.h"
+
+int gsm_decode (gsm s, gsm_byte * c, gsm_signal * target)
+{
+	int16_t LARc [8], Nc [4], Mc [4], bc [4], xmaxc [4], xmc [13 * 4] ;
+
+#ifdef WAV49
+	if (s->wav_fmt)
+	{	uint16_t sr = 0 ;
+
+		s->frame_index = !s->frame_index ;
+		if (s->frame_index)
+		{	sr = *c++ ;
+			LARc [0] = sr & 0x3f ; sr >>= 6 ;
+			sr |= (uint16_t) *c++ << 2 ;
+			LARc [1] = sr & 0x3f ; sr >>= 6 ;
+			sr |= (uint16_t) *c++ << 4 ;
+			LARc [2] = sr & 0x1f ; sr >>= 5 ;
+			LARc [3] = sr & 0x1f ; sr >>= 5 ;
+			sr |= (uint16_t) *c++ << 2 ;
+			LARc [4] = sr & 0xf ; sr >>= 4 ;
+			LARc [5] = sr & 0xf ; sr >>= 4 ;
+			sr |= (uint16_t) *c++ << 2 ;			/* 5 */
+			LARc [6] = sr & 0x7 ; sr >>= 3 ;
+			LARc [7] = sr & 0x7 ; sr >>= 3 ;
+			sr |= (uint16_t) *c++ << 4 ;
+			Nc [0] = sr & 0x7f ; sr >>= 7 ;
+			bc [0] = sr & 0x3 ; sr >>= 2 ;
+			Mc [0] = sr & 0x3 ; sr >>= 2 ;
+			sr |= (uint16_t) *c++ << 1 ;
+			xmaxc [0] = sr & 0x3f ; sr >>= 6 ;
+			xmc [0] = sr & 0x7 ; sr >>= 3 ;
+			sr = *c++ ;
+			xmc [1] = sr & 0x7 ; sr >>= 3 ;
+			xmc [2] = sr & 0x7 ; sr >>= 3 ;
+			sr |= (uint16_t) *c++ << 2 ;
+			xmc [3] = sr & 0x7 ; sr >>= 3 ;
+			xmc [4] = sr & 0x7 ; sr >>= 3 ;
+			xmc [5] = sr & 0x7 ; sr >>= 3 ;
+			sr |= (uint16_t) *c++ << 1 ;			/* 10 */
+			xmc [6] = sr & 0x7 ; sr >>= 3 ;
+			xmc [7] = sr & 0x7 ; sr >>= 3 ;
+			xmc [8] = sr & 0x7 ; sr >>= 3 ;
+			sr = *c++ ;
+			xmc [9] = sr & 0x7 ; sr >>= 3 ;
+			xmc [10] = sr & 0x7 ; sr >>= 3 ;
+			sr |= (uint16_t) *c++ << 2 ;
+			xmc [11] = sr & 0x7 ; sr >>= 3 ;
+			xmc [12] = sr & 0x7 ; sr >>= 3 ;
+			sr |= (uint16_t) *c++ << 4 ;
+			Nc [1] = sr & 0x7f ; sr >>= 7 ;
+			bc [1] = sr & 0x3 ; sr >>= 2 ;
+			Mc [1] = sr & 0x3 ; sr >>= 2 ;
+			sr |= (uint16_t) *c++ << 1 ;
+			xmaxc [1] = sr & 0x3f ; sr >>= 6 ;
+			xmc [13] = sr & 0x7 ; sr >>= 3 ;
+			sr = *c++ ;				/* 15 */
+			xmc [14] = sr & 0x7 ; sr >>= 3 ;
+			xmc [15] = sr & 0x7 ; sr >>= 3 ;
+			sr |= (uint16_t) *c++ << 2 ;
+			xmc [16] = sr & 0x7 ; sr >>= 3 ;
+			xmc [17] = sr & 0x7 ; sr >>= 3 ;
+			xmc [18] = sr & 0x7 ; sr >>= 3 ;
+			sr |= (uint16_t) *c++ << 1 ;
+			xmc [19] = sr & 0x7 ; sr >>= 3 ;
+			xmc [20] = sr & 0x7 ; sr >>= 3 ;
+			xmc [21] = sr & 0x7 ; sr >>= 3 ;
+			sr = *c++ ;
+			xmc [22] = sr & 0x7 ; sr >>= 3 ;
+			xmc [23] = sr & 0x7 ; sr >>= 3 ;
+			sr |= (uint16_t) *c++ << 2 ;
+			xmc [24] = sr & 0x7 ; sr >>= 3 ;
+			xmc [25] = sr & 0x7 ; sr >>= 3 ;
+			sr |= (uint16_t) *c++ << 4 ;			/* 20 */
+			Nc [2] = sr & 0x7f ; sr >>= 7 ;
+			bc [2] = sr & 0x3 ; sr >>= 2 ;
+			Mc [2] = sr & 0x3 ; sr >>= 2 ;
+			sr |= (uint16_t) *c++ << 1 ;
+			xmaxc [2] = sr & 0x3f ; sr >>= 6 ;
+			xmc [26] = sr & 0x7 ; sr >>= 3 ;
+			sr = *c++ ;
+			xmc [27] = sr & 0x7 ; sr >>= 3 ;
+			xmc [28] = sr & 0x7 ; sr >>= 3 ;
+			sr |= (uint16_t) *c++ << 2 ;
+			xmc [29] = sr & 0x7 ; sr >>= 3 ;
+			xmc [30] = sr & 0x7 ; sr >>= 3 ;
+			xmc [31] = sr & 0x7 ; sr >>= 3 ;
+			sr |= (uint16_t) *c++ << 1 ;
+			xmc [32] = sr & 0x7 ; sr >>= 3 ;
+			xmc [33] = sr & 0x7 ; sr >>= 3 ;
+			xmc [34] = sr & 0x7 ; sr >>= 3 ;
+			sr = *c++ ;				/* 25 */
+			xmc [35] = sr & 0x7 ; sr >>= 3 ;
+			xmc [36] = sr & 0x7 ; sr >>= 3 ;
+			sr |= (uint16_t) *c++ << 2 ;
+			xmc [37] = sr & 0x7 ; sr >>= 3 ;
+			xmc [38] = sr & 0x7 ; sr >>= 3 ;
+			sr |= (uint16_t) *c++ << 4 ;
+			Nc [3] = sr & 0x7f ; sr >>= 7 ;
+			bc [3] = sr & 0x3 ; sr >>= 2 ;
+			Mc [3] = sr & 0x3 ; sr >>= 2 ;
+			sr |= (uint16_t) *c++ << 1 ;
+			xmaxc [3] = sr & 0x3f ; sr >>= 6 ;
+			xmc [39] = sr & 0x7 ; sr >>= 3 ;
+			sr = *c++ ;
+			xmc [40] = sr & 0x7 ; sr >>= 3 ;
+			xmc [41] = sr & 0x7 ; sr >>= 3 ;
+			sr |= (uint16_t) *c++ << 2 ;			/* 30 */
+			xmc [42] = sr & 0x7 ; sr >>= 3 ;
+			xmc [43] = sr & 0x7 ; sr >>= 3 ;
+			xmc [44] = sr & 0x7 ; sr >>= 3 ;
+			sr |= (uint16_t) *c++ << 1 ;
+			xmc [45] = sr & 0x7 ; sr >>= 3 ;
+			xmc [46] = sr & 0x7 ; sr >>= 3 ;
+			xmc [47] = sr & 0x7 ; sr >>= 3 ;
+			sr = *c++ ;
+			xmc [48] = sr & 0x7 ; sr >>= 3 ;
+			xmc [49] = sr & 0x7 ; sr >>= 3 ;
+			sr |= (uint16_t) *c++ << 2 ;
+			xmc [50] = sr & 0x7 ; sr >>= 3 ;
+			xmc [51] = sr & 0x7 ; sr >>= 3 ;
+
+			s->frame_chain = sr & 0xf ;
+		}
+		else {
+			sr = s->frame_chain ;
+			sr |= (uint16_t) *c++ << 4 ;			/* 1 */
+			LARc [0] = sr & 0x3f ; sr >>= 6 ;
+			LARc [1] = sr & 0x3f ; sr >>= 6 ;
+			sr = *c++ ;
+			LARc [2] = sr & 0x1f ; sr >>= 5 ;
+			sr |= (uint16_t) *c++ << 3 ;
+			LARc [3] = sr & 0x1f ; sr >>= 5 ;
+			LARc [4] = sr & 0xf ; sr >>= 4 ;
+			sr |= (uint16_t) *c++ << 2 ;
+			LARc [5] = sr & 0xf ; sr >>= 4 ;
+			LARc [6] = sr & 0x7 ; sr >>= 3 ;
+			LARc [7] = sr & 0x7 ; sr >>= 3 ;
+			sr = *c++ ;				/* 5 */
+			Nc [0] = sr & 0x7f ; sr >>= 7 ;
+			sr |= (uint16_t) *c++ << 1 ;
+			bc [0] = sr & 0x3 ; sr >>= 2 ;
+			Mc [0] = sr & 0x3 ; sr >>= 2 ;
+			sr |= (uint16_t) *c++ << 5 ;
+			xmaxc [0] = sr & 0x3f ; sr >>= 6 ;
+			xmc [0] = sr & 0x7 ; sr >>= 3 ;
+			xmc [1] = sr & 0x7 ; sr >>= 3 ;
+			sr |= (uint16_t) *c++ << 1 ;
+			xmc [2] = sr & 0x7 ; sr >>= 3 ;
+			xmc [3] = sr & 0x7 ; sr >>= 3 ;
+			xmc [4] = sr & 0x7 ; sr >>= 3 ;
+			sr = *c++ ;
+			xmc [5] = sr & 0x7 ; sr >>= 3 ;
+			xmc [6] = sr & 0x7 ; sr >>= 3 ;
+			sr |= (uint16_t) *c++ << 2 ;			/* 10 */
+			xmc [7] = sr & 0x7 ; sr >>= 3 ;
+			xmc [8] = sr & 0x7 ; sr >>= 3 ;
+			xmc [9] = sr & 0x7 ; sr >>= 3 ;
+			sr |= (uint16_t) *c++ << 1 ;
+			xmc [10] = sr & 0x7 ; sr >>= 3 ;
+			xmc [11] = sr & 0x7 ; sr >>= 3 ;
+			xmc [12] = sr & 0x7 ; sr >>= 3 ;
+			sr = *c++ ;
+			Nc [1] = sr & 0x7f ; sr >>= 7 ;
+			sr |= (uint16_t) *c++ << 1 ;
+			bc [1] = sr & 0x3 ; sr >>= 2 ;
+			Mc [1] = sr & 0x3 ; sr >>= 2 ;
+			sr |= (uint16_t) *c++ << 5 ;
+			xmaxc [1] = sr & 0x3f ; sr >>= 6 ;
+			xmc [13] = sr & 0x7 ; sr >>= 3 ;
+			xmc [14] = sr & 0x7 ; sr >>= 3 ;
+			sr |= (uint16_t) *c++ << 1 ;			/* 15 */
+			xmc [15] = sr & 0x7 ; sr >>= 3 ;
+			xmc [16] = sr & 0x7 ; sr >>= 3 ;
+			xmc [17] = sr & 0x7 ; sr >>= 3 ;
+			sr = *c++ ;
+			xmc [18] = sr & 0x7 ; sr >>= 3 ;
+			xmc [19] = sr & 0x7 ; sr >>= 3 ;
+			sr |= (uint16_t) *c++ << 2 ;
+			xmc [20] = sr & 0x7 ; sr >>= 3 ;
+			xmc [21] = sr & 0x7 ; sr >>= 3 ;
+			xmc [22] = sr & 0x7 ; sr >>= 3 ;
+			sr |= (uint16_t) *c++ << 1 ;
+			xmc [23] = sr & 0x7 ; sr >>= 3 ;
+			xmc [24] = sr & 0x7 ; sr >>= 3 ;
+			xmc [25] = sr & 0x7 ; sr >>= 3 ;
+			sr = *c++ ;
+			Nc [2] = sr & 0x7f ; sr >>= 7 ;
+			sr |= (uint16_t) *c++ << 1 ;			/* 20 */
+			bc [2] = sr & 0x3 ; sr >>= 2 ;
+			Mc [2] = sr & 0x3 ; sr >>= 2 ;
+			sr |= (uint16_t) *c++ << 5 ;
+			xmaxc [2] = sr & 0x3f ; sr >>= 6 ;
+			xmc [26] = sr & 0x7 ; sr >>= 3 ;
+			xmc [27] = sr & 0x7 ; sr >>= 3 ;
+			sr |= (uint16_t) *c++ << 1 ;
+			xmc [28] = sr & 0x7 ; sr >>= 3 ;
+			xmc [29] = sr & 0x7 ; sr >>= 3 ;
+			xmc [30] = sr & 0x7 ; sr >>= 3 ;
+			sr = *c++ ;
+			xmc [31] = sr & 0x7 ; sr >>= 3 ;
+			xmc [32] = sr & 0x7 ; sr >>= 3 ;
+			sr |= (uint16_t) *c++ << 2 ;
+			xmc [33] = sr & 0x7 ; sr >>= 3 ;
+			xmc [34] = sr & 0x7 ; sr >>= 3 ;
+			xmc [35] = sr & 0x7 ; sr >>= 3 ;
+			sr |= (uint16_t) *c++ << 1 ;			/* 25 */
+			xmc [36] = sr & 0x7 ; sr >>= 3 ;
+			xmc [37] = sr & 0x7 ; sr >>= 3 ;
+			xmc [38] = sr & 0x7 ; sr >>= 3 ;
+			sr = *c++ ;
+			Nc [3] = sr & 0x7f ; sr >>= 7 ;
+			sr |= (uint16_t) *c++ << 1 ;
+			bc [3] = sr & 0x3 ; sr >>= 2 ;
+			Mc [3] = sr & 0x3 ; sr >>= 2 ;
+			sr |= (uint16_t) *c++ << 5 ;
+			xmaxc [3] = sr & 0x3f ; sr >>= 6 ;
+			xmc [39] = sr & 0x7 ; sr >>= 3 ;
+			xmc [40] = sr & 0x7 ; sr >>= 3 ;
+			sr |= (uint16_t) *c++ << 1 ;
+			xmc [41] = sr & 0x7 ; sr >>= 3 ;
+			xmc [42] = sr & 0x7 ; sr >>= 3 ;
+			xmc [43] = sr & 0x7 ; sr >>= 3 ;
+			sr = *c++ ;				/* 30 */
+			xmc [44] = sr & 0x7 ; sr >>= 3 ;
+			xmc [45] = sr & 0x7 ; sr >>= 3 ;
+			sr |= (uint16_t) *c++ << 2 ;
+			xmc [46] = sr & 0x7 ; sr >>= 3 ;
+			xmc [47] = sr & 0x7 ; sr >>= 3 ;
+			xmc [48] = sr & 0x7 ; sr >>= 3 ;
+			sr |= (uint16_t) *c++ << 1 ;
+			xmc [49] = sr & 0x7 ; sr >>= 3 ;
+			xmc [50] = sr & 0x7 ; sr >>= 3 ;
+			xmc [51] = sr & 0x7 ; sr >>= 3 ;
+		}
+	}
+	else
+#endif
+	{
+		/* GSM_MAGIC = (*c >> 4) & 0xF ; */
+
+		if (((*c >> 4) & 0x0F) != GSM_MAGIC) return -1 ;
+
+		LARc [0] = (*c++ & 0xF) << 2 ;		/* 1 */
+		LARc [0] |= (*c >> 6) & 0x3 ;
+		LARc [1] = *c++ & 0x3F ;
+		LARc [2] = (*c >> 3) & 0x1F ;
+		LARc [3] = (*c++ & 0x7) << 2 ;
+		LARc [3] |= (*c >> 6) & 0x3 ;
+		LARc [4] = (*c >> 2) & 0xF ;
+		LARc [5] = (*c++ & 0x3) << 2 ;
+		LARc [5] |= (*c >> 6) & 0x3 ;
+		LARc [6] = (*c >> 3) & 0x7 ;
+		LARc [7] = *c++ & 0x7 ;
+		Nc [0] = (*c >> 1) & 0x7F ;
+		bc [0] = (*c++ & 0x1) << 1 ;
+		bc [0] |= (*c >> 7) & 0x1 ;
+		Mc [0] = (*c >> 5) & 0x3 ;
+		xmaxc [0] = (*c++ & 0x1F) << 1 ;
+		xmaxc [0] |= (*c >> 7) & 0x1 ;
+		xmc [0] = (*c >> 4) & 0x7 ;
+		xmc [1] = (*c >> 1) & 0x7 ;
+		xmc [2] = (*c++ & 0x1) << 2 ;
+		xmc [2] |= (*c >> 6) & 0x3 ;
+		xmc [3] = (*c >> 3) & 0x7 ;
+		xmc [4] = *c++ & 0x7 ;
+		xmc [5] = (*c >> 5) & 0x7 ;
+		xmc [6] = (*c >> 2) & 0x7 ;
+		xmc [7] = (*c++ & 0x3) << 1 ;		/* 10 */
+		xmc [7] |= (*c >> 7) & 0x1 ;
+		xmc [8] = (*c >> 4) & 0x7 ;
+		xmc [9] = (*c >> 1) & 0x7 ;
+		xmc [10] = (*c++ & 0x1) << 2 ;
+		xmc [10] |= (*c >> 6) & 0x3 ;
+		xmc [11] = (*c >> 3) & 0x7 ;
+		xmc [12] = *c++ & 0x7 ;
+		Nc [1] = (*c >> 1) & 0x7F ;
+		bc [1] = (*c++ & 0x1) << 1 ;
+		bc [1] |= (*c >> 7) & 0x1 ;
+		Mc [1] = (*c >> 5) & 0x3 ;
+		xmaxc [1] = (*c++ & 0x1F) << 1 ;
+		xmaxc [1] |= (*c >> 7) & 0x1 ;
+		xmc [13] = (*c >> 4) & 0x7 ;
+		xmc [14] = (*c >> 1) & 0x7 ;
+		xmc [15] = (*c++ & 0x1) << 2 ;
+		xmc [15] |= (*c >> 6) & 0x3 ;
+		xmc [16] = (*c >> 3) & 0x7 ;
+		xmc [17] = *c++ & 0x7 ;
+		xmc [18] = (*c >> 5) & 0x7 ;
+		xmc [19] = (*c >> 2) & 0x7 ;
+		xmc [20] = (*c++ & 0x3) << 1 ;
+		xmc [20] |= (*c >> 7) & 0x1 ;
+		xmc [21] = (*c >> 4) & 0x7 ;
+		xmc [22] = (*c >> 1) & 0x7 ;
+		xmc [23] = (*c++ & 0x1) << 2 ;
+		xmc [23] |= (*c >> 6) & 0x3 ;
+		xmc [24] = (*c >> 3) & 0x7 ;
+		xmc [25] = *c++ & 0x7 ;
+		Nc [2] = (*c >> 1) & 0x7F ;
+		bc [2] = (*c++ & 0x1) << 1 ;		/* 20 */
+		bc [2] |= (*c >> 7) & 0x1 ;
+		Mc [2] = (*c >> 5) & 0x3 ;
+		xmaxc [2] = (*c++ & 0x1F) << 1 ;
+		xmaxc [2] |= (*c >> 7) & 0x1 ;
+		xmc [26] = (*c >> 4) & 0x7 ;
+		xmc [27] = (*c >> 1) & 0x7 ;
+		xmc [28] = (*c++ & 0x1) << 2 ;
+		xmc [28] |= (*c >> 6) & 0x3 ;
+		xmc [29] = (*c >> 3) & 0x7 ;
+		xmc [30] = *c++ & 0x7 ;
+		xmc [31] = (*c >> 5) & 0x7 ;
+		xmc [32] = (*c >> 2) & 0x7 ;
+		xmc [33] = (*c++ & 0x3) << 1 ;
+		xmc [33] |= (*c >> 7) & 0x1 ;
+		xmc [34] = (*c >> 4) & 0x7 ;
+		xmc [35] = (*c >> 1) & 0x7 ;
+		xmc [36] = (*c++ & 0x1) << 2 ;
+		xmc [36] |= (*c >> 6) & 0x3 ;
+		xmc [37] = (*c >> 3) & 0x7 ;
+		xmc [38] = *c++ & 0x7 ;
+		Nc [3] = (*c >> 1) & 0x7F ;
+		bc [3] = (*c++ & 0x1) << 1 ;
+		bc [3] |= (*c >> 7) & 0x1 ;
+		Mc [3] = (*c >> 5) & 0x3 ;
+		xmaxc [3] = (*c++ & 0x1F) << 1 ;
+		xmaxc [3] |= (*c >> 7) & 0x1 ;
+		xmc [39] = (*c >> 4) & 0x7 ;
+		xmc [40] = (*c >> 1) & 0x7 ;
+		xmc [41] = (*c++ & 0x1) << 2 ;
+		xmc [41] |= (*c >> 6) & 0x3 ;
+		xmc [42] = (*c >> 3) & 0x7 ;
+		xmc [43] = *c++ & 0x7 ;			/* 30  */
+		xmc [44] = (*c >> 5) & 0x7 ;
+		xmc [45] = (*c >> 2) & 0x7 ;
+		xmc [46] = (*c++ & 0x3) << 1 ;
+		xmc [46] |= (*c >> 7) & 0x1 ;
+		xmc [47] = (*c >> 4) & 0x7 ;
+		xmc [48] = (*c >> 1) & 0x7 ;
+		xmc [49] = (*c++ & 0x1) << 2 ;
+		xmc [49] |= (*c >> 6) & 0x3 ;
+		xmc [50] = (*c >> 3) & 0x7 ;
+		xmc [51] = *c & 0x7 ;			/* 33 */
+	}
+
+	Gsm_Decoder (s, LARc, Nc, bc, Mc, xmaxc, xmc, target) ;
+
+	return 0 ;
+}
+
diff --git a/src/GSM610/gsm_destroy.c b/src/GSM610/gsm_destroy.c
new file mode 100644
index 0000000..03bf7c3
--- /dev/null
+++ b/src/GSM610/gsm_destroy.c
@@ -0,0 +1,25 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin.  See the accompanying file "COPYRIGHT" for
+ * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+#include "gsm.h"
+#include "config.h"
+
+#ifdef	HAS_STDLIB_H
+#	include	<stdlib.h>
+#else
+#	ifdef	HAS_MALLOC_H
+#		include 	<malloc.h>
+#	else
+		extern void free () ;
+#	endif
+#endif
+
+void gsm_destroy (gsm S)
+{
+	if (S)
+		free ((char *) S) ;
+}
+
diff --git a/src/GSM610/gsm_encode.c b/src/GSM610/gsm_encode.c
new file mode 100644
index 0000000..5fd5351
--- /dev/null
+++ b/src/GSM610/gsm_encode.c
@@ -0,0 +1,448 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin.  See the accompanying file "COPYRIGHT" for
+ * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+#include "gsm610_priv.h"
+#include "gsm.h"
+
+void gsm_encode (gsm s, gsm_signal * source, gsm_byte * c)
+{
+	int16_t		LARc [8], Nc [4], Mc [4], bc [4], xmaxc [4], xmc [13 * 4] ;
+
+	Gsm_Coder (s, source, LARc, Nc, bc, Mc, xmaxc, xmc) ;
+
+
+	/*	variable	size
+
+		GSM_MAGIC	4
+
+		LARc [0]		6
+		LARc [1]		6
+		LARc [2]		5
+		LARc [3]		5
+		LARc [4]		4
+		LARc [5]		4
+		LARc [6]		3
+		LARc [7]		3
+
+		Nc [0]		7
+		bc [0]		2
+		Mc [0]		2
+		xmaxc [0]	6
+		xmc [0]		3
+		xmc [1]		3
+		xmc [2]		3
+		xmc [3]		3
+		xmc [4]		3
+		xmc [5]		3
+		xmc [6]		3
+		xmc [7]		3
+		xmc [8]		3
+		xmc [9]		3
+		xmc [10]		3
+		xmc [11]		3
+		xmc [12]		3
+
+		Nc [1]		7
+		bc [1]		2
+		Mc [1]		2
+		xmaxc [1]	6
+		xmc [13]		3
+		xmc [14]		3
+		xmc [15]		3
+		xmc [16]		3
+		xmc [17]		3
+		xmc [18]		3
+		xmc [19]		3
+		xmc [20]		3
+		xmc [21]		3
+		xmc [22]		3
+		xmc [23]		3
+		xmc [24]		3
+		xmc [25]		3
+
+		Nc [2]		7
+		bc [2]		2
+		Mc [2]		2
+		xmaxc [2]	6
+		xmc [26]		3
+		xmc [27]		3
+		xmc [28]		3
+		xmc [29]		3
+		xmc [30]		3
+		xmc [31]		3
+		xmc [32]		3
+		xmc [33]		3
+		xmc [34]		3
+		xmc [35]		3
+		xmc [36]		3
+		xmc [37]		3
+		xmc [38]		3
+
+		Nc [3]		7
+		bc [3]		2
+		Mc [3]		2
+		xmaxc [3]	6
+		xmc [39]		3
+		xmc [40]		3
+		xmc [41]		3
+		xmc [42]		3
+		xmc [43]		3
+		xmc [44]		3
+		xmc [45]		3
+		xmc [46]		3
+		xmc [47]		3
+		xmc [48]		3
+		xmc [49]		3
+		xmc [50]		3
+		xmc [51]		3
+	*/
+
+#ifdef WAV49
+
+	if (s->wav_fmt)
+	{	s->frame_index = !s->frame_index ;
+		if (s->frame_index)
+		{	uint16_t sr ;
+
+			sr = 0 ;
+			sr = sr >> 6 | LARc [0] << 10 ;
+			sr = sr >> 6 | LARc [1] << 10 ;
+			*c++ = sr >> 4 ;
+			sr = sr >> 5 | LARc [2] << 11 ;
+			*c++ = sr >> 7 ;
+			sr = sr >> 5 | LARc [3] << 11 ;
+			sr = sr >> 4 | LARc [4] << 12 ;
+			*c++ = sr >> 6 ;
+			sr = sr >> 4 | LARc [5] << 12 ;
+			sr = sr >> 3 | LARc [6] << 13 ;
+			*c++ = sr >> 7 ;
+			sr = sr >> 3 | LARc [7] << 13 ;
+			sr = sr >> 7 | Nc [0] << 9 ;
+			*c++ = sr >> 5 ;
+			sr = sr >> 2 | bc [0] << 14 ;
+			sr = sr >> 2 | Mc [0] << 14 ;
+			sr = sr >> 6 | xmaxc [0] << 10 ;
+			*c++ = sr >> 3 ;
+			sr = sr >> 3 | xmc [0] << 13 ;
+			*c++ = sr >> 8 ;
+			sr = sr >> 3 | xmc [1] << 13 ;
+			sr = sr >> 3 | xmc [2] << 13 ;
+			sr = sr >> 3 | xmc [3] << 13 ;
+			*c++ = sr >> 7 ;
+			sr = sr >> 3 | xmc [4] << 13 ;
+			sr = sr >> 3 | xmc [5] << 13 ;
+			sr = sr >> 3 | xmc [6] << 13 ;
+			*c++ = sr >> 6 ;
+			sr = sr >> 3 | xmc [7] << 13 ;
+			sr = sr >> 3 | xmc [8] << 13 ;
+			*c++ = sr >> 8 ;
+			sr = sr >> 3 | xmc [9] << 13 ;
+			sr = sr >> 3 | xmc [10] << 13 ;
+			sr = sr >> 3 | xmc [11] << 13 ;
+			*c++ = sr >> 7 ;
+			sr = sr >> 3 | xmc [12] << 13 ;
+			sr = sr >> 7 | Nc [1] << 9 ;
+			*c++ = sr >> 5 ;
+			sr = sr >> 2 | bc [1] << 14 ;
+			sr = sr >> 2 | Mc [1] << 14 ;
+			sr = sr >> 6 | xmaxc [1] << 10 ;
+			*c++ = sr >> 3 ;
+			sr = sr >> 3 | xmc [13] << 13 ;
+			*c++ = sr >> 8 ;
+			sr = sr >> 3 | xmc [14] << 13 ;
+			sr = sr >> 3 | xmc [15] << 13 ;
+			sr = sr >> 3 | xmc [16] << 13 ;
+			*c++ = sr >> 7 ;
+			sr = sr >> 3 | xmc [17] << 13 ;
+			sr = sr >> 3 | xmc [18] << 13 ;
+			sr = sr >> 3 | xmc [19] << 13 ;
+			*c++ = sr >> 6 ;
+			sr = sr >> 3 | xmc [20] << 13 ;
+			sr = sr >> 3 | xmc [21] << 13 ;
+			*c++ = sr >> 8 ;
+			sr = sr >> 3 | xmc [22] << 13 ;
+			sr = sr >> 3 | xmc [23] << 13 ;
+			sr = sr >> 3 | xmc [24] << 13 ;
+			*c++ = sr >> 7 ;
+			sr = sr >> 3 | xmc [25] << 13 ;
+			sr = sr >> 7 | Nc [2] << 9 ;
+			*c++ = sr >> 5 ;
+			sr = sr >> 2 | bc [2] << 14 ;
+			sr = sr >> 2 | Mc [2] << 14 ;
+			sr = sr >> 6 | xmaxc [2] << 10 ;
+			*c++ = sr >> 3 ;
+			sr = sr >> 3 | xmc [26] << 13 ;
+			*c++ = sr >> 8 ;
+			sr = sr >> 3 | xmc [27] << 13 ;
+			sr = sr >> 3 | xmc [28] << 13 ;
+			sr = sr >> 3 | xmc [29] << 13 ;
+			*c++ = sr >> 7 ;
+			sr = sr >> 3 | xmc [30] << 13 ;
+			sr = sr >> 3 | xmc [31] << 13 ;
+			sr = sr >> 3 | xmc [32] << 13 ;
+			*c++ = sr >> 6 ;
+			sr = sr >> 3 | xmc [33] << 13 ;
+			sr = sr >> 3 | xmc [34] << 13 ;
+			*c++ = sr >> 8 ;
+			sr = sr >> 3 | xmc [35] << 13 ;
+			sr = sr >> 3 | xmc [36] << 13 ;
+			sr = sr >> 3 | xmc [37] << 13 ;
+			*c++ = sr >> 7 ;
+			sr = sr >> 3 | xmc [38] << 13 ;
+			sr = sr >> 7 | Nc [3] << 9 ;
+			*c++ = sr >> 5 ;
+			sr = sr >> 2 | bc [3] << 14 ;
+			sr = sr >> 2 | Mc [3] << 14 ;
+			sr = sr >> 6 | xmaxc [3] << 10 ;
+			*c++ = sr >> 3 ;
+			sr = sr >> 3 | xmc [39] << 13 ;
+			*c++ = sr >> 8 ;
+			sr = sr >> 3 | xmc [40] << 13 ;
+			sr = sr >> 3 | xmc [41] << 13 ;
+			sr = sr >> 3 | xmc [42] << 13 ;
+			*c++ = sr >> 7 ;
+			sr = sr >> 3 | xmc [43] << 13 ;
+			sr = sr >> 3 | xmc [44] << 13 ;
+			sr = sr >> 3 | xmc [45] << 13 ;
+			*c++ = sr >> 6 ;
+			sr = sr >> 3 | xmc [46] << 13 ;
+			sr = sr >> 3 | xmc [47] << 13 ;
+			*c++ = sr >> 8 ;
+			sr = sr >> 3 | xmc [48] << 13 ;
+			sr = sr >> 3 | xmc [49] << 13 ;
+			sr = sr >> 3 | xmc [50] << 13 ;
+			*c++ = sr >> 7 ;
+			sr = sr >> 3 | xmc [51] << 13 ;
+			sr = sr >> 4 ;
+			*c = sr >> 8 ;
+			s->frame_chain = *c ;
+		}
+		else {
+			uint16_t sr ;
+
+			sr = 0 ;
+			sr = sr >> 4 | s->frame_chain << 12 ;
+			sr = sr >> 6 | LARc [0] << 10 ;
+			*c++ = sr >> 6 ;
+			sr = sr >> 6 | LARc [1] << 10 ;
+			*c++ = sr >> 8 ;
+			sr = sr >> 5 | LARc [2] << 11 ;
+			sr = sr >> 5 | LARc [3] << 11 ;
+			*c++ = sr >> 6 ;
+			sr = sr >> 4 | LARc [4] << 12 ;
+			sr = sr >> 4 | LARc [5] << 12 ;
+			*c++ = sr >> 6 ;
+			sr = sr >> 3 | LARc [6] << 13 ;
+			sr = sr >> 3 | LARc [7] << 13 ;
+			*c++ = sr >> 8 ;
+			sr = sr >> 7 | Nc [0] << 9 ;
+			sr = sr >> 2 | bc [0] << 14 ;
+			*c++ = sr >> 7 ;
+			sr = sr >> 2 | Mc [0] << 14 ;
+			sr = sr >> 6 | xmaxc [0] << 10 ;
+			*c++ = sr >> 7 ;
+			sr = sr >> 3 | xmc [0] << 13 ;
+			sr = sr >> 3 | xmc [1] << 13 ;
+			sr = sr >> 3 | xmc [2] << 13 ;
+			*c++ = sr >> 6 ;
+			sr = sr >> 3 | xmc [3] << 13 ;
+			sr = sr >> 3 | xmc [4] << 13 ;
+			*c++ = sr >> 8 ;
+			sr = sr >> 3 | xmc [5] << 13 ;
+			sr = sr >> 3 | xmc [6] << 13 ;
+			sr = sr >> 3 | xmc [7] << 13 ;
+			*c++ = sr >> 7 ;
+			sr = sr >> 3 | xmc [8] << 13 ;
+			sr = sr >> 3 | xmc [9] << 13 ;
+			sr = sr >> 3 | xmc [10] << 13 ;
+			*c++ = sr >> 6 ;
+			sr = sr >> 3 | xmc [11] << 13 ;
+			sr = sr >> 3 | xmc [12] << 13 ;
+			*c++ = sr >> 8 ;
+			sr = sr >> 7 | Nc [1] << 9 ;
+			sr = sr >> 2 | bc [1] << 14 ;
+			*c++ = sr >> 7 ;
+			sr = sr >> 2 | Mc [1] << 14 ;
+			sr = sr >> 6 | xmaxc [1] << 10 ;
+			*c++ = sr >> 7 ;
+			sr = sr >> 3 | xmc [13] << 13 ;
+			sr = sr >> 3 | xmc [14] << 13 ;
+			sr = sr >> 3 | xmc [15] << 13 ;
+			*c++ = sr >> 6 ;
+			sr = sr >> 3 | xmc [16] << 13 ;
+			sr = sr >> 3 | xmc [17] << 13 ;
+			*c++ = sr >> 8 ;
+			sr = sr >> 3 | xmc [18] << 13 ;
+			sr = sr >> 3 | xmc [19] << 13 ;
+			sr = sr >> 3 | xmc [20] << 13 ;
+			*c++ = sr >> 7 ;
+			sr = sr >> 3 | xmc [21] << 13 ;
+			sr = sr >> 3 | xmc [22] << 13 ;
+			sr = sr >> 3 | xmc [23] << 13 ;
+			*c++ = sr >> 6 ;
+			sr = sr >> 3 | xmc [24] << 13 ;
+			sr = sr >> 3 | xmc [25] << 13 ;
+			*c++ = sr >> 8 ;
+			sr = sr >> 7 | Nc [2] << 9 ;
+			sr = sr >> 2 | bc [2] << 14 ;
+			*c++ = sr >> 7 ;
+			sr = sr >> 2 | Mc [2] << 14 ;
+			sr = sr >> 6 | xmaxc [2] << 10 ;
+			*c++ = sr >> 7 ;
+			sr = sr >> 3 | xmc [26] << 13 ;
+			sr = sr >> 3 | xmc [27] << 13 ;
+			sr = sr >> 3 | xmc [28] << 13 ;
+			*c++ = sr >> 6 ;
+			sr = sr >> 3 | xmc [29] << 13 ;
+			sr = sr >> 3 | xmc [30] << 13 ;
+			*c++ = sr >> 8 ;
+			sr = sr >> 3 | xmc [31] << 13 ;
+			sr = sr >> 3 | xmc [32] << 13 ;
+			sr = sr >> 3 | xmc [33] << 13 ;
+			*c++ = sr >> 7 ;
+			sr = sr >> 3 | xmc [34] << 13 ;
+			sr = sr >> 3 | xmc [35] << 13 ;
+			sr = sr >> 3 | xmc [36] << 13 ;
+			*c++ = sr >> 6 ;
+			sr = sr >> 3 | xmc [37] << 13 ;
+			sr = sr >> 3 | xmc [38] << 13 ;
+			*c++ = sr >> 8 ;
+			sr = sr >> 7 | Nc [3] << 9 ;
+			sr = sr >> 2 | bc [3] << 14 ;
+			*c++ = sr >> 7 ;
+			sr = sr >> 2 | Mc [3] << 14 ;
+			sr = sr >> 6 | xmaxc [3] << 10 ;
+			*c++ = sr >> 7 ;
+			sr = sr >> 3 | xmc [39] << 13 ;
+			sr = sr >> 3 | xmc [40] << 13 ;
+			sr = sr >> 3 | xmc [41] << 13 ;
+			*c++ = sr >> 6 ;
+			sr = sr >> 3 | xmc [42] << 13 ;
+			sr = sr >> 3 | xmc [43] << 13 ;
+			*c++ = sr >> 8 ;
+			sr = sr >> 3 | xmc [44] << 13 ;
+			sr = sr >> 3 | xmc [45] << 13 ;
+			sr = sr >> 3 | xmc [46] << 13 ;
+			*c++ = sr >> 7 ;
+			sr = sr >> 3 | xmc [47] << 13 ;
+			sr = sr >> 3 | xmc [48] << 13 ;
+			sr = sr >> 3 | xmc [49] << 13 ;
+			*c++ = sr >> 6 ;
+			sr = sr >> 3 | xmc [50] << 13 ;
+			sr = sr >> 3 | xmc [51] << 13 ;
+			*c++ = sr >> 8 ;
+		}
+	}
+
+	else
+
+#endif	/* WAV49 */
+	{
+
+		*c++ = ((GSM_MAGIC & 0xF) << 4)		/* 1 */
+				| ((LARc [0] >> 2) & 0xF) ;
+		*c++ = ((LARc [0] & 0x3) << 6)
+				| (LARc [1] & 0x3F) ;
+		*c++ = ((LARc [2] & 0x1F) << 3)
+				| ((LARc [3] >> 2) & 0x7) ;
+		*c++ = ((LARc [3] & 0x3) << 6)
+				| ((LARc [4] & 0xF) << 2)
+				| ((LARc [5] >> 2) & 0x3) ;
+		*c++ = ((LARc [5] & 0x3) << 6)
+				| ((LARc [6] & 0x7) << 3)
+				| (LARc [7] & 0x7) ;
+		*c++ = ((Nc [0] & 0x7F) << 1)
+				| ((bc [0] >> 1) & 0x1) ;
+		*c++ = ((bc [0] & 0x1) << 7)
+				| ((Mc [0] & 0x3) << 5)
+				| ((xmaxc [0] >> 1) & 0x1F) ;
+		*c++ = ((xmaxc [0] & 0x1) << 7)
+				| ((xmc [0] & 0x7) << 4)
+				| ((xmc [1] & 0x7) << 1)
+				| ((xmc [2] >> 2) & 0x1) ;
+		*c++ = ((xmc [2] & 0x3) << 6)
+				| ((xmc [3] & 0x7) << 3)
+				| (xmc [4] & 0x7) ;
+		*c++ = ((xmc [5] & 0x7) << 5)			/* 10 */
+				| ((xmc [6] & 0x7) << 2)
+				| ((xmc [7] >> 1) & 0x3) ;
+		*c++ = ((xmc [7] & 0x1) << 7)
+				| ((xmc [8] & 0x7) << 4)
+				| ((xmc [9] & 0x7) << 1)
+				| ((xmc [10] >> 2) & 0x1) ;
+		*c++ = ((xmc [10] & 0x3) << 6)
+				| ((xmc [11] & 0x7) << 3)
+				| (xmc [12] & 0x7) ;
+		*c++ = ((Nc [1] & 0x7F) << 1)
+				| ((bc [1] >> 1) & 0x1) ;
+		*c++ = ((bc [1] & 0x1) << 7)
+				| ((Mc [1] & 0x3) << 5)
+				| ((xmaxc [1] >> 1) & 0x1F) ;
+		*c++ = ((xmaxc [1] & 0x1) << 7)
+				| ((xmc [13] & 0x7) << 4)
+				| ((xmc [14] & 0x7) << 1)
+				| ((xmc [15] >> 2) & 0x1) ;
+		*c++ = ((xmc [15] & 0x3) << 6)
+				| ((xmc [16] & 0x7) << 3)
+				| (xmc [17] & 0x7) ;
+		*c++ = ((xmc [18] & 0x7) << 5)
+				| ((xmc [19] & 0x7) << 2)
+				| ((xmc [20] >> 1) & 0x3) ;
+		*c++ = ((xmc [20] & 0x1) << 7)
+				| ((xmc [21] & 0x7) << 4)
+				| ((xmc [22] & 0x7) << 1)
+				| ((xmc [23] >> 2) & 0x1) ;
+		*c++ = ((xmc [23] & 0x3) << 6)
+				| ((xmc [24] & 0x7) << 3)
+				| (xmc [25] & 0x7) ;
+		*c++ = ((Nc [2] & 0x7F) << 1)			/* 20 */
+				| ((bc [2] >> 1) & 0x1) ;
+		*c++ = ((bc [2] & 0x1) << 7)
+				| ((Mc [2] & 0x3) << 5)
+				| ((xmaxc [2] >> 1) & 0x1F) ;
+		*c++ = ((xmaxc [2] & 0x1) << 7)
+				| ((xmc [26] & 0x7) << 4)
+				| ((xmc [27] & 0x7) << 1)
+				| ((xmc [28] >> 2) & 0x1) ;
+		*c++ = ((xmc [28] & 0x3) << 6)
+				| ((xmc [29] & 0x7) << 3)
+				| (xmc [30] & 0x7) ;
+		*c++ = ((xmc [31] & 0x7) << 5)
+				| ((xmc [32] & 0x7) << 2)
+				| ((xmc [33] >> 1) & 0x3) ;
+		*c++ = ((xmc [33] & 0x1) << 7)
+				| ((xmc [34] & 0x7) << 4)
+				| ((xmc [35] & 0x7) << 1)
+				| ((xmc [36] >> 2) & 0x1) ;
+		*c++ = ((xmc [36] & 0x3) << 6)
+				| ((xmc [37] & 0x7) << 3)
+				| (xmc [38] & 0x7) ;
+		*c++ = ((Nc [3] & 0x7F) << 1)
+				| ((bc [3] >> 1) & 0x1) ;
+		*c++ = ((bc [3] & 0x1) << 7)
+				| ((Mc [3] & 0x3) << 5)
+				| ((xmaxc [3] >> 1) & 0x1F) ;
+		*c++ = ((xmaxc [3] & 0x1) << 7)
+				| ((xmc [39] & 0x7) << 4)
+				| ((xmc [40] & 0x7) << 1)
+				| ((xmc [41] >> 2) & 0x1) ;
+		*c++ = ((xmc [41] & 0x3) << 6)			/* 30 */
+				| ((xmc [42] & 0x7) << 3)
+				| (xmc [43] & 0x7) ;
+		*c++ = ((xmc [44] & 0x7) << 5)
+				| ((xmc [45] & 0x7) << 2)
+				| ((xmc [46] >> 1) & 0x3) ;
+		*c++ = ((xmc [46] & 0x1) << 7)
+				| ((xmc [47] & 0x7) << 4)
+				| ((xmc [48] & 0x7) << 1)
+				| ((xmc [49] >> 2) & 0x1) ;
+		*c++ = ((xmc [49] & 0x3) << 6)
+				| ((xmc [50] & 0x7) << 3)
+				| (xmc [51] & 0x7) ;
+
+	}
+}
+
diff --git a/src/GSM610/gsm_option.c b/src/GSM610/gsm_option.c
new file mode 100644
index 0000000..2087683
--- /dev/null
+++ b/src/GSM610/gsm_option.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin.  See the accompanying file "COPYRIGHT" for
+ * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+#include "gsm610_priv.h"
+
+#include "gsm.h"
+
+int gsm_option (gsm r, int opt, int * val)
+{
+	int 	result = -1 ;
+
+	switch (opt) {
+	case GSM_OPT_LTP_CUT:
+#ifdef 	LTP_CUT
+		result = r->ltp_cut ;
+		if (val) r->ltp_cut = *val ;
+#endif
+		break ;
+
+	case GSM_OPT_VERBOSE:
+#ifndef	NDEBUG
+		result = r->verbose ;
+		if (val) r->verbose = *val ;
+#endif
+		break ;
+
+	case GSM_OPT_FAST:
+
+#if	defined (FAST) && defined (USE_FLOAT_MUL)
+		result = r->fast ;
+		if (val) r->fast = !!*val ;
+#endif
+		break ;
+
+	case GSM_OPT_FRAME_CHAIN:
+
+#ifdef WAV49
+		result = r->frame_chain ;
+		if (val) r->frame_chain = *val ;
+#endif
+		break ;
+
+	case GSM_OPT_FRAME_INDEX:
+
+#ifdef WAV49
+		result = r->frame_index ;
+		if (val) r->frame_index = *val ;
+#endif
+		break ;
+
+	case GSM_OPT_WAV49:
+
+#ifdef WAV49
+		result = r->wav_fmt ;
+		if (val) r->wav_fmt = !!*val ;
+#endif
+		break ;
+
+	default:
+		break ;
+	}
+	return result ;
+}
diff --git a/src/GSM610/long_term.c b/src/GSM610/long_term.c
new file mode 100644
index 0000000..3276a46
--- /dev/null
+++ b/src/GSM610/long_term.c
@@ -0,0 +1,932 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin.  See the accompanying file "COPYRIGHT" for
+ * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <assert.h>
+
+#include "gsm610_priv.h"
+
+/*
+ *  4.2.11 .. 4.2.12 LONG TERM PREDICTOR (LTP) SECTION
+ */
+
+
+/*
+ * This module computes the LTP gain (bc) and the LTP lag (Nc)
+ * for the long term analysis filter.   This is done by calculating a
+ * maximum of the cross-correlation function between the current
+ * sub-segment short term residual signal d [0..39] (output of
+ * the short term analysis filter ; for simplification the index
+ * of this array begins at 0 and ends at 39 for each sub-segment of the
+ * RPE-LTP analysis) and the previous reconstructed short term
+ * residual signal dp [-120 .. -1].  A dynamic scaling must be
+ * performed to avoid overflow.
+ */
+
+ /* The next procedure exists in six versions.  First two integer
+  * version (if USE_FLOAT_MUL is not defined) ; then four floating
+  * point versions, twice with proper scaling (USE_FLOAT_MUL defined),
+  * once without (USE_FLOAT_MUL and FAST defined, and fast run-time
+  * option used).  Every pair has first a Cut version (see the -C
+  * option to toast or the LTP_CUT option to gsm_option ()), then the
+  * uncut one.  (For a detailed explanation of why this is altogether
+  * a bad idea, see Henry Spencer and Geoff Collyer, ``#ifdef Considered
+  * Harmful''.)
+  */
+
+#ifndef	USE_FLOAT_MUL
+
+#ifdef	LTP_CUT
+
+static void Cut_Calculation_of_the_LTP_parameters (
+
+	struct gsm_state * st,
+
+	register int16_t	* d,		/* [0..39]	IN	*/
+	register int16_t	* dp,		/* [-120..-1]	IN	*/
+	int16_t		* bc_out,	/* 		OUT	*/
+	int16_t		* Nc_out	/* 		OUT	*/)
+{
+	register int	k, lambda ;
+	int16_t		Nc, bc ;
+	int16_t		wt [40] ;
+
+	int32_t	L_result ;
+	int32_t	L_max, L_power ;
+	int16_t		R, S, dmax, scal, best_k ;
+	int16_t		ltp_cut ;
+
+	register int16_t	temp, wt_k ;
+
+	/*  Search of the optimum scaling of d [0..39]. */
+	dmax = 0 ;
+	for (k = 0 ; k <= 39 ; k++)
+	{	temp = d [k] ;
+		temp = GSM_ABS (temp) ;
+		if (temp > dmax)
+		{	dmax = temp ;
+			best_k = k ;
+			}
+		}
+	temp = 0 ;
+	if (dmax == 0)
+		scal = 0 ;
+	else
+	{	assert (dmax > 0) ;
+		temp = gsm_norm ((int32_t) dmax << 16) ;
+		}
+	if (temp > 6) scal = 0 ;
+	else scal = 6 - temp ;
+	assert (scal >= 0) ;
+
+	/* Search for the maximum cross-correlation and coding of the LTP lag
+	 */
+	L_max = 0 ;
+	Nc = 40 ;	/* index for the maximum cross-correlation */
+	wt_k = SASR_W (d [best_k], scal) ;
+
+	for (lambda = 40 ; lambda <= 120 ; lambda++)
+	{	L_result = (int32_t) wt_k * dp [best_k - lambda] ;
+		if (L_result > L_max)
+		{	Nc = lambda ;
+			L_max = L_result ;
+			}
+		}
+	*Nc_out = Nc ;
+	L_max <<= 1 ;
+
+	/*  Rescaling of L_max
+	 */
+	assert (scal <= 100 && scal >= -100) ;
+	L_max = L_max >> (6 - scal) ;	/* sub (6, scal) */
+
+	assert (Nc <= 120 && Nc >= 40) ;
+
+	/*   Compute the power of the reconstructed short term residual
+	 *   signal dp [..]
+	 */
+	L_power = 0 ;
+	for (k = 0 ; k <= 39 ; k++)
+	{	register int32_t L_temp ;
+
+		L_temp = SASR_W (dp [k - Nc], 3) ;
+		L_power += L_temp * L_temp ;
+		}
+	L_power <<= 1 ;	/* from L_MULT */
+
+	/*  Normalization of L_max and L_power */
+
+	if (L_max <= 0)
+	{	*bc_out = 0 ;
+		return ;
+		}
+	if (L_max >= L_power)
+	{	*bc_out = 3 ;
+		return ;
+		}
+
+	temp = gsm_norm (L_power) ;
+
+	R = SASR (L_max << temp, 16) ;
+	S = SASR (L_power << temp, 16) ;
+
+	/*  Coding of the LTP gain
+	 */
+
+	/*  Table 4.3a must be used to obtain the level DLB [i] for the
+	 *  quantization of the LTP gain b to get the coded version bc.
+	 */
+	for (bc = 0 ; bc <= 2 ; bc++) if (R <= gsm_mult (S, gsm_DLB [bc])) break ;
+	*bc_out = bc ;
+}
+
+#endif 	/* LTP_CUT */
+
+static void Calculation_of_the_LTP_parameters (
+	register int16_t	* d,		/* [0..39]	IN	*/
+	register int16_t	* dp,		/* [-120..-1]	IN	*/
+	int16_t		* bc_out,	/* 		OUT	*/
+	int16_t		* Nc_out	/* 		OUT	*/)
+{
+	register int	k, lambda ;
+	int16_t		Nc, bc ;
+	int16_t		wt [40] ;
+
+	int32_t	L_max, L_power ;
+	int16_t		R, S, dmax, scal ;
+	register int16_t	temp ;
+
+	/*  Search of the optimum scaling of d [0..39].
+	 */
+	dmax = 0 ;
+
+	for (k = 0 ; k <= 39 ; k++)
+	{	temp = d [k] ;
+		temp = GSM_ABS (temp) ;
+		if (temp > dmax) dmax = temp ;
+		}
+
+	temp = 0 ;
+	if (dmax == 0)
+		scal = 0 ;
+	else
+	{	assert (dmax > 0) ;
+		temp = gsm_norm ((int32_t) dmax << 16) ;
+		}
+
+	if (temp > 6) scal = 0 ;
+	else scal = 6 - temp ;
+
+	assert (scal >= 0) ;
+
+	/*  Initialization of a working array wt
+	 */
+
+	for (k = 0 ; k <= 39 ; k++) wt [k] = SASR_W (d [k], scal) ;
+
+	/* Search for the maximum cross-correlation and coding of the LTP lag */
+	L_max = 0 ;
+	Nc = 40 ;	/* index for the maximum cross-correlation */
+
+	for (lambda = 40 ; lambda <= 120 ; lambda++)
+	{
+
+# undef STEP
+#		define STEP(k) 	(int32_t) wt [k] * dp [k - lambda]
+
+		register int32_t L_result ;
+
+		L_result = STEP (0) ; L_result += STEP (1) ;
+		L_result += STEP (2) ; L_result += STEP (3) ;
+		L_result += STEP (4) ; L_result += STEP (5) ;
+		L_result += STEP (6) ; L_result += STEP (7) ;
+		L_result += STEP (8) ; L_result += STEP (9) ;
+		L_result += STEP (10) ; L_result += STEP (11) ;
+		L_result += STEP (12) ; L_result += STEP (13) ;
+		L_result += STEP (14) ; L_result += STEP (15) ;
+		L_result += STEP (16) ; L_result += STEP (17) ;
+		L_result += STEP (18) ; L_result += STEP (19) ;
+		L_result += STEP (20) ; L_result += STEP (21) ;
+		L_result += STEP (22) ; L_result += STEP (23) ;
+		L_result += STEP (24) ; L_result += STEP (25) ;
+		L_result += STEP (26) ; L_result += STEP (27) ;
+		L_result += STEP (28) ; L_result += STEP (29) ;
+		L_result += STEP (30) ; L_result += STEP (31) ;
+		L_result += STEP (32) ; L_result += STEP (33) ;
+		L_result += STEP (34) ; L_result += STEP (35) ;
+		L_result += STEP (36) ; L_result += STEP (37) ;
+		L_result += STEP (38) ; L_result += STEP (39) ;
+
+		if (L_result > L_max)
+		{	Nc = lambda ;
+			L_max = L_result ;
+			}
+		}
+
+	*Nc_out = Nc ;
+
+	L_max <<= 1 ;
+
+	/*  Rescaling of L_max
+	 */
+	assert (scal <= 100 && scal >= -100) ;
+	L_max = L_max >> (6 - scal) ;	/* sub (6, scal) */
+
+	assert (Nc <= 120 && Nc >= 40) ;
+
+	/*   Compute the power of the reconstructed short term residual
+	 *   signal dp [..]
+	 */
+	L_power = 0 ;
+	for (k = 0 ; k <= 39 ; k++)
+	{	register int32_t L_temp ;
+
+		L_temp = SASR_W (dp [k - Nc], 3) ;
+		L_power += L_temp * L_temp ;
+		}
+	L_power <<= 1 ;	/* from L_MULT */
+
+	/*  Normalization of L_max and L_power
+	 */
+
+	if (L_max <= 0)
+	{	*bc_out = 0 ;
+		return ;
+		}
+	if (L_max >= L_power)
+	{	*bc_out = 3 ;
+		return ;
+		}
+
+	temp = gsm_norm (L_power) ;
+
+	R = SASR_L (L_max << temp, 16) ;
+	S = SASR_L (L_power << temp, 16) ;
+
+	/*  Coding of the LTP gain
+	 */
+
+	/*  Table 4.3a must be used to obtain the level DLB [i] for the
+	 *  quantization of the LTP gain b to get the coded version bc.
+	 */
+	for (bc = 0 ; bc <= 2 ; bc++) if (R <= gsm_mult (S, gsm_DLB [bc])) break ;
+	*bc_out = bc ;
+}
+
+#else	/* USE_FLOAT_MUL */
+
+#ifdef	LTP_CUT
+
+static void Cut_Calculation_of_the_LTP_parameters (
+	struct gsm_state * st,		/*              IN 	*/
+	register int16_t	* d,		/* [0..39]	IN	*/
+	register int16_t	* dp,		/* [-120..-1]	IN	*/
+	int16_t		* bc_out,	/* 		OUT	*/
+	int16_t		* Nc_out	/* 		OUT	*/)
+{
+	register int	k, lambda ;
+	int16_t		Nc, bc ;
+	int16_t		ltp_cut ;
+
+	float		wt_float [40] ;
+	float		dp_float_base [120], * dp_float = dp_float_base + 120 ;
+
+	int32_t	L_max, L_power ;
+	int16_t		R, S, dmax, scal ;
+	register int16_t	temp ;
+
+	/*  Search of the optimum scaling of d [0..39].
+	 */
+	dmax = 0 ;
+
+	for (k = 0 ; k <= 39 ; k++)
+	{	temp = d [k] ;
+		temp = GSM_ABS (temp) ;
+		if (temp > dmax) dmax = temp ;
+		}
+
+	temp = 0 ;
+	if (dmax == 0) scal = 0 ;
+	else
+	{	assert (dmax > 0) ;
+		temp = gsm_norm ((int32_t) dmax << 16) ;
+		}
+
+	if (temp > 6) scal = 0 ;
+	else scal = 6 - temp ;
+
+	assert (scal >= 0) ;
+	ltp_cut = (int32_t) SASR_W (dmax, scal) * st->ltp_cut / 100 ;
+
+	/*  Initialization of a working array wt */
+
+	for (k = 0 ; k < 40 ; k++)
+	{	register int16_t w = SASR_W (d [k], scal) ;
+		if (w < 0 ? w > -ltp_cut : w < ltp_cut)
+			wt_float [k] = 0.0 ;
+		else
+			wt_float [k] = w ;
+		}
+	for (k = -120 ; k < 0 ; k++) dp_float [k] = dp [k] ;
+
+	/* Search for the maximum cross-correlation and coding of the LTP lag
+	 */
+	L_max = 0 ;
+	Nc = 40 ;	/* index for the maximum cross-correlation */
+
+	for (lambda = 40 ; lambda <= 120 ; lambda += 9)
+	{	/*  Calculate L_result for l = lambda .. lambda + 9. */
+		register float *lp = dp_float - lambda ;
+
+		register float W ;
+		register float a = lp [-8], b = lp [-7], c = lp [-6],
+						d = lp [-5], e = lp [-4], f = lp [-3],
+						g = lp [-2], h = lp [-1] ;
+		register float E ;
+		register float S0 = 0, S1 = 0, S2 = 0, S3 = 0, S4 = 0,
+						S5 = 0, S6 = 0, S7 = 0, S8 = 0 ;
+
+#		undef STEP
+#		define	STEP(K, a, b, c, d, e, f, g, h) \
+			if ((W = wt_float [K]) != 0.0) {	\
+			E = W * a ; S8 += E ;		\
+			E = W * b ; S7 += E ;		\
+			E = W * c ; S6 += E ;		\
+			E = W * d ; S5 += E ;		\
+			E = W * e ; S4 += E ;		\
+			E = W * f ; S3 += E ;		\
+			E = W * g ; S2 += E ;		\
+			E = W * h ; S1 += E ;		\
+			a = lp [K] ;				\
+			E = W * a ; S0 += E ; } else (a = lp [K])
+
+#		define	STEP_A(K)	STEP (K, a, b, c, d, e, f, g, h)
+#		define	STEP_B(K)	STEP (K, b, c, d, e, f, g, h, a)
+#		define	STEP_C(K)	STEP (K, c, d, e, f, g, h, a, b)
+#		define	STEP_D(K)	STEP (K, d, e, f, g, h, a, b, c)
+#		define	STEP_E(K)	STEP (K, e, f, g, h, a, b, c, d)
+#		define	STEP_F(K)	STEP (K, f, g, h, a, b, c, d, e)
+#		define	STEP_G(K)	STEP (K, g, h, a, b, c, d, e, f)
+#		define	STEP_H(K)	STEP (K, h, a, b, c, d, e, f, g)
+
+		STEP_A (0) ; STEP_B (1) ; STEP_C (2) ; STEP_D (3) ;
+		STEP_E (4) ; STEP_F (5) ; STEP_G (6) ; STEP_H (7) ;
+
+		STEP_A (8) ; STEP_B (9) ; STEP_C (10) ; STEP_D (11) ;
+		STEP_E (12) ; STEP_F (13) ; STEP_G (14) ; STEP_H (15) ;
+
+		STEP_A (16) ; STEP_B (17) ; STEP_C (18) ; STEP_D (19) ;
+		STEP_E (20) ; STEP_F (21) ; STEP_G (22) ; STEP_H (23) ;
+
+		STEP_A (24) ; STEP_B (25) ; STEP_C (26) ; STEP_D (27) ;
+		STEP_E (28) ; STEP_F (29) ; STEP_G (30) ; STEP_H (31) ;
+
+		STEP_A (32) ; STEP_B (33) ; STEP_C (34) ; STEP_D (35) ;
+		STEP_E (36) ; STEP_F (37) ; STEP_G (38) ; STEP_H (39) ;
+
+#		undef STEP_A
+#		undef STEP_B
+#		undef STEP_C
+#		undef STEP_D
+#		undef STEP_E
+#		undef STEP_F
+#		undef STEP_G
+#		undef STEP_H
+
+		if (S0 > L_max) { L_max = S0 ; Nc = lambda ; }
+		if (S1 > L_max) { L_max = S1 ; Nc = lambda + 1 ; }
+		if (S2 > L_max) { L_max = S2 ; Nc = lambda + 2 ; }
+		if (S3 > L_max) { L_max = S3 ; Nc = lambda + 3 ; }
+		if (S4 > L_max) { L_max = S4 ; Nc = lambda + 4 ; }
+		if (S5 > L_max) { L_max = S5 ; Nc = lambda + 5 ; }
+		if (S6 > L_max) { L_max = S6 ; Nc = lambda + 6 ; }
+		if (S7 > L_max) { L_max = S7 ; Nc = lambda + 7 ; }
+		if (S8 > L_max) { L_max = S8 ; Nc = lambda + 8 ; }
+
+	}
+	*Nc_out = Nc ;
+
+	L_max <<= 1 ;
+
+	/*  Rescaling of L_max
+	 */
+	assert (scal <= 100 && scal >= -100) ;
+	L_max = L_max >> (6 - scal) ;	/* sub (6, scal) */
+
+	assert (Nc <= 120 && Nc >= 40) ;
+
+	/*   Compute the power of the reconstructed short term residual
+	 *   signal dp [..]
+	 */
+	L_power = 0 ;
+	for (k = 0 ; k <= 39 ; k++)
+	{	register int32_t L_temp ;
+
+		L_temp = SASR_W (dp [k - Nc], 3) ;
+		L_power += L_temp * L_temp ;
+		}
+	L_power <<= 1 ;	/* from L_MULT */
+
+	/*  Normalization of L_max and L_power
+	 */
+
+	if (L_max <= 0)
+	{	*bc_out = 0 ;
+		return ;
+		}
+	if (L_max >= L_power)
+	{	*bc_out = 3 ;
+		return ;
+		}
+
+	temp = gsm_norm (L_power) ;
+
+	R = SASR (L_max << temp, 16) ;
+	S = SASR (L_power << temp, 16) ;
+
+	/*  Coding of the LTP gain
+	 */
+
+	/*  Table 4.3a must be used to obtain the level DLB [i] for the
+	 *  quantization of the LTP gain b to get the coded version bc.
+	 */
+	for (bc = 0 ; bc <= 2 ; bc++) if (R <= gsm_mult (S, gsm_DLB [bc])) break ;
+	*bc_out = bc ;
+}
+
+#endif /* LTP_CUT */
+
+static void Calculation_of_the_LTP_parameters (
+	register int16_t	* din,		/* [0..39]	IN	*/
+	register int16_t	* dp,		/* [-120..-1]	IN	*/
+	int16_t		* bc_out,	/* 		OUT	*/
+	int16_t		* Nc_out	/* 		OUT	*/)
+{
+	register int	k, lambda ;
+	int16_t	Nc, bc ;
+
+	float	wt_float [40] ;
+	float	dp_float_base [120], * dp_float = dp_float_base + 120 ;
+
+	int32_t	L_max, L_power ;
+	int16_t		R, S, dmax, scal ;
+	register int16_t	temp ;
+
+	/*  Search of the optimum scaling of d [0..39].
+	 */
+	dmax = 0 ;
+
+	for (k = 0 ; k <= 39 ; k++)
+	{	temp = din [k] ;
+		temp = GSM_ABS (temp) ;
+		if (temp > dmax) dmax = temp ;
+		}
+
+	temp = 0 ;
+	if (dmax == 0) scal = 0 ;
+	else
+	{	assert (dmax > 0) ;
+		temp = gsm_norm ((int32_t) dmax << 16) ;
+		}
+
+	if (temp > 6) scal = 0 ;
+	else scal = 6 - temp ;
+
+	assert (scal >= 0) ;
+
+	/*  Initialization of a working array wt */
+
+	for (k = 0 ; k < 40 ; k++)		wt_float [k] = SASR_W (din [k], scal) ;
+	for (k = -120 ; k < 0 ; k++)	dp_float [k] = dp [k] ;
+
+	/* Search for the maximum cross-correlation and coding of the LTP lag
+	 */
+	L_max = 0 ;
+	Nc = 40 ;	/* index for the maximum cross-correlation */
+
+	for (lambda = 40 ; lambda <= 120 ; lambda += 9)
+	{	/*  Calculate L_result for l = lambda .. lambda + 9. */
+		register float *lp = dp_float - lambda ;
+
+		register float W ;
+		register float a = lp [-8], b = lp [-7], c = lp [-6],
+						d = lp [-5], e = lp [-4], f = lp [-3],
+						g = lp [-2], h = lp [-1] ;
+		register float E ;
+		register float S0 = 0, S1 = 0, S2 = 0, S3 = 0, S4 = 0,
+						S5 = 0, S6 = 0, S7 = 0, S8 = 0 ;
+
+#		undef STEP
+#		define	STEP(K, a, b, c, d, e, f, g, h) \
+			W = wt_float [K] ;		\
+			E = W * a ; S8 += E ;		\
+			E = W * b ; S7 += E ;		\
+			E = W * c ; S6 += E ;		\
+			E = W * d ; S5 += E ;		\
+			E = W * e ; S4 += E ;		\
+			E = W * f ; S3 += E ;		\
+			E = W * g ; S2 += E ;		\
+			E = W * h ; S1 += E ;		\
+			a = lp [K] ;				\
+			E = W * a ; S0 += E
+
+#		define	STEP_A(K)	STEP (K, a, b, c, d, e, f, g, h)
+#		define	STEP_B(K)	STEP (K, b, c, d, e, f, g, h, a)
+#		define	STEP_C(K)	STEP (K, c, d, e, f, g, h, a, b)
+#		define	STEP_D(K)	STEP (K, d, e, f, g, h, a, b, c)
+#		define	STEP_E(K)	STEP (K, e, f, g, h, a, b, c, d)
+#		define	STEP_F(K)	STEP (K, f, g, h, a, b, c, d, e)
+#		define	STEP_G(K)	STEP (K, g, h, a, b, c, d, e, f)
+#		define	STEP_H(K)	STEP (K, h, a, b, c, d, e, f, g)
+
+		STEP_A (0) ; STEP_B (1) ; STEP_C (2) ; STEP_D (3) ;
+		STEP_E (4) ; STEP_F (5) ; STEP_G (6) ; STEP_H (7) ;
+
+		STEP_A (8) ; STEP_B (9) ; STEP_C (10) ; STEP_D (11) ;
+		STEP_E (12) ; STEP_F (13) ; STEP_G (14) ; STEP_H (15) ;
+
+		STEP_A (16) ; STEP_B (17) ; STEP_C (18) ; STEP_D (19) ;
+		STEP_E (20) ; STEP_F (21) ; STEP_G (22) ; STEP_H (23) ;
+
+		STEP_A (24) ; STEP_B (25) ; STEP_C (26) ; STEP_D (27) ;
+		STEP_E (28) ; STEP_F (29) ; STEP_G (30) ; STEP_H (31) ;
+
+		STEP_A (32) ; STEP_B (33) ; STEP_C (34) ; STEP_D (35) ;
+		STEP_E (36) ; STEP_F (37) ; STEP_G (38) ; STEP_H (39) ;
+
+#		undef STEP_A
+#		undef STEP_B
+#		undef STEP_C
+#		undef STEP_D
+#		undef STEP_E
+#		undef STEP_F
+#		undef STEP_G
+#		undef STEP_H
+
+		if (S0 > L_max) { L_max = S0 ; Nc = lambda ; }
+		if (S1 > L_max) { L_max = S1 ; Nc = lambda + 1 ; }
+		if (S2 > L_max) { L_max = S2 ; Nc = lambda + 2 ; }
+		if (S3 > L_max) { L_max = S3 ; Nc = lambda + 3 ; }
+		if (S4 > L_max) { L_max = S4 ; Nc = lambda + 4 ; }
+		if (S5 > L_max) { L_max = S5 ; Nc = lambda + 5 ; }
+		if (S6 > L_max) { L_max = S6 ; Nc = lambda + 6 ; }
+		if (S7 > L_max) { L_max = S7 ; Nc = lambda + 7 ; }
+		if (S8 > L_max) { L_max = S8 ; Nc = lambda + 8 ; }
+	}
+	*Nc_out = Nc ;
+
+	L_max <<= 1 ;
+
+	/*  Rescaling of L_max
+	 */
+	assert (scal <= 100 && scal >= -100) ;
+	L_max = L_max >> (6 - scal) ;	/* sub (6, scal) */
+
+	assert (Nc <= 120 && Nc >= 40) ;
+
+	/*   Compute the power of the reconstructed short term residual
+	 *   signal dp [..]
+	 */
+	L_power = 0 ;
+	for (k = 0 ; k <= 39 ; k++)
+	{	register int32_t L_temp ;
+
+		L_temp = SASR_W (dp [k - Nc], 3) ;
+		L_power += L_temp * L_temp ;
+		}
+	L_power <<= 1 ;	/* from L_MULT */
+
+	/*  Normalization of L_max and L_power
+	 */
+
+	if (L_max <= 0)
+	{	*bc_out = 0 ;
+		return ;
+		}
+	if (L_max >= L_power)
+	{	*bc_out = 3 ;
+		return ;
+		}
+
+	temp = gsm_norm (L_power) ;
+
+	R = SASR_L (L_max << temp, 16) ;
+	S = SASR_L (L_power << temp, 16) ;
+
+	/*  Coding of the LTP gain
+	 */
+
+	/*  Table 4.3a must be used to obtain the level DLB [i] for the
+	 *  quantization of the LTP gain b to get the coded version bc.
+	 */
+	for (bc = 0 ; bc <= 2 ; bc++) if (R <= gsm_mult (S, gsm_DLB [bc])) break ;
+	*bc_out = bc ;
+}
+
+#ifdef	FAST
+#ifdef	LTP_CUT
+
+static void Cut_Fast_Calculation_of_the_LTP_parameters (
+	struct gsm_state * st,		/*              IN	*/
+	register int16_t	* d,		/* [0..39]	IN	*/
+	register int16_t	* dp,		/* [-120..-1]	IN	*/
+	int16_t		* bc_out,	/* 		OUT	*/
+	int16_t		* Nc_out	/* 		OUT	*/)
+{
+	register int	k, lambda ;
+	register float	wt_float ;
+	int16_t	Nc, bc ;
+	int16_t	wt_max, best_k, ltp_cut ;
+
+	float		dp_float_base [120], * dp_float = dp_float_base + 120 ;
+
+	register float	L_result, L_max, L_power ;
+
+	wt_max = 0 ;
+
+	for (k = 0 ; k < 40 ; ++k)
+	{	if (d [k] > wt_max) wt_max = d [best_k = k] ;
+		else if (-d [k] > wt_max) wt_max = -d [best_k = k] ;
+		}
+
+	assert (wt_max >= 0) ;
+	wt_float = (float) wt_max ;
+
+	for (k = -120 ; k < 0 ; ++k) dp_float [k] = (float) dp [k] ;
+
+	/* Search for the maximum cross-correlation and coding of the LTP lag */
+	L_max = 0 ;
+	Nc = 40 ;	/* index for the maximum cross-correlation */
+
+	for (lambda = 40 ; lambda <= 120 ; lambda++)
+	{	L_result = wt_float * dp_float [best_k - lambda] ;
+		if (L_result > L_max)
+		{	Nc = lambda ;
+			L_max = L_result ;
+			}
+		}
+
+	*Nc_out = Nc ;
+	if (L_max <= 0.)
+	{	*bc_out = 0 ;
+		return ;
+		}
+
+	/*  Compute the power of the reconstructed short term residual
+	 *  signal dp [..]
+	 */
+	dp_float -= Nc ;
+	L_power = 0 ;
+	for (k = 0 ; k < 40 ; ++k)
+	{	register float f = dp_float [k] ;
+		L_power += f * f ;
+		}
+
+	if (L_max >= L_power)
+	{	*bc_out = 3 ;
+		return ;
+		}
+
+	/*  Coding of the LTP gain
+	 *  Table 4.3a must be used to obtain the level DLB [i] for the
+	 *  quantization of the LTP gain b to get the coded version bc.
+	 */
+	lambda = L_max / L_power * 32768.0 ;
+	for (bc = 0 ; bc <= 2 ; ++bc) if (lambda <= gsm_DLB [bc]) break ;
+	*bc_out = bc ;
+}
+
+#endif /* LTP_CUT */
+
+static void Fast_Calculation_of_the_LTP_parameters (
+	register int16_t	* din,		/* [0..39]	IN	*/
+	register int16_t	* dp,		/* [-120..-1]	IN	*/
+	int16_t		* bc_out,	/* 		OUT	*/
+	int16_t		* Nc_out	/* 		OUT	*/)
+{
+	register int	k, lambda ;
+	int16_t			Nc, bc ;
+
+	float		wt_float [40] ;
+	float		dp_float_base [120], * dp_float = dp_float_base + 120 ;
+
+	register float	L_max, L_power ;
+
+	for (k = 0 ; k < 40 ; ++k) wt_float [k] = (float) din [k] ;
+	for (k = -120 ; k < 0 ; ++k) dp_float [k] = (float) dp [k] ;
+
+	/* Search for the maximum cross-correlation and coding of the LTP lag */
+	L_max = 0 ;
+	Nc = 40 ;	/* index for the maximum cross-correlation */
+
+	for (lambda = 40 ; lambda <= 120 ; lambda += 9)
+	{	/*  Calculate L_result for l = lambda .. lambda + 9. */
+		register float *lp = dp_float - lambda ;
+
+		register float W ;
+		register float a = lp [-8], b = lp [-7], c = lp [-6],
+						d = lp [-5], e = lp [-4], f = lp [-3],
+						g = lp [-2], h = lp [-1] ;
+		register float E ;
+		register float S0 = 0, S1 = 0, S2 = 0, S3 = 0, S4 = 0,
+						S5 = 0, S6 = 0, S7 = 0, S8 = 0 ;
+
+#		undef STEP
+#		define	STEP(K, a, b, c, d, e, f, g, h) \
+			W = wt_float [K] ;		\
+			E = W * a ; S8 += E ;		\
+			E = W * b ; S7 += E ;		\
+			E = W * c ; S6 += E ;		\
+			E = W * d ; S5 += E ;		\
+			E = W * e ; S4 += E ;		\
+			E = W * f ; S3 += E ;		\
+			E = W * g ; S2 += E ;		\
+			E = W * h ; S1 += E ;		\
+			a = lp [K] ;				\
+			E = W * a ; S0 += E
+
+#		define	STEP_A(K)	STEP (K, a, b, c, d, e, f, g, h)
+#		define	STEP_B(K)	STEP (K, b, c, d, e, f, g, h, a)
+#		define	STEP_C(K)	STEP (K, c, d, e, f, g, h, a, b)
+#		define	STEP_D(K)	STEP (K, d, e, f, g, h, a, b, c)
+#		define	STEP_E(K)	STEP (K, e, f, g, h, a, b, c, d)
+#		define	STEP_F(K)	STEP (K, f, g, h, a, b, c, d, e)
+#		define	STEP_G(K)	STEP (K, g, h, a, b, c, d, e, f)
+#		define	STEP_H(K)	STEP (K, h, a, b, c, d, e, f, g)
+
+		STEP_A (0) ; STEP_B (1) ; STEP_C (2) ; STEP_D (3) ;
+		STEP_E (4) ; STEP_F (5) ; STEP_G (6) ; STEP_H (7) ;
+
+		STEP_A (8) ; STEP_B (9) ; STEP_C (10) ; STEP_D (11) ;
+		STEP_E (12) ; STEP_F (13) ; STEP_G (14) ; STEP_H (15) ;
+
+		STEP_A (16) ; STEP_B (17) ; STEP_C (18) ; STEP_D (19) ;
+		STEP_E (20) ; STEP_F (21) ; STEP_G (22) ; STEP_H (23) ;
+
+		STEP_A (24) ; STEP_B (25) ; STEP_C (26) ; STEP_D (27) ;
+		STEP_E (28) ; STEP_F (29) ; STEP_G (30) ; STEP_H (31) ;
+
+		STEP_A (32) ; STEP_B (33) ; STEP_C (34) ; STEP_D (35) ;
+		STEP_E (36) ; STEP_F (37) ; STEP_G (38) ; STEP_H (39) ;
+
+		if (S0 > L_max) { L_max = S0 ; Nc = lambda ; }
+		if (S1 > L_max) { L_max = S1 ; Nc = lambda + 1 ; }
+		if (S2 > L_max) { L_max = S2 ; Nc = lambda + 2 ; }
+		if (S3 > L_max) { L_max = S3 ; Nc = lambda + 3 ; }
+		if (S4 > L_max) { L_max = S4 ; Nc = lambda + 4 ; }
+		if (S5 > L_max) { L_max = S5 ; Nc = lambda + 5 ; }
+		if (S6 > L_max) { L_max = S6 ; Nc = lambda + 6 ; }
+		if (S7 > L_max) { L_max = S7 ; Nc = lambda + 7 ; }
+		if (S8 > L_max) { L_max = S8 ; Nc = lambda + 8 ; }
+	}
+	*Nc_out = Nc ;
+
+	if (L_max <= 0.0)
+	{	*bc_out = 0 ;
+		return ;
+		}
+
+	/*  Compute the power of the reconstructed short term residual
+	 *  signal dp [..]
+	 */
+	dp_float -= Nc ;
+	L_power = 0 ;
+	for (k = 0 ; k < 40 ; ++k)
+	{	register float f = dp_float [k] ;
+		L_power += f * f ;
+		}
+
+	if (L_max >= L_power)
+	{	*bc_out = 3 ;
+		return ;
+		}
+
+	/*  Coding of the LTP gain
+	 *  Table 4.3a must be used to obtain the level DLB [i] for the
+	 *  quantization of the LTP gain b to get the coded version bc.
+	 */
+	lambda = L_max / L_power * 32768.0 ;
+	for (bc = 0 ; bc <= 2 ; ++bc) if (lambda <= gsm_DLB [bc]) break ;
+	*bc_out = bc ;
+}
+
+#endif	/* FAST 	 */
+#endif	/* USE_FLOAT_MUL */
+
+
+/* 4.2.12 */
+
+static void Long_term_analysis_filtering (
+	int16_t		bc,	/* 					IN  */
+	int16_t		Nc,	/* 					IN  */
+	register int16_t	* dp,	/* previous d	[-120..-1]		IN  */
+	register int16_t	* d,	/* d		[0..39]			IN  */
+	register int16_t	* dpp,	/* estimate	[0..39]			OUT */
+	register int16_t	* e	/* long term res. signal [0..39]	OUT */)
+/*
+ *  In this part, we have to decode the bc parameter to compute
+ *  the samples of the estimate dpp [0..39].  The decoding of bc needs the
+ *  use of table 4.3b.  The long term residual signal e [0..39]
+ *  is then calculated to be fed to the RPE encoding section.
+ */
+{
+	register int k ;
+
+#	undef STEP
+#	define STEP(BP)					\
+	for (k = 0 ; k <= 39 ; k++)		\
+	{	dpp [k] = GSM_MULT_R (BP, dp [k - Nc]) ;	\
+		e [k]	= GSM_SUB (d [k], dpp [k]) ;	\
+		}
+
+	switch (bc)
+	{	case 0:	STEP (3277) ; break ;
+		case 1:	STEP (11469) ; break ;
+		case 2: STEP (21299) ; break ;
+		case 3: STEP (32767) ; break ;
+		}
+}
+
+void Gsm_Long_Term_Predictor (	/* 4x for 160 samples */
+
+	struct gsm_state	* S,
+
+	int16_t	* d,	/* [0..39]   residual signal	IN	*/
+	int16_t	* dp,	/* [-120..-1] d'		IN	*/
+
+	int16_t	* e,	/* [0..39] 			OUT	*/
+	int16_t	* dpp,	/* [0..39] 			OUT	*/
+	int16_t	* Nc,	/* correlation lag		OUT	*/
+	int16_t	* bc	/* gain factor			OUT	*/)
+{
+	assert (d) ; assert (dp) ; assert (e) ;
+	assert (dpp) ; assert (Nc) ; assert (bc) ;
+
+#if defined (FAST) && defined (USE_FLOAT_MUL)
+	if (S->fast)
+#if defined (LTP_CUT)
+		if (S->ltp_cut)
+			Cut_Fast_Calculation_of_the_LTP_parameters (S,
+				d, dp, bc, Nc) ;
+		else
+#endif /* LTP_CUT */
+			Fast_Calculation_of_the_LTP_parameters (d, dp, bc, Nc) ;
+	else
+#endif /* FAST & USE_FLOAT_MUL */
+#ifdef LTP_CUT
+		if (S->ltp_cut)
+			Cut_Calculation_of_the_LTP_parameters (S, d, dp, bc, Nc) ;
+		else
+#endif
+			Calculation_of_the_LTP_parameters (d, dp, bc, Nc) ;
+
+	Long_term_analysis_filtering (*bc, *Nc, dp, d, dpp, e) ;
+}
+
+/* 4.3.2 */
+void Gsm_Long_Term_Synthesis_Filtering (
+	struct gsm_state	* S,
+
+	int16_t			Ncr,
+	int16_t			bcr,
+	register int16_t	* erp,	/* [0..39]		  	 IN */
+	register int16_t	* drp	/* [-120..-1] IN, [-120..40] OUT */)
+/*
+ *  This procedure uses the bcr and Ncr parameter to realize the
+ *  long term synthesis filtering.  The decoding of bcr needs
+ *  table 4.3b.
+ */
+{
+	register int 		k ;
+	int16_t			brp, drpp, Nr ;
+
+	/*  Check the limits of Nr.
+	 */
+	Nr = Ncr < 40 || Ncr > 120 ? S->nrp : Ncr ;
+	S->nrp = Nr ;
+	assert (Nr >= 40 && Nr <= 120) ;
+
+	/*  Decoding of the LTP gain bcr
+	 */
+	brp = gsm_QLB [bcr] ;
+
+	/*  Computation of the reconstructed short term residual
+	 *  signal drp [0..39]
+	 */
+	assert (brp != MIN_WORD) ;
+
+	for (k = 0 ; k <= 39 ; k++)
+	{	drpp = GSM_MULT_R (brp, drp [k - Nr]) ;
+		drp [k] = GSM_ADD (erp [k], drpp) ;
+		}
+
+	/*
+	 *  Update of the reconstructed short term residual signal
+	 *  drp [-1..-120]
+	 */
+
+	for (k = 0 ; k <= 119 ; k++) drp [-120 + k] = drp [-80 + k] ;
+}
diff --git a/src/GSM610/lpc.c b/src/GSM610/lpc.c
new file mode 100644
index 0000000..922421e
--- /dev/null
+++ b/src/GSM610/lpc.c
@@ -0,0 +1,333 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin.  See the accompanying file "COPYRIGHT" for
+ * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include "gsm610_priv.h"
+
+/*
+ *  4.2.4 .. 4.2.7 LPC ANALYSIS SECTION
+ */
+
+/* 4.2.4 */
+
+
+static void Autocorrelation (
+	int16_t		* s,		/* [0..159]	IN/OUT  */
+ 	int32_t	* L_ACF)	/* [0..8]	OUT     */
+/*
+ *  The goal is to compute the array L_ACF [k].  The signal s [i] must
+ *  be scaled in order to avoid an overflow situation.
+ */
+{
+	register int	k, i ;
+
+	int16_t		temp, smax, scalauto ;
+
+#ifdef	USE_FLOAT_MUL
+	float		float_s [160] ;
+#endif
+
+	/*  Dynamic scaling of the array  s [0..159] */
+
+	/*  Search for the maximum. */
+	smax = 0 ;
+	for (k = 0 ; k <= 159 ; k++)
+	{	temp = GSM_ABS (s [k]) ;
+		if (temp > smax) smax = temp ;
+		}
+
+	/*  Computation of the scaling factor.
+	 */
+	if (smax == 0)
+		scalauto = 0 ;
+	else
+	{	assert (smax > 0) ;
+		scalauto = 4 - gsm_norm ((int32_t) smax << 16) ;	/* sub (4,..) */
+		}
+
+	/*  Scaling of the array s [0...159]
+	 */
+
+	if (scalauto > 0)
+	{
+
+# ifdef USE_FLOAT_MUL
+#	define SCALE(n)	\
+	case n: for (k = 0 ; k <= 159 ; k++) \
+			float_s [k] = (float)	\
+				(s [k] = GSM_MULT_R (s [k], 16384 >> (n-1))) ;\
+		break ;
+# else
+#	define SCALE(n)	\
+	case n: for (k = 0 ; k <= 159 ; k++) \
+			s [k] = GSM_MULT_R (s [k], 16384 >> (n-1)) ;\
+		break ;
+# endif /* USE_FLOAT_MUL */
+
+		switch (scalauto) {
+		SCALE (1)
+		SCALE (2)
+		SCALE (3)
+		SCALE (4)
+		}
+# undef	SCALE
+	}
+# ifdef	USE_FLOAT_MUL
+	else for (k = 0 ; k <= 159 ; k++) float_s [k] = (float) s [k] ;
+# endif
+
+	/*  Compute the L_ACF [..].
+	 */
+	{
+# ifdef	USE_FLOAT_MUL
+		register float	*sp = float_s ;
+		register float	sl = *sp ;
+
+#		define STEP(k)	L_ACF [k] += (int32_t) (sl * sp [- (k)]) ;
+# else
+		int16_t	*sp = s ;
+		int16_t	sl = *sp ;
+
+#		define STEP(k)	L_ACF [k] += ((int32_t) sl * sp [- (k)]) ;
+# endif
+
+#	define NEXTI	sl = *++sp
+
+
+	for (k = 9 ; k-- ; L_ACF [k] = 0) ;
+
+	STEP (0) ;
+	NEXTI ;
+	STEP (0) ; STEP (1) ;
+	NEXTI ;
+	STEP (0) ; STEP (1) ; STEP (2) ;
+	NEXTI ;
+	STEP (0) ; STEP (1) ; STEP (2) ; STEP (3) ;
+	NEXTI ;
+	STEP (0) ; STEP (1) ; STEP (2) ; STEP (3) ; STEP (4) ;
+	NEXTI ;
+	STEP (0) ; STEP (1) ; STEP (2) ; STEP (3) ; STEP (4) ; STEP (5) ;
+	NEXTI ;
+	STEP (0) ; STEP (1) ; STEP (2) ; STEP (3) ; STEP (4) ; STEP (5) ; STEP (6) ;
+	NEXTI ;
+	STEP (0) ; STEP (1) ; STEP (2) ; STEP (3) ; STEP (4) ; STEP (5) ; STEP (6) ; STEP (7) ;
+
+	for (i = 8 ; i <= 159 ; i++)
+	{	NEXTI ;
+
+		STEP (0) ;
+		STEP (1) ; STEP (2) ; STEP (3) ; STEP (4) ;
+		STEP (5) ; STEP (6) ; STEP (7) ; STEP (8) ;
+		}
+
+	for (k = 9 ; k-- ; )
+		L_ACF [k] = SASL_L (L_ACF [k], 1) ;
+
+	}
+	/*   Rescaling of the array s [0..159]
+	 */
+	if (scalauto > 0)
+	{	assert (scalauto <= 4) ;
+		for (k = 160 ; k-- ; s++)
+			*s = SASL_W (*s, scalauto) ;
+		}
+}
+
+#if defined (USE_FLOAT_MUL) && defined (FAST)
+
+static void Fast_Autocorrelation (
+	int16_t * s,		/* [0..159]	IN/OUT  */
+ 	int32_t * L_ACF)	/* [0..8]	OUT     */
+{
+	register int	k, i ;
+	float f_L_ACF [9] ;
+	float scale ;
+
+	float			s_f [160] ;
+	register float *sf = s_f ;
+
+	for (i = 0 ; i < 160 ; ++i) sf [i] = s [i] ;
+	for (k = 0 ; k <= 8 ; k++)
+	{	register float L_temp2 = 0 ;
+		register float *sfl = sf - k ;
+		for (i = k ; i < 160 ; ++i) L_temp2 += sf [i] * sfl [i] ;
+		f_L_ACF [k] = L_temp2 ;
+		}
+	scale = 2147483648.0f / f_L_ACF [0] ;
+
+	for (k = 0 ; k <= 8 ; k++)
+		L_ACF [k] = f_L_ACF [k] * scale ;
+}
+#endif	/* defined (USE_FLOAT_MUL) && defined (FAST) */
+
+/* 4.2.5 */
+
+static void Reflection_coefficients (
+	int32_t	* L_ACF,		/* 0...8	IN	*/
+	register int16_t	* r			/* 0...7	OUT 	*/
+)
+{
+	register int	i, m, n ;
+	register int16_t	temp ;
+	int16_t		ACF [9] ;	/* 0..8 */
+	int16_t		P [9] ;	/* 0..8 */
+	int16_t		K [9] ; /* 2..8 */
+
+	/*  Schur recursion with 16 bits arithmetic.
+	 */
+
+	if (L_ACF [0] == 0)
+	{	memset (r, 0, 8 * sizeof (r [0])) ;
+		return ;
+		}
+
+	assert (L_ACF [0] != 0) ;
+	temp = gsm_norm (L_ACF [0]) ;
+
+	assert (temp >= 0 && temp < 32) ;
+
+	/* ? overflow ? */
+	for (i = 0 ; i <= 8 ; i++) ACF [i] = SASR_L (SASL_L (L_ACF [i], temp), 16) ;
+
+	/*   Initialize array P [..] and K [..] for the recursion.
+	 */
+
+	for (i = 1 ; i <= 7 ; i++) K [i] = ACF [i] ;
+	for (i = 0 ; i <= 8 ; i++) P [i] = ACF [i] ;
+
+	/*   Compute reflection coefficients
+	 */
+	for (n = 1 ; n <= 8 ; n++, r++)
+	{	temp = P [1] ;
+		temp = GSM_ABS (temp) ;
+		if (P [0] < temp)
+		{	for (i = n ; i <= 8 ; i++) *r++ = 0 ;
+			return ;
+			}
+
+		*r = gsm_div (temp, P [0]) ;
+
+		assert (*r >= 0) ;
+		if (P [1] > 0) *r = -*r ;		/* r [n] = sub (0, r [n]) */
+		assert (*r != MIN_WORD) ;
+		if (n == 8) return ;
+
+		/*  Schur recursion
+		 */
+		temp = GSM_MULT_R (P [1], *r) ;
+		P [0] = GSM_ADD (P [0], temp) ;
+
+		for (m = 1 ; m <= 8 - n ; m++)
+		{	temp = GSM_MULT_R (K [m], *r) ;
+			P [m] = GSM_ADD (P [m + 1], temp) ;
+
+			temp = GSM_MULT_R (P [m + 1], *r) ;
+			K [m] = GSM_ADD (K [m], temp) ;
+			}
+		}
+}
+
+/* 4.2.6 */
+
+static void Transformation_to_Log_Area_Ratios (
+	register int16_t	* r 			/* 0..7	   IN/OUT */
+)
+/*
+ *  The following scaling for r [..] and LAR [..] has been used:
+ *
+ *  r [..]   = integer (real_r [..]*32768.) ; -1 <= real_r < 1.
+ *  LAR [..] = integer (real_LAR [..] * 16384) ;
+ *  with -1.625 <= real_LAR <= 1.625
+ */
+{
+	register int16_t	temp ;
+	register int	i ;
+
+
+	/* Computation of the LAR [0..7] from the r [0..7]
+	 */
+	for (i = 1 ; i <= 8 ; i++, r++)
+	{	temp = *r ;
+		temp = GSM_ABS (temp) ;
+		assert (temp >= 0) ;
+
+		if (temp < 22118)
+		{	temp >>= 1 ;
+			}
+		else if (temp < 31130)
+		{	assert (temp >= 11059) ;
+			temp -= 11059 ;
+			}
+		else
+		{	assert (temp >= 26112) ;
+			temp -= 26112 ;
+			temp <<= 2 ;
+			}
+
+		*r = *r < 0 ? -temp : temp ;
+		assert (*r != MIN_WORD) ;
+	}
+}
+
+/* 4.2.7 */
+
+static void Quantization_and_coding (
+	register int16_t * LAR		/* [0..7]	IN/OUT	*/
+)
+{
+	register int16_t	temp ;
+
+	/*  This procedure needs four tables ; the following equations
+	 *  give the optimum scaling for the constants:
+	 *
+	 *  A [0..7] = integer (real_A [0..7] * 1024)
+	 *  B [0..7] = integer (real_B [0..7] *  512)
+	 *  MAC [0..7] = maximum of the LARc [0..7]
+	 *  MIC [0..7] = minimum of the LARc [0..7]
+	 */
+
+#	undef STEP
+#	define	STEP(A, B, MAC, MIC)	\
+		temp = GSM_MULT (A, *LAR) ;	\
+		temp = GSM_ADD (temp, B) ;	\
+		temp = GSM_ADD (temp, 256) ;	\
+		temp = SASR_W (temp, 9) ;	\
+		*LAR = temp > MAC ? MAC - MIC : (temp < MIC ? 0 : temp - MIC) ; \
+		LAR++ ;
+
+	STEP (20480, 0, 31, -32) ;
+	STEP (20480, 0, 31, -32) ;
+	STEP (20480, 2048, 15, -16) ;
+	STEP (20480, -2560, 15, -16) ;
+
+	STEP (13964, 94, 7, -8) ;
+	STEP (15360, -1792, 7, -8) ;
+	STEP (8534, -341, 3, -4) ;
+	STEP (9036, -1144, 3, -4) ;
+
+#	undef	STEP
+}
+
+void Gsm_LPC_Analysis (
+	struct gsm_state *S,
+	int16_t			* s,	/* 0..159 signals	IN/OUT	*/
+	int16_t			*LARc)	/* 0..7   LARc's	OUT	*/
+{
+	int32_t	L_ACF [9] ;
+
+#if defined (USE_FLOAT_MUL) && defined (FAST)
+	if (S->fast)
+		Fast_Autocorrelation (s, L_ACF) ;
+	else
+#endif
+		Autocorrelation (s,	L_ACF	) ;
+	Reflection_coefficients (L_ACF, LARc	) ;
+	Transformation_to_Log_Area_Ratios (LARc) ;
+	Quantization_and_coding (LARc) ;
+}
diff --git a/src/GSM610/preprocess.c b/src/GSM610/preprocess.c
new file mode 100644
index 0000000..82f4fe2
--- /dev/null
+++ b/src/GSM610/preprocess.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin.  See the accompanying file "COPYRIGHT" for
+ * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+#include	<stdio.h>
+#include	<assert.h>
+
+#include "gsm610_priv.h"
+
+/*	4.2.0 .. 4.2.3	PREPROCESSING SECTION
+ *
+ *  	After A-law to linear conversion (or directly from the
+ *   	Ato D converter) the following scaling is assumed for
+ * 	input to the RPE-LTP algorithm:
+ *
+ *      in:  0.1.....................12
+ *	     S.v.v.v.v.v.v.v.v.v.v.v.v.*.*.*
+ *
+ *	Where S is the sign bit, v a valid bit, and * a "don't care" bit.
+ * 	The original signal is called sop[..]
+ *
+ *      out:   0.1................... 12
+ *	     S.S.v.v.v.v.v.v.v.v.v.v.v.v.0.0
+ */
+
+
+void Gsm_Preprocess (
+	struct gsm_state * S,
+	int16_t		* s,
+	int16_t		* so)		/* [0..159] 	IN/OUT	*/
+{
+
+	int16_t		z1 = S->z1 ;
+	int32_t 	L_z2 = S->L_z2 ;
+	int16_t		mp = S->mp ;
+
+	int16_t		s1 ;
+	int32_t	L_s2 ;
+
+	int32_t	L_temp ;
+
+	int16_t		msp, lsp ;
+	int16_t		SO ;
+
+	register int		k = 160 ;
+
+	while (k--)
+	{
+
+		/*  4.2.1   Downscaling of the input signal */
+		SO = arith_shift_left (SASR_W (*s, 3), 2) ;
+		s++ ;
+
+		assert (SO >= -0x4000) ;	/* downscaled by     */
+		assert (SO <= 0x3FFC) ;		/* previous routine. */
+
+
+		/*  4.2.2   Offset compensation
+		 *
+		 *  This part implements a high-pass filter and requires extended
+		 *  arithmetic precision for the recursive part of this filter.
+		 *  The input of this procedure is the array so[0...159] and the
+		 *  output the array sof[ 0...159 ].
+		 */
+
+		/*   Compute the non-recursive part */
+
+		s1 = SO - z1 ;			/* s1 = gsm_sub (*so, z1) ; */
+		z1 = SO ;
+
+		assert (s1 != MIN_WORD) ;
+
+		/*   Compute the recursive part */
+		L_s2 = s1 ;
+		L_s2 = arith_shift_left (L_s2, 15) ;
+
+		/*   Execution of a 31 bv 16 bits multiplication */
+
+		msp = SASR_L (L_z2, 15) ;
+		lsp = L_z2 - arith_shift_left ((int32_t) msp, 15) ; /* gsm_L_sub (L_z2,(msp<<15)) ; */
+
+		L_s2 += GSM_MULT_R (lsp, 32735) ;
+		L_temp = (int32_t) msp * 32735 ; /* GSM_L_MULT (msp,32735) >> 1 ;*/
+		L_z2 = GSM_L_ADD (L_temp, L_s2) ;
+
+		/*    Compute sof[k] with rounding */
+		L_temp = GSM_L_ADD (L_z2, 16384) ;
+
+		/*   4.2.3  Preemphasis */
+
+		msp	= GSM_MULT_R (mp, -28180) ;
+		mp	= SASR_L (L_temp, 15) ;
+		*so++ = GSM_ADD (mp, msp) ;
+		}
+
+	S->z1	= z1 ;
+	S->L_z2	= L_z2 ;
+	S->mp	= mp ;
+}
diff --git a/src/GSM610/rpe.c b/src/GSM610/rpe.c
new file mode 100644
index 0000000..d9e90f2
--- /dev/null
+++ b/src/GSM610/rpe.c
@@ -0,0 +1,460 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin.  See the accompanying file "COPYRIGHT" for
+ * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <assert.h>
+
+#include "gsm610_priv.h"
+
+/*  4.2.13 .. 4.2.17  RPE ENCODING SECTION
+ */
+
+/* 4.2.13 */
+
+static void Weighting_filter (
+	register int16_t	* e,		/* signal [-5..0.39.44]	IN  */
+	int16_t		* x		/* signal [0..39]	OUT */
+)
+/*
+ *  The coefficients of the weighting filter are stored in a table
+ *  (see table 4.4).  The following scaling is used:
+ *
+ *	H[0..10] = integer(real_H [0..10] * 8192) ;
+ */
+{
+	/* int16_t			wt [50] ; */
+
+	register int32_t	L_result ;
+	register int		k /* , i */ ;
+
+	/*  Initialization of a temporary working array wt[0...49]
+	 */
+
+	/* for (k =  0 ; k <=  4 ; k++) wt[k] = 0 ;
+	 * for (k =  5 ; k <= 44 ; k++) wt[k] = *e++;
+	 * for (k = 45 ; k <= 49 ; k++) wt[k] = 0 ;
+	 *
+	 *  (e[-5..-1] and e[40..44] are allocated by the caller,
+	 *  are initially zero and are not written anywhere.)
+	 */
+	e -= 5 ;
+
+	/*  Compute the signal x[0..39]
+	 */
+	for (k = 0 ; k <= 39 ; k++)
+	{	L_result = 8192 >> 1 ;
+
+		/* for (i = 0 ; i <= 10 ; i++) {
+		 *	L_temp   = GSM_L_MULT(wt[k+i], gsm_H[i]) ;
+		 *	L_result = GSM_L_ADD(L_result, L_temp) ;
+		 * }
+		 */
+
+#undef	STEP
+#define	STEP(i, H)	(e [k + i] * (int32_t) H)
+
+		/*  Every one of these multiplications is done twice --
+		 *  but I don't see an elegant way to optimize this.
+		 *  Do you?
+		 */
+
+#ifdef	STUPID_COMPILER
+		L_result += STEP (0, -134) ;
+		L_result += STEP (1, -374) ;
+					/* + STEP (2, 0)  */
+		L_result += STEP (3, 2054) ;
+		L_result += STEP (4, 5741) ;
+		L_result += STEP (5, 8192) ;
+		L_result += STEP (6, 5741) ;
+		L_result += STEP (7, 2054) ;
+					/* + STEP (8, 0)  */
+		L_result += STEP (9, -374) ;
+		L_result += STEP (10, -134) ;
+#else
+		L_result += STEP (0, -134)
+				+ STEP (1, -374)
+					/* + STEP (2, 0)  */
+				+ STEP (3, 2054)
+				+ STEP (4, 5741)
+				+ STEP (5, 8192)
+				+ STEP (6, 5741)
+				+ STEP (7, 2054)
+					/* + STEP (8, 0)  */
+				+ STEP (9, -374)
+				+ STEP (10, -134) ;
+#endif
+
+		/* L_result = GSM_L_ADD(L_result, L_result) ; (* scaling(x2) *)
+		 * L_result = GSM_L_ADD(L_result, L_result) ; (* scaling(x4) *)
+		 *
+		 * x[k] = SASR(L_result, 16) ;
+		 */
+
+		/* 2 adds vs. >>16 => 14, minus one shift to compensate for
+		 * those we lost when replacing L_MULT by '*'.
+		 */
+
+		L_result = SASR_L (L_result, 13) ;
+		x [k] = (L_result < MIN_WORD ? MIN_WORD
+			: (L_result > MAX_WORD ? MAX_WORD : L_result)) ;
+	}
+}
+
+/* 4.2.14 */
+
+static void RPE_grid_selection (
+	int16_t		* x,		/* [0..39]		IN  */
+	int16_t		* xM,		/* [0..12]		OUT */
+	int16_t		* Mc_out	/*			OUT */
+)
+/*
+ *  The signal x[0..39] is used to select the RPE grid which is
+ *  represented by Mc.
+ */
+{
+	register int		i ;
+	register int32_t	L_result, L_temp ;
+	int32_t		EM ;	/* xxx should be L_EM? */
+	int16_t			Mc ;
+
+	int32_t		L_common_0_3 ;
+
+	EM = 0 ;
+	Mc = 0 ;
+
+	/* for (m = 0 ; m <= 3 ; m++) {
+	 *	L_result = 0 ;
+	 *
+	 *
+	 *	for (i = 0 ; i <= 12 ; i++) {
+	 *
+	 *		temp1	= SASR_W (x[m + 3*i], 2) ;
+	 *
+	 *		assert (temp1 != MIN_WORD) ;
+	 *
+	 *		L_temp   = GSM_L_MULT(temp1, temp1) ;
+	 *		L_result = GSM_L_ADD(L_temp, L_result) ;
+	 *	}
+	 *
+	 *	if (L_result > EM) {
+	 *		Mc = m ;
+	 *		EM = L_result ;
+	 *	}
+	 * }
+	 */
+
+#undef	STEP
+#define	STEP(m, i)	L_temp = SASR_W (x [m + 3 * i], 2) ;	\
+					L_result += L_temp * L_temp ;
+
+	/* common part of 0 and 3 */
+
+	L_result = 0 ;
+	STEP (0, 1) ; STEP (0, 2) ; STEP (0, 3) ; STEP (0, 4) ;
+	STEP (0, 5) ; STEP (0, 6) ; STEP (0, 7) ; STEP (0, 8) ;
+	STEP (0, 9) ; STEP (0, 10) ; STEP (0, 11) ; STEP (0, 12) ;
+	L_common_0_3 = L_result ;
+
+	/* i = 0 */
+
+	STEP (0, 0) ;
+	L_result <<= 1 ;	/* implicit in L_MULT */
+	EM = L_result ;
+
+	/* i = 1 */
+
+	L_result = 0 ;
+	STEP (1, 0) ;
+	STEP (1, 1) ; STEP (1, 2) ; STEP (1, 3) ; STEP (1, 4) ;
+	STEP (1, 5) ; STEP (1, 6) ; STEP (1, 7) ; STEP (1, 8) ;
+	STEP (1, 9) ; STEP (1, 10) ; STEP (1, 11) ; STEP (1, 12) ;
+	L_result <<= 1 ;
+	if (L_result > EM)
+	{	Mc = 1 ;
+		EM = L_result ;
+		}
+
+	/* i = 2 */
+
+	L_result = 0 ;
+	STEP (2, 0) ;
+	STEP (2, 1) ; STEP (2, 2) ; STEP (2, 3) ; STEP (2, 4) ;
+	STEP (2, 5) ; STEP (2, 6) ; STEP (2, 7) ; STEP (2, 8) ;
+	STEP (2, 9) ; STEP (2, 10) ; STEP (2, 11) ; STEP (2, 12) ;
+	L_result <<= 1 ;
+	if (L_result > EM)
+	{	Mc = 2 ;
+		EM = L_result ;
+		}
+
+	/* i = 3 */
+
+	L_result = L_common_0_3 ;
+	STEP (3, 12) ;
+	L_result <<= 1 ;
+	if (L_result > EM)
+	{	Mc = 3 ;
+		EM = L_result ;
+		}
+
+	/*  Down-sampling by a factor 3 to get the selected xM [0..12]
+	 *  RPE sequence.
+	 */
+	for (i = 0 ; i <= 12 ; i ++) xM [i] = x [Mc + 3 * i] ;
+	*Mc_out = Mc ;
+}
+
+/* 4.12.15 */
+
+static void APCM_quantization_xmaxc_to_exp_mant (
+	int16_t		xmaxc,		/* IN 	*/
+	int16_t		* expon_out,	/* OUT	*/
+	int16_t		* mant_out)	/* OUT  */
+{
+	int16_t	expon, mant ;
+
+	/* Compute expononent and mantissa of the decoded version of xmaxc
+	 */
+
+	expon = 0 ;
+	if (xmaxc > 15) expon = SASR_W (xmaxc, 3) - 1 ;
+	mant = xmaxc - (expon << 3) ;
+
+	if (mant == 0)
+	{	expon = -4 ;
+		mant = 7 ;
+		}
+	else
+	{	while (mant <= 7)
+		{	mant = mant << 1 | 1 ;
+			expon-- ;
+			}
+		mant -= 8 ;
+		}
+
+	assert (expon >= -4 && expon <= 6) ;
+	assert (mant >= 0 && mant <= 7) ;
+
+	*expon_out = expon ;
+	*mant_out = mant ;
+}
+
+static void APCM_quantization (
+	int16_t		* xM,		/* [0..12]		IN	*/
+	int16_t		* xMc,		/* [0..12]		OUT	*/
+	int16_t		* mant_out,	/* 			OUT	*/
+	int16_t		* expon_out,	/*			OUT	*/
+	int16_t		* xmaxc_out	/*			OUT	*/
+)
+{
+	int	i, itest ;
+
+	int16_t	xmax, xmaxc, temp, temp1, temp2 ;
+	int16_t	expon, mant ;
+
+
+	/*  Find the maximum absolute value xmax of xM [0..12].
+	 */
+
+	xmax = 0 ;
+	for (i = 0 ; i <= 12 ; i++)
+	{	temp = xM [i] ;
+		temp = GSM_ABS (temp) ;
+		if (temp > xmax) xmax = temp ;
+		}
+
+	/*  Qantizing and coding of xmax to get xmaxc.
+	 */
+
+	expon = 0 ;
+	temp = SASR_W (xmax, 9) ;
+	itest = 0 ;
+
+	for (i = 0 ; i <= 5 ; i++)
+	{	itest |= (temp <= 0) ;
+		temp = SASR_W (temp, 1) ;
+
+		assert (expon <= 5) ;
+		if (itest == 0) expon++ ;		/* expon = add (expon, 1) */
+		}
+
+	assert (expon <= 6 && expon >= 0) ;
+	temp = expon + 5 ;
+
+	assert (temp <= 11 && temp >= 0) ;
+	xmaxc = gsm_add (SASR_W (xmax, temp), (int16_t) (expon << 3)) ;
+
+	/*   Quantizing and coding of the xM [0..12] RPE sequence
+	 *   to get the xMc [0..12]
+	 */
+
+	APCM_quantization_xmaxc_to_exp_mant (xmaxc, &expon, &mant) ;
+
+	/*  This computation uses the fact that the decoded version of xmaxc
+	 *  can be calculated by using the expononent and the mantissa part of
+	 *  xmaxc (logarithmic table).
+	 *  So, this method avoids any division and uses only a scaling
+	 *  of the RPE samples by a function of the expononent.  A direct
+	 *  multiplication by the inverse of the mantissa (NRFAC[0..7]
+	 *  found in table 4.5) gives the 3 bit coded version xMc [0..12]
+	 *  of the RPE samples.
+	 */
+
+
+	/* Direct computation of xMc [0..12] using table 4.5
+	 */
+
+	assert (expon <= 4096 && expon >= -4096) ;
+	assert (mant >= 0 && mant <= 7) ;
+
+	temp1 = 6 - expon ;			/* normalization by the expononent */
+	temp2 = gsm_NRFAC [mant] ;	/* inverse mantissa 		 */
+
+	for (i = 0 ; i <= 12 ; i++)
+	{	assert (temp1 >= 0 && temp1 < 16) ;
+
+		temp = arith_shift_left (xM [i], temp1) ;
+		temp = GSM_MULT (temp, temp2) ;
+		temp = SASR_W (temp, 12) ;
+		xMc [i] = temp + 4 ;		/* see note below */
+	}
+
+	/*  NOTE: This equation is used to make all the xMc [i] positive.
+	 */
+
+	*mant_out = mant ;
+	*expon_out = expon ;
+	*xmaxc_out = xmaxc ;
+}
+
+/* 4.2.16 */
+
+static void APCM_inverse_quantization (
+	register int16_t	* xMc,	/* [0..12]			IN 	*/
+	int16_t		mant,
+	int16_t		expon,
+	register int16_t	* xMp)	/* [0..12]			OUT 	*/
+/*
+ *  This part is for decoding the RPE sequence of coded xMc [0..12]
+ *  samples to obtain the xMp[0..12] array.  Table 4.6 is used to get
+ *  the mantissa of xmaxc (FAC[0..7]).
+ */
+{
+	int	i ;
+	int16_t	temp, temp1, temp2, temp3 ;
+
+	assert (mant >= 0 && mant <= 7) ;
+
+	temp1 = gsm_FAC [mant] ;	/* see 4.2-15 for mant */
+	temp2 = gsm_sub (6, expon) ;	/* see 4.2-15 for exp  */
+	temp3 = gsm_asl (1, gsm_sub (temp2, 1)) ;
+
+	for (i = 13 ; i-- ;)
+	{	assert (*xMc <= 7 && *xMc >= 0) ;	/* 3 bit unsigned */
+
+		/* temp = gsm_sub (*xMc++ << 1, 7) ; */
+		temp = (*xMc++ << 1) - 7 ;			/* restore sign   */
+		assert (temp <= 7 && temp >= -7) ;	/* 4 bit signed   */
+
+		temp = arith_shift_left (temp, 12) ;	/* 16 bit signed  */
+		temp = GSM_MULT_R (temp1, temp) ;
+		temp = GSM_ADD (temp, temp3) ;
+		*xMp++ = gsm_asr (temp, temp2) ;
+	}
+}
+
+/* 4.2.17 */
+
+static void RPE_grid_positioning (
+	int16_t		Mc,		/* grid position	IN	*/
+	register int16_t	* xMp,		/* [0..12]		IN	*/
+	register int16_t	* ep		/* [0..39]		OUT	*/
+)
+/*
+ *  This procedure computes the reconstructed long term residual signal
+ *  ep[0..39] for the LTP analysis filter.  The inputs are the Mc
+ *  which is the grid position selection and the xMp[0..12] decoded
+ *  RPE samples which are upsampled by a factor of 3 by inserting zero
+ *  values.
+ */
+{
+	int	i = 13 ;
+
+	assert (0 <= Mc && Mc <= 3) ;
+
+	switch (Mc)
+	{	case 3: *ep++ = 0 ;
+				/* Falls through. */
+		case 2: do
+				{	*ep++ = 0 ;
+				/* Falls through. */
+		case 1:		*ep++ = 0 ;
+				/* Falls through. */
+		case 0:		*ep++ = *xMp++ ;
+					} while (--i) ;
+	}
+	while (++Mc < 4) *ep++ = 0 ;
+}
+
+/* 4.2.18 */
+
+/*  This procedure adds the reconstructed long term residual signal
+ *  ep[0..39] to the estimated signal dpp[0..39] from the long term
+ *  analysis filter to compute the reconstructed short term residual
+ *  signal dp[-40..-1] ; also the reconstructed short term residual
+ *  array dp[-120..-41] is updated.
+ */
+
+#if 0	/* Has been inlined in code.c */
+void Gsm_Update_of_reconstructed_short_time_residual_signal (
+	int16_t	* dpp,		/* [0...39]	IN	*/
+	int16_t	* ep,		/* [0...39]	IN	*/
+	int16_t	* dp)		/* [-120...-1]  IN/OUT 	*/
+{
+	int 		k ;
+
+	for (k = 0 ; k <= 79 ; k++)
+		dp [-120 + k] = dp [-80 + k] ;
+
+	for (k = 0 ; k <= 39 ; k++)
+		dp [-40 + k] = gsm_add (ep [k], dpp [k]) ;
+}
+#endif	/* Has been inlined in code.c */
+
+void Gsm_RPE_Encoding (
+	int16_t	* e,		/* -5..-1][0..39][40..44	IN/OUT  */
+	int16_t	* xmaxc,	/* 				OUT */
+	int16_t	* Mc,		/* 			  	OUT */
+	int16_t	* xMc)		/* [0..12]			OUT */
+{
+	int16_t	x [40] ;
+	int16_t	xM [13], xMp [13] ;
+	int16_t	mant, expon ;
+
+	Weighting_filter (e, x) ;
+	RPE_grid_selection (x, xM, Mc) ;
+
+	APCM_quantization (xM, xMc, &mant, &expon, xmaxc) ;
+	APCM_inverse_quantization (xMc, mant, expon, xMp) ;
+
+	RPE_grid_positioning (*Mc, xMp, e) ;
+
+}
+
+void Gsm_RPE_Decoding (
+	int16_t 		xmaxcr,
+	int16_t		Mcr,
+	int16_t		* xMcr,	/* [0..12], 3 bits 		IN	*/
+	int16_t		* erp	/* [0..39]			OUT 	*/
+)
+{
+	int16_t	expon, mant ;
+	int16_t	xMp [13] ;
+
+	APCM_quantization_xmaxc_to_exp_mant (xmaxcr, &expon, &mant) ;
+	APCM_inverse_quantization (xMcr, mant, expon, xMp) ;
+	RPE_grid_positioning (Mcr, xMp, erp) ;
+}
diff --git a/src/GSM610/short_term.c b/src/GSM610/short_term.c
new file mode 100644
index 0000000..e8cdac3
--- /dev/null
+++ b/src/GSM610/short_term.c
@@ -0,0 +1,412 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin.  See the accompanying file "COPYRIGHT" for
+ * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <assert.h>
+
+#include "gsm610_priv.h"
+
+/*
+ *  SHORT TERM ANALYSIS FILTERING SECTION
+ */
+
+/* 4.2.8 */
+
+static void Decoding_of_the_coded_Log_Area_Ratios (
+	int16_t 	* LARc,		/* coded log area ratio	[0..7] 	IN	*/
+	int16_t	* LARpp)	/* out: decoded ..			*/
+{
+	register int16_t	temp1 ;
+
+	/*  This procedure requires for efficient implementation
+	 *  two tables.
+ 	 *
+	 *  INVA[1..8] = integer((32768 * 8) / real_A[1..8])
+	 *  MIC[1..8]  = minimum value of the LARc[1..8]
+	 */
+
+	/*  Compute the LARpp[1..8]
+	 */
+
+	/* 	for (i = 1; i <= 8; i++, B++, MIC++, INVA++, LARc++, LARpp++) {
+	 *
+	 *		temp1  = GSM_ADD (*LARc, *MIC) << 10;
+	 *		temp2  = *B << 1;
+	 *		temp1  = GSM_SUB(temp1, temp2) ;
+	 *
+	 *		assert(*INVA != MIN_WORD) ;
+	 *
+	 *		temp1  = GSM_MULT_R (*INVA, temp1) ;
+	 *		*LARpp = GSM_ADD (temp1, temp1) ;
+	 *	}
+	 */
+
+#undef	STEP
+#define	STEP(B, MIC, INVA)	\
+		temp1	= arith_shift_left (GSM_ADD (*LARc++, MIC), 10) ;	\
+		temp1	= GSM_SUB (temp1, B * 2) ;			\
+		temp1	= GSM_MULT_R (INVA, temp1) ;		\
+		*LARpp++ = GSM_ADD (temp1, temp1) ;
+
+	STEP (0, -32, 13107) ;
+	STEP (0, -32, 13107) ;
+	STEP (2048, -16, 13107) ;
+	STEP (-2560, -16, 13107) ;
+
+	STEP (94, -8, 19223) ;
+	STEP (-1792, -8, 17476) ;
+	STEP (-341, -4, 31454) ;
+	STEP (-1144, -4, 29708) ;
+
+	/* NOTE: the addition of *MIC is used to restore
+	 * 	 the sign of *LARc.
+	 */
+}
+
+/* 4.2.9 */
+/* Computation of the quantized reflection coefficients
+ */
+
+/* 4.2.9.1  Interpolation of the LARpp[1..8] to get the LARp[1..8]
+ */
+
+/*
+ *  Within each frame of 160 analyzed speech samples the short term
+ *  analysis and synthesis filters operate with four different sets of
+ *  coefficients, derived from the previous set of decoded LARs(LARpp(j-1))
+ *  and the actual set of decoded LARs (LARpp(j))
+ *
+ * (Initial value: LARpp(j-1)[1..8] = 0.)
+ */
+
+static void Coefficients_0_12 (
+	register int16_t * LARpp_j_1,
+	register int16_t * LARpp_j,
+	register int16_t * LARp)
+{
+	register int 	i ;
+
+	for (i = 1 ; i <= 8 ; i++, LARp++, LARpp_j_1++, LARpp_j++)
+	{	*LARp = GSM_ADD (SASR_W (*LARpp_j_1, 2), SASR_W (*LARpp_j, 2)) ;
+		*LARp = GSM_ADD (*LARp, SASR_W (*LARpp_j_1, 1)) ;
+		}
+}
+
+static void Coefficients_13_26 (
+	register int16_t * LARpp_j_1,
+	register int16_t * LARpp_j,
+	register int16_t * LARp)
+{
+	register int i ;
+	for (i = 1 ; i <= 8 ; i++, LARpp_j_1++, LARpp_j++, LARp++)
+		*LARp = GSM_ADD (SASR_W (*LARpp_j_1, 1), SASR_W (*LARpp_j, 1)) ;
+}
+
+static void Coefficients_27_39 (
+	register int16_t * LARpp_j_1,
+	register int16_t * LARpp_j,
+	register int16_t * LARp)
+{
+	register int i ;
+
+	for (i = 1 ; i <= 8 ; i++, LARpp_j_1++, LARpp_j++, LARp++)
+	{	*LARp = GSM_ADD (SASR_W (*LARpp_j_1, 2), SASR_W (*LARpp_j, 2)) ;
+		*LARp = GSM_ADD (*LARp, SASR_W (*LARpp_j, 1)) ;
+		}
+}
+
+
+static void Coefficients_40_159 (
+	register int16_t * LARpp_j,
+	register int16_t * LARp)
+{
+	register int i ;
+
+	for (i = 1 ; i <= 8 ; i++, LARp++, LARpp_j++)
+		*LARp = *LARpp_j ;
+}
+
+/* 4.2.9.2 */
+
+static void LARp_to_rp (
+	register int16_t * LARp)	/* [0..7] IN/OUT  */
+/*
+ *  The input of this procedure is the interpolated LARp[0..7] array.
+ *  The reflection coefficients, rp[i], are used in the analysis
+ *  filter and in the synthesis filter.
+ */
+{
+	register int 		i ;
+	register int16_t		temp ;
+
+	for (i = 1 ; i <= 8 ; i++, LARp++)
+	{	/* temp = GSM_ABS(*LARp) ;
+	         *
+		 * if (temp < 11059) temp <<= 1;
+		 * else if (temp < 20070) temp += 11059;
+		 * else temp = GSM_ADD (temp >> 2, 26112) ;
+		 *
+		 * *LARp = *LARp < 0 ? -temp : temp;
+		 */
+
+		if (*LARp < 0)
+		{	temp = *LARp == MIN_WORD ? MAX_WORD : - (*LARp) ;
+			*LARp = - ((temp < 11059) ? temp << 1
+				: ((temp < 20070) ? temp + 11059
+				: GSM_ADD ((int16_t) (temp >> 2), (int16_t) 26112))) ;
+			}
+		else
+		{	temp = *LARp ;
+			*LARp = (temp < 11059) ? temp << 1
+				: ((temp < 20070) ? temp + 11059
+				: GSM_ADD ((int16_t) (temp >> 2), (int16_t) 26112)) ;
+			}
+	}
+}
+
+
+/* 4.2.10 */
+static void Short_term_analysis_filtering (
+	struct gsm_state * S,
+	register int16_t	* rp,	/* [0..7]	IN	*/
+	register int 	k_n, 	/*   k_end - k_start	*/
+	register int16_t	* s	/* [0..n-1]	IN/OUT	*/
+)
+/*
+ *  This procedure computes the short term residual signal d[..] to be fed
+ *  to the RPE-LTP loop from the s[..] signal and from the local rp[..]
+ *  array (quantized reflection coefficients).  As the call of this
+ *  procedure can be done in many ways (see the interpolation of the LAR
+ *  coefficient), it is assumed that the computation begins with index
+ *  k_start (for arrays d[..] and s[..]) and stops with index k_end
+ *  (k_start and k_end are defined in 4.2.9.1).  This procedure also
+ *  needs to keep the array u [0..7] in memory for each call.
+ */
+{
+	register int16_t		* u = S->u ;
+	register int		i ;
+	register int16_t		di, zzz, ui, sav, rpi ;
+
+	for ( ; k_n-- ; s++)
+	{	di = sav = *s ;
+
+		for (i = 0 ; i < 8 ; i++)
+		{	/* YYY */
+			ui	= u [i] ;
+			rpi	= rp [i] ;
+			u [i] = sav ;
+
+			zzz	= GSM_MULT_R (rpi, di) ;
+			sav	= GSM_ADD (ui, zzz) ;
+
+			zzz	= GSM_MULT_R (rpi, ui) ;
+			di	= GSM_ADD (di, zzz) ;
+		}
+
+		*s = di ;
+	}
+}
+
+#if defined (USE_FLOAT_MUL) && defined (FAST)
+
+static void Fast_Short_term_analysis_filtering (
+	struct gsm_state * S,
+	register int16_t	* rp,	/* [0..7]	IN	*/
+	register int 	k_n, 	/*   k_end - k_start	*/
+	register int16_t	* s	/* [0..n-1]	IN/OUT	*/
+)
+{
+	register int16_t		* u = S->u ;
+	register int		i ;
+
+	float uf [8], rpf [8] ;
+
+	register float scalef = 3.0517578125e-5 ;
+	register float sav, di, temp ;
+
+	for (i = 0 ; i < 8 ; ++i)
+	{	uf [i]	= u [i] ;
+		rpf [i] = rp [i] * scalef ;
+		}
+	for ( ; k_n-- ; s++)
+	{	sav = di = *s ;
+		for (i = 0 ; i < 8 ; i++)
+		{	register float rpfi = rpf [i] ;
+			register float ufi	= uf [i] ;
+
+			uf [i]	= sav ;
+			temp	= rpfi * di + ufi ;
+			di		+= rpfi * ufi ;
+			sav		= temp ;
+		}
+		*s = di ;
+	}
+	for (i = 0 ; i < 8 ; i++) u [i] = uf [i] ;
+}
+#endif /* ! (defined (USE_FLOAT_MUL) && defined (FAST)) */
+
+static void Short_term_synthesis_filtering (
+	struct gsm_state * S,
+	register int16_t	* rrp,	/* [0..7]	IN	*/
+	register int	k,	/* k_end - k_start	*/
+	register int16_t	* wt,	/* [0..k-1]	IN	*/
+	register int16_t	* sr	/* [0..k-1]	OUT	*/
+)
+{
+	register int16_t		* v = S->v ;
+	register int		i ;
+	register int16_t		sri, tmp1, tmp2 ;
+
+	while (k--)
+	{	sri = *wt++ ;
+		for (i = 8 ; i-- ; )
+		{	/* sri = GSM_SUB(sri, gsm_mult_r(rrp[i], v [i])) ;
+			 */
+			tmp1 = rrp [i] ;
+			tmp2 = v [i] ;
+			tmp2 = (tmp1 == MIN_WORD && tmp2 == MIN_WORD
+				? MAX_WORD
+				: 0x0FFFF & (((int32_t) tmp1 * (int32_t) tmp2
+							+ 16384) >> 15)) ;
+
+			sri = GSM_SUB (sri, tmp2) ;
+
+			/* v [i+1] = GSM_ADD (v [i], gsm_mult_r(rrp[i], sri)) ;
+			 */
+			tmp1 = (tmp1 == MIN_WORD && sri == MIN_WORD
+				? MAX_WORD
+				: 0x0FFFF & (((int32_t) tmp1 * (int32_t) sri
+							+ 16384) >> 15)) ;
+
+			v [i + 1] = GSM_ADD (v [i], tmp1) ;
+		}
+		*sr++ = v [0] = sri ;
+	}
+}
+
+
+#if defined (FAST) && defined (USE_FLOAT_MUL)
+
+static void Fast_Short_term_synthesis_filtering (
+	struct gsm_state * S,
+	register int16_t	* rrp,	/* [0..7]	IN	*/
+	register int	k,	/* k_end - k_start	*/
+	register int16_t	* wt,	/* [0..k-1]	IN	*/
+	register int16_t	* sr	/* [0..k-1]	OUT	*/
+)
+{
+	register int16_t		* v = S->v ;
+	register int		i ;
+
+	float va [9], rrpa [8] ;
+	register float scalef = 3.0517578125e-5, temp ;
+
+	for (i = 0 ; i < 8 ; ++i)
+	{	va [i]	= v [i] ;
+		rrpa [i] = (float) rrp [i] * scalef ;
+		}
+	while (k--) {
+		register float sri = *wt++ ;
+		for (i = 8 ; i-- ; )
+		{	sri -= rrpa [i] * va [i] ;
+			if		(sri < -32768.0) sri = -32768.0 ;
+			else if (sri > 32767.0) sri = 32767.0 ;
+
+			temp = va [i] + rrpa [i] * sri ;
+			if		(temp < -32768.0) temp = -32768.0 ;
+			else if (temp > 32767.0) temp = 32767.0 ;
+			va [i+1] = temp ;
+		}
+		*sr++ = va [0] = sri ;
+	}
+	for (i = 0 ; i < 9 ; ++i) v [i] = va [i] ;
+}
+
+#endif /* defined(FAST) && defined(USE_FLOAT_MUL) */
+
+void Gsm_Short_Term_Analysis_Filter (
+	struct gsm_state * S,
+
+	int16_t	* LARc,		/* coded log area ratio [0..7]  IN	*/
+	int16_t	* s			/* signal [0..159]		IN/OUT	*/
+)
+{
+	int16_t		* LARpp_j	= S->LARpp [S->j] ;
+	int16_t		* LARpp_j_1	= S->LARpp [S->j ^= 1] ;
+
+	int16_t		LARp [8] ;
+
+#undef	FILTER
+#if	defined (FAST) && defined (USE_FLOAT_MUL)
+# 	define	FILTER 	(* (S->fast								\
+					? Fast_Short_term_analysis_filtering	\
+					: Short_term_analysis_filtering))
+
+#else
+# 	define	FILTER	Short_term_analysis_filtering
+#endif
+
+	Decoding_of_the_coded_Log_Area_Ratios (LARc, LARpp_j) ;
+
+	Coefficients_0_12 (LARpp_j_1, LARpp_j, LARp) ;
+	LARp_to_rp (LARp) ;
+	FILTER (S, LARp, 13, s) ;
+
+	Coefficients_13_26 (LARpp_j_1, LARpp_j, LARp) ;
+	LARp_to_rp (LARp) ;
+	FILTER (S, LARp, 14, s + 13) ;
+
+	Coefficients_27_39 (LARpp_j_1, LARpp_j, LARp) ;
+	LARp_to_rp (LARp) ;
+	FILTER (S, LARp, 13, s + 27) ;
+
+	Coefficients_40_159 (LARpp_j, LARp) ;
+	LARp_to_rp (LARp) ;
+	FILTER (S, LARp, 120, s + 40) ;
+}
+
+void Gsm_Short_Term_Synthesis_Filter (
+	struct gsm_state * S,
+
+	int16_t	* LARcr,	/* received log area ratios [0..7] IN  */
+	int16_t	* wt,		/* received d [0..159]		   IN  */
+
+	int16_t	* s		/* signal   s [0..159]		  OUT  */
+)
+{
+	int16_t		* LARpp_j	= S->LARpp [S->j] ;
+	int16_t		* LARpp_j_1	= S->LARpp [S->j ^= 1] ;
+
+	int16_t		LARp [8] ;
+
+#undef	FILTER
+#if defined (FAST) && defined (USE_FLOAT_MUL)
+
+# 	define	FILTER 	(* (S->fast							\
+				? Fast_Short_term_synthesis_filtering	\
+				: Short_term_synthesis_filtering))
+#else
+#	define	FILTER	Short_term_synthesis_filtering
+#endif
+
+	Decoding_of_the_coded_Log_Area_Ratios (LARcr, LARpp_j) ;
+
+	Coefficients_0_12 (LARpp_j_1, LARpp_j, LARp) ;
+	LARp_to_rp (LARp) ;
+	FILTER (S, LARp, 13, wt, s) ;
+
+	Coefficients_13_26 (LARpp_j_1, LARpp_j, LARp) ;
+	LARp_to_rp (LARp) ;
+	FILTER (S, LARp, 14, wt + 13, s + 13) ;
+
+	Coefficients_27_39 (LARpp_j_1, LARpp_j, LARp) ;
+	LARp_to_rp (LARp) ;
+	FILTER (S, LARp, 13, wt + 27, s + 27) ;
+
+	Coefficients_40_159 (LARpp_j, LARp) ;
+	LARp_to_rp (LARp) ;
+	FILTER (S, LARp, 120, wt + 40, s + 40) ;
+}
diff --git a/src/GSM610/table.c b/src/GSM610/table.c
new file mode 100644
index 0000000..30590af
--- /dev/null
+++ b/src/GSM610/table.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin.  See the accompanying file "COPYRIGHT" for
+ * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/*  Most of these tables are inlined at their point of use.
+ */
+
+/*  4.4 TABLES USED IN THE FIXED POINT IMPLEMENTATION OF THE RPE-LTP
+ *      CODER AND DECODER
+ *
+ *	(Most of them inlined, so watch out.)
+ */
+
+#define	GSM_TABLE_C
+#include "gsm610_priv.h"
+
+/*  Table 4.1  Quantization of the Log.-Area Ratios
+ */
+/* i 		     1      2      3        4      5      6        7       8 */
+int16_t gsm_A [8]	= { 20480,	20480,	20480,	20480,	13964,	15360,	8534,	9036 } ;
+int16_t gsm_B [8]	= {		0,		0,	2048,	-2560,	94,		-1792,	-341,	-1144 } ;
+int16_t gsm_MIC [8] = { -32,	-32,	-16,	-16,	-8,		-8,		-4,		-4 } ;
+int16_t gsm_MAC [8] = { 31,	31,		15,		15,		7,		7,		3,		3 } ;
+
+
+/*  Table 4.2  Tabulation  of 1/A[1..8]
+ */
+int16_t gsm_INVA [8] = { 13107, 13107, 13107, 13107, 19223, 17476, 31454, 29708 } ;
+
+
+/*   Table 4.3a  Decision level of the LTP gain quantizer
+ */
+/*  bc		      0	        1	  2	     3			*/
+int16_t gsm_DLB [4] = { 6554, 16384,	26214,	32767 } ;
+
+
+/*   Table 4.3b   Quantization levels of the LTP gain quantizer
+ */
+/* bc		      0          1        2          3			*/
+int16_t gsm_QLB [4] = { 3277,	11469,	21299,		32767	} ;
+
+
+/*   Table 4.4	 Coefficients of the weighting filter
+ */
+/* i		    0      1   2    3   4      5      6     7   8   9    10  */
+int16_t gsm_H [11] = { -134, -374, 0, 2054, 5741, 8192, 5741, 2054, 0, -374, -134 } ;
+
+
+/*   Table 4.5 	 Normalized inverse mantissa used to compute xM/xmax
+ */
+/* i		 	0        1    2      3      4      5     6      7   */
+int16_t gsm_NRFAC [8] = { 29128, 26215, 23832, 21846, 20165, 18725, 17476, 16384 } ;
+
+
+/*   Table 4.6	 Normalized direct mantissa used to compute xM/xmax
+ */
+/* i                  0      1       2      3      4      5      6      7   */
+int16_t gsm_FAC [8]	= { 18431, 20479, 22527, 24575, 26623, 28671, 30719, 32767 } ;
diff --git a/src/aiff.c b/src/aiff.c
new file mode 100644
index 0000000..a2bda8f
--- /dev/null
+++ b/src/aiff.c
@@ -0,0 +1,1851 @@
+/*
+** Copyright (C) 1999-2018 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2005 David Viens <davidv@plogue.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <ctype.h>
+#include <inttypes.h>
+
+#include "sndfile.h"
+#include "sfendian.h"
+#include "common.h"
+#include "chanmap.h"
+
+/*------------------------------------------------------------------------------
+ * Macros to handle big/little endian issues.
+ */
+
+#define FORM_MARKER		(MAKE_MARKER ('F', 'O', 'R', 'M'))
+#define AIFF_MARKER		(MAKE_MARKER ('A', 'I', 'F', 'F'))
+#define AIFC_MARKER		(MAKE_MARKER ('A', 'I', 'F', 'C'))
+#define COMM_MARKER		(MAKE_MARKER ('C', 'O', 'M', 'M'))
+#define SSND_MARKER		(MAKE_MARKER ('S', 'S', 'N', 'D'))
+#define MARK_MARKER		(MAKE_MARKER ('M', 'A', 'R', 'K'))
+#define INST_MARKER		(MAKE_MARKER ('I', 'N', 'S', 'T'))
+#define APPL_MARKER		(MAKE_MARKER ('A', 'P', 'P', 'L'))
+#define CHAN_MARKER		(MAKE_MARKER ('C', 'H', 'A', 'N'))
+
+#define c_MARKER		(MAKE_MARKER ('(', 'c', ')', ' '))
+#define NAME_MARKER		(MAKE_MARKER ('N', 'A', 'M', 'E'))
+#define AUTH_MARKER		(MAKE_MARKER ('A', 'U', 'T', 'H'))
+#define ANNO_MARKER		(MAKE_MARKER ('A', 'N', 'N', 'O'))
+#define COMT_MARKER		(MAKE_MARKER ('C', 'O', 'M', 'T'))
+#define FVER_MARKER		(MAKE_MARKER ('F', 'V', 'E', 'R'))
+#define SFX_MARKER		(MAKE_MARKER ('S', 'F', 'X', '!'))
+
+#define PEAK_MARKER		(MAKE_MARKER ('P', 'E', 'A', 'K'))
+#define basc_MARKER		(MAKE_MARKER ('b', 'a', 's', 'c'))
+
+/* Supported AIFC encodings.*/
+#define NONE_MARKER		(MAKE_MARKER ('N', 'O', 'N', 'E'))
+#define sowt_MARKER		(MAKE_MARKER ('s', 'o', 'w', 't'))
+#define twos_MARKER		(MAKE_MARKER ('t', 'w', 'o', 's'))
+#define raw_MARKER		(MAKE_MARKER ('r', 'a', 'w', ' '))
+#define in24_MARKER		(MAKE_MARKER ('i', 'n', '2', '4'))
+#define ni24_MARKER		(MAKE_MARKER ('4', '2', 'n', '1'))
+#define in32_MARKER		(MAKE_MARKER ('i', 'n', '3', '2'))
+#define ni32_MARKER		(MAKE_MARKER ('2', '3', 'n', 'i'))
+
+#define fl32_MARKER		(MAKE_MARKER ('f', 'l', '3', '2'))
+#define FL32_MARKER		(MAKE_MARKER ('F', 'L', '3', '2'))
+#define fl64_MARKER		(MAKE_MARKER ('f', 'l', '6', '4'))
+#define FL64_MARKER		(MAKE_MARKER ('F', 'L', '6', '4'))
+
+#define ulaw_MARKER		(MAKE_MARKER ('u', 'l', 'a', 'w'))
+#define ULAW_MARKER		(MAKE_MARKER ('U', 'L', 'A', 'W'))
+#define alaw_MARKER		(MAKE_MARKER ('a', 'l', 'a', 'w'))
+#define ALAW_MARKER		(MAKE_MARKER ('A', 'L', 'A', 'W'))
+
+#define DWVW_MARKER		(MAKE_MARKER ('D', 'W', 'V', 'W'))
+#define GSM_MARKER		(MAKE_MARKER ('G', 'S', 'M', ' '))
+#define ima4_MARKER		(MAKE_MARKER ('i', 'm', 'a', '4'))
+
+/*
+**	This value is officially assigned to Mega Nerd Pty Ltd by Apple
+**	Corportation as the Application marker for libsndfile.
+**
+**	See : http://developer.apple.com/faq/datatype.html
+*/
+#define m3ga_MARKER		(MAKE_MARKER ('m', '3', 'g', 'a'))
+
+/* Unsupported AIFC encodings.*/
+
+#define MAC3_MARKER		(MAKE_MARKER ('M', 'A', 'C', '3'))
+#define MAC6_MARKER		(MAKE_MARKER ('M', 'A', 'C', '6'))
+#define ADP4_MARKER		(MAKE_MARKER ('A', 'D', 'P', '4'))
+
+/* Predfined chunk sizes. */
+#define SIZEOF_AIFF_COMM		18
+#define SIZEOF_AIFC_COMM_MIN	22
+#define SIZEOF_AIFC_COMM		24
+#define SIZEOF_SSND_CHUNK		8
+#define SIZEOF_INST_CHUNK		20
+
+/* Is it constant? */
+
+/* AIFC/IMA4 defines. */
+#define AIFC_IMA4_BLOCK_LEN				34
+#define AIFC_IMA4_SAMPLES_PER_BLOCK		64
+
+#define AIFF_PEAK_CHUNK_SIZE(ch)	(2 * sizeof (int) + ch * (sizeof (float) + sizeof (int)))
+
+/*------------------------------------------------------------------------------
+ * Typedefs for file chunks.
+ */
+
+enum
+{	HAVE_FORM		= 0x01,
+	HAVE_AIFF		= 0x02,
+	HAVE_AIFC		= 0x04,
+	HAVE_FVER		= 0x08,
+	HAVE_COMM		= 0x10,
+	HAVE_SSND		= 0x20
+} ;
+
+typedef struct
+{	uint32_t	size ;
+	int16_t		numChannels ;
+	uint32_t	numSampleFrames ;
+	int16_t		sampleSize ;
+	uint8_t		sampleRate [10] ;
+	uint32_t	encoding ;
+	char			zero_bytes [2] ;
+} COMM_CHUNK ;
+
+typedef struct
+{	uint32_t	offset ;
+	uint32_t	blocksize ;
+} SSND_CHUNK ;
+
+typedef struct
+{	int16_t		playMode ;
+	uint16_t	beginLoop ;
+	uint16_t	endLoop ;
+} INST_LOOP ;
+
+typedef struct
+{	int8_t		baseNote ;		/* all notes are MIDI note numbers */
+	int8_t		detune ;		/* cents off, only -50 to +50 are significant */
+	int8_t		lowNote ;
+	int8_t		highNote ;
+	int8_t		lowVelocity ;	/* 1 to 127 */
+	int8_t		highVelocity ;	/* 1 to 127 */
+	int16_t		gain ;			/* in dB, 0 is normal */
+	INST_LOOP	sustain_loop ;
+	INST_LOOP	release_loop ;
+} INST_CHUNK ;
+
+
+enum
+{	basc_SCALE_MINOR = 1,
+	basc_SCALE_MAJOR,
+	basc_SCALE_NEITHER,
+	basc_SCALE_BOTH
+} ;
+
+enum
+{	basc_TYPE_LOOP = 0,
+	basc_TYPE_ONE_SHOT
+} ;
+
+
+typedef struct
+{	uint32_t	version ;
+	uint32_t	numBeats ;
+	uint16_t	rootNote ;
+	uint16_t	scaleType ;
+	uint16_t	sigNumerator ;
+	uint16_t	sigDenominator ;
+	uint16_t	loopType ;
+} basc_CHUNK ;
+
+typedef struct
+{	uint16_t	markerID ;
+	uint32_t	position ;
+} MARK_ID_POS ;
+
+typedef struct
+{	sf_count_t	comm_offset ;
+	sf_count_t	ssnd_offset ;
+
+	int32_t		chanmap_tag ;
+
+	MARK_ID_POS *markstr ;
+} AIFF_PRIVATE ;
+
+/*------------------------------------------------------------------------------
+ * Private static functions.
+ */
+
+static int	aiff_close (SF_PRIVATE *psf) ;
+
+static int	tenbytefloat2int (uint8_t *bytes) ;
+static void uint2tenbytefloat (uint32_t num, uint8_t *bytes) ;
+
+static int	aiff_read_comm_chunk (SF_PRIVATE *psf, COMM_CHUNK *comm_fmt) ;
+
+static int	aiff_read_header (SF_PRIVATE *psf, COMM_CHUNK *comm_fmt) ;
+
+static int	aiff_write_header (SF_PRIVATE *psf, int calc_length) ;
+static int	aiff_write_tailer (SF_PRIVATE *psf) ;
+static void	aiff_write_strings (SF_PRIVATE *psf, int location) ;
+
+static int	aiff_command (SF_PRIVATE *psf, int command, void *data, int datasize) ;
+
+static const char *get_loop_mode_str (int16_t mode) ;
+
+static int16_t get_loop_mode (int16_t mode) ;
+
+static int aiff_read_basc_chunk (SF_PRIVATE * psf, int) ;
+
+static int aiff_read_chanmap (SF_PRIVATE * psf, unsigned dword) ;
+
+static uint32_t marker_to_position (const MARK_ID_POS *m, uint16_t n, int marksize) ;
+
+static int aiff_set_chunk (SF_PRIVATE *psf, const SF_CHUNK_INFO * chunk_info) ;
+static SF_CHUNK_ITERATOR * aiff_next_chunk_iterator (SF_PRIVATE *psf, SF_CHUNK_ITERATOR * iterator) ;
+static int aiff_get_chunk_size (SF_PRIVATE *psf, const SF_CHUNK_ITERATOR * iterator, SF_CHUNK_INFO * chunk_info) ;
+static int aiff_get_chunk_data (SF_PRIVATE *psf, const SF_CHUNK_ITERATOR * iterator, SF_CHUNK_INFO * chunk_info) ;
+
+/*------------------------------------------------------------------------------
+** Public function.
+*/
+
+int
+aiff_open (SF_PRIVATE *psf)
+{	COMM_CHUNK comm_fmt ;
+	int error = 0, subformat ;
+
+	memset (&comm_fmt, 0, sizeof (comm_fmt)) ;
+
+	subformat = SF_CODEC (psf->sf.format) ;
+
+	if ((psf->container_data = calloc (1, sizeof (AIFF_PRIVATE))) == NULL)
+		return SFE_MALLOC_FAILED ;
+
+	psf->container_close = aiff_close ;
+
+	if (psf->file.mode == SFM_READ || (psf->file.mode == SFM_RDWR && psf->filelength > 0))
+	{	if ((error = aiff_read_header (psf, &comm_fmt)))
+			return error ;
+
+		psf->next_chunk_iterator = aiff_next_chunk_iterator ;
+		psf->get_chunk_size = aiff_get_chunk_size ;
+		psf->get_chunk_data = aiff_get_chunk_data ;
+
+		psf_fseek (psf, psf->dataoffset, SEEK_SET) ;
+		} ;
+
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	if (psf->is_pipe)
+			return SFE_NO_PIPE_WRITE ;
+
+		if ((SF_CONTAINER (psf->sf.format)) != SF_FORMAT_AIFF)
+			return SFE_BAD_OPEN_FORMAT ;
+
+		if (psf->file.mode == SFM_WRITE && (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE))
+		{	if ((psf->peak_info = peak_info_calloc (psf->sf.channels)) == NULL)
+				return SFE_MALLOC_FAILED ;
+			psf->peak_info->peak_loc = SF_PEAK_START ;
+			} ;
+
+		if (psf->file.mode != SFM_RDWR || psf->filelength < 40)
+		{	psf->filelength = 0 ;
+			psf->datalength = 0 ;
+			psf->dataoffset = 0 ;
+			psf->sf.frames = 0 ;
+			} ;
+
+		psf->strings.flags = SF_STR_ALLOW_START | SF_STR_ALLOW_END ;
+
+		if ((error = aiff_write_header (psf, SF_FALSE)))
+			return error ;
+
+		psf->write_header	= aiff_write_header ;
+		psf->set_chunk		= aiff_set_chunk ;
+		} ;
+
+	psf->command = aiff_command ;
+
+	switch (SF_CODEC (psf->sf.format))
+	{	case SF_FORMAT_PCM_U8 :
+				error = pcm_init (psf) ;
+				break ;
+
+		case SF_FORMAT_PCM_S8 :
+				error = pcm_init (psf) ;
+				break ;
+
+		case SF_FORMAT_PCM_16 :
+		case SF_FORMAT_PCM_24 :
+		case SF_FORMAT_PCM_32 :
+				error = pcm_init (psf) ;
+				break ;
+
+		case SF_FORMAT_ULAW :
+				error = ulaw_init (psf) ;
+				break ;
+
+		case SF_FORMAT_ALAW :
+				error = alaw_init (psf) ;
+				break ;
+
+		/* Lite remove start */
+		case SF_FORMAT_FLOAT :
+				error = float32_init (psf) ;
+				break ;
+
+		case SF_FORMAT_DOUBLE :
+				error = double64_init (psf) ;
+				break ;
+
+		case SF_FORMAT_DWVW_12 :
+				if (psf->sf.frames > comm_fmt.numSampleFrames)
+					psf->sf.frames = comm_fmt.numSampleFrames ;
+				break ;
+
+		case SF_FORMAT_DWVW_16 :
+				error = dwvw_init (psf, 16) ;
+				if (psf->sf.frames > comm_fmt.numSampleFrames)
+					psf->sf.frames = comm_fmt.numSampleFrames ;
+				break ;
+
+		case SF_FORMAT_DWVW_24 :
+				error = dwvw_init (psf, 24) ;
+				if (psf->sf.frames > comm_fmt.numSampleFrames)
+					psf->sf.frames = comm_fmt.numSampleFrames ;
+				break ;
+
+		case SF_FORMAT_DWVW_N :
+				if (psf->file.mode != SFM_READ)
+				{	error = SFE_DWVW_BAD_BITWIDTH ;
+					break ;
+					} ;
+				if (comm_fmt.sampleSize >= 8 && comm_fmt.sampleSize < 24)
+				{	error = dwvw_init (psf, comm_fmt.sampleSize) ;
+					if (psf->sf.frames > comm_fmt.numSampleFrames)
+						psf->sf.frames = comm_fmt.numSampleFrames ;
+					break ;
+					} ;
+				psf_log_printf (psf, "AIFC/DWVW : Bad bitwidth %d\n", comm_fmt.sampleSize) ;
+				error = SFE_DWVW_BAD_BITWIDTH ;
+				break ;
+
+		case SF_FORMAT_IMA_ADPCM :
+				/*
+				**	IMA ADPCM encoded AIFF files always have a block length
+				**	of 34 which decodes to 64 samples.
+				*/
+				error = aiff_ima_init (psf, AIFC_IMA4_BLOCK_LEN, AIFC_IMA4_SAMPLES_PER_BLOCK) ;
+				break ;
+		/* Lite remove end */
+
+		case SF_FORMAT_GSM610 :
+				error = gsm610_init (psf) ;
+				if (psf->sf.frames > comm_fmt.numSampleFrames)
+					psf->sf.frames = comm_fmt.numSampleFrames ;
+				break ;
+
+		default : return SFE_UNIMPLEMENTED ;
+		} ;
+
+	if (psf->file.mode != SFM_WRITE && psf->sf.frames - comm_fmt.numSampleFrames != 0)
+	{	psf_log_printf (psf,
+			"*** Frame count read from 'COMM' chunk (%u) not equal to frame count\n"
+			"*** calculated from length of 'SSND' chunk (%u).\n",
+			comm_fmt.numSampleFrames, (uint32_t) psf->sf.frames) ;
+		} ;
+
+	return error ;
+} /* aiff_open */
+
+/*==========================================================================================
+** Private functions.
+*/
+
+/* This function ought to check size */
+static uint32_t
+marker_to_position (const MARK_ID_POS *m, uint16_t n, int marksize)
+{	int i ;
+
+	for (i = 0 ; i < marksize ; i++)
+		if (m [i].markerID == n)
+			return m [i].position ;
+	return 0 ;
+} /* marker_to_position */
+
+static int
+aiff_read_header (SF_PRIVATE *psf, COMM_CHUNK *comm_fmt)
+{	SSND_CHUNK	ssnd_fmt ;
+	AIFF_PRIVATE *paiff ;
+	BUF_UNION	ubuf ;
+	uint32_t	chunk_size = 0, FORMsize, SSNDsize, bytesread, mark_count = 0 ;
+	int			k, found_chunk = 0, done = 0, error = 0 ;
+	char		*cptr ;
+	int			instr_found = 0, mark_found = 0 ;
+
+	if (psf->filelength > 0xFFFFFFFFLL)
+		psf_log_printf (psf, "Warning : filelength > 0xffffffff. This is bad!!!!\n") ;
+
+	if ((paiff = psf->container_data) == NULL)
+		return SFE_INTERNAL ;
+
+	paiff->comm_offset = 0 ;
+	paiff->ssnd_offset = 0 ;
+
+	/* Set position to start of file to begin reading header. */
+	psf_binheader_readf (psf, "p", 0) ;
+
+	memset (comm_fmt, 0, sizeof (COMM_CHUNK)) ;
+
+	/* Until recently AIF* file were all BIG endian. */
+	psf->endian = SF_ENDIAN_BIG ;
+
+	/*	AIFF files can apparently have their chunks in any order. However, they
+	**	must have a FORM chunk. Approach here is to read all the chunks one by
+	**	one and then check for the mandatory chunks at the end.
+	*/
+	while (! done)
+	{	unsigned	marker ;
+		size_t jump = chunk_size & 1 ;
+
+		marker = chunk_size = 0 ;
+		psf_binheader_readf (psf, "Ejm4", jump, &marker, &chunk_size) ;
+		if (marker == 0)
+		{	sf_count_t pos = psf_ftell (psf) ;
+			psf_log_printf (psf, "Have 0 marker at position %D (0x%x).\n", pos, pos) ;
+			break ;
+			} ;
+
+		if (psf->file.mode == SFM_RDWR && (found_chunk & HAVE_SSND))
+			return SFE_AIFF_RW_SSND_NOT_LAST ;
+
+		psf_store_read_chunk_u32 (&psf->rchunks, marker, psf_ftell (psf), chunk_size) ;
+
+		switch (marker)
+		{	case FORM_MARKER :
+					if (found_chunk)
+						return SFE_AIFF_NO_FORM ;
+
+					FORMsize = chunk_size ;
+
+					found_chunk |= HAVE_FORM ;
+					psf_binheader_readf (psf, "m", &marker) ;
+					switch (marker)
+					{	case AIFC_MARKER :
+						case AIFF_MARKER :
+							found_chunk |= (marker == AIFC_MARKER) ? (HAVE_AIFC | HAVE_AIFF) : HAVE_AIFF ;
+							break ;
+						default :
+							break ;
+						} ;
+
+					if (psf->fileoffset > 0 && psf->filelength > FORMsize + 8)
+					{	/* Set file length. */
+						psf->filelength = FORMsize + 8 ;
+						psf_log_printf (psf, "FORM : %u\n %M\n", FORMsize, marker) ;
+						}
+					else if (FORMsize != psf->filelength - 2 * SIGNED_SIZEOF (chunk_size))
+					{	chunk_size = psf->filelength - 2 * sizeof (chunk_size) ;
+						psf_log_printf (psf, "FORM : %u (should be %u)\n %M\n", FORMsize, chunk_size, marker) ;
+						FORMsize = chunk_size ;
+						}
+					else
+						psf_log_printf (psf, "FORM : %u\n %M\n", FORMsize, marker) ;
+					/* Set this to 0, so we don't jump a byte when parsing the next marker. */
+					chunk_size = 0 ;
+					break ;
+
+
+			case COMM_MARKER :
+					paiff->comm_offset = psf_ftell (psf) - 8 ;
+					chunk_size += chunk_size & 1 ;
+					comm_fmt->size = chunk_size ;
+					if ((error = aiff_read_comm_chunk (psf, comm_fmt)) != 0)
+						return error ;
+
+					found_chunk |= HAVE_COMM ;
+					break ;
+
+			case PEAK_MARKER :
+					/* Must have COMM chunk before PEAK chunk. */
+					if ((found_chunk & (HAVE_FORM | HAVE_AIFF | HAVE_COMM)) != (HAVE_FORM | HAVE_AIFF | HAVE_COMM))
+						return SFE_AIFF_PEAK_B4_COMM ;
+
+					psf_log_printf (psf, "%M : %d\n", marker, chunk_size) ;
+					if (chunk_size != AIFF_PEAK_CHUNK_SIZE (psf->sf.channels))
+					{	psf_binheader_readf (psf, "j", chunk_size) ;
+						psf_log_printf (psf, "*** File PEAK chunk too big.\n") ;
+						return SFE_WAV_BAD_PEAK ;
+						} ;
+
+					if (psf->peak_info)
+					{	psf_log_printf (psf, "*** Found existing peak info, using last one.\n") ;
+						free (psf->peak_info) ;
+						psf->peak_info = NULL ;
+						} ;
+					if ((psf->peak_info = peak_info_calloc (psf->sf.channels)) == NULL)
+						return SFE_MALLOC_FAILED ;
+
+					/* Read in rest of PEAK chunk. */
+					psf_binheader_readf (psf, "E44", &(psf->peak_info->version), &(psf->peak_info->timestamp)) ;
+
+					if (psf->peak_info->version != 1)
+						psf_log_printf (psf, "  version    : %d *** (should be version 1)\n", psf->peak_info->version) ;
+					else
+						psf_log_printf (psf, "  version    : %d\n", psf->peak_info->version) ;
+
+					psf_log_printf (psf, "  time stamp : %d\n", psf->peak_info->timestamp) ;
+					psf_log_printf (psf, "    Ch   Position       Value\n") ;
+
+					cptr = ubuf.cbuf ;
+					for (k = 0 ; k < psf->sf.channels ; k++)
+					{	float value ;
+						uint32_t position ;
+
+						psf_binheader_readf (psf, "Ef4", &value, &position) ;
+						psf->peak_info->peaks [k].value = value ;
+						psf->peak_info->peaks [k].position = position ;
+
+						snprintf (cptr, sizeof (ubuf.scbuf), "    %2d   %-12" PRId64 "   %g\n",
+								k, psf->peak_info->peaks [k].position, psf->peak_info->peaks [k].value) ;
+						cptr [sizeof (ubuf.scbuf) - 1] = 0 ;
+						psf_log_printf (psf, "%s", cptr) ;
+						} ;
+
+					psf->peak_info->peak_loc = ((found_chunk & HAVE_SSND) == 0) ? SF_PEAK_START : SF_PEAK_END ;
+					break ;
+
+			case SSND_MARKER :
+					if ((found_chunk & HAVE_AIFC) && (found_chunk & HAVE_FVER) == 0)
+						psf_log_printf (psf, "*** Valid AIFC files should have an FVER chunk.\n") ;
+
+					paiff->ssnd_offset = psf_ftell (psf) - 8 ;
+					SSNDsize = chunk_size ;
+					psf_binheader_readf (psf, "E44", &(ssnd_fmt.offset), &(ssnd_fmt.blocksize)) ;
+
+					psf->datalength = SSNDsize - sizeof (ssnd_fmt) ;
+					psf->dataoffset = psf_ftell (psf) ;
+
+					if (psf->datalength > psf->filelength - psf->dataoffset || psf->datalength < 0)
+					{	psf_log_printf (psf, " SSND : %u (should be %D)\n", SSNDsize, psf->filelength - psf->dataoffset + sizeof (SSND_CHUNK)) ;
+						psf->datalength = psf->filelength - psf->dataoffset ;
+						}
+					else
+						psf_log_printf (psf, " SSND : %u\n", SSNDsize) ;
+
+					if (ssnd_fmt.offset == 0 || psf->dataoffset + ssnd_fmt.offset == ssnd_fmt.blocksize)
+					{	psf_log_printf (psf, "  Offset     : %u\n", ssnd_fmt.offset) ;
+						psf_log_printf (psf, "  Block Size : %u\n", ssnd_fmt.blocksize) ;
+
+						psf->dataoffset += ssnd_fmt.offset ;
+						psf->datalength -= ssnd_fmt.offset ;
+						}
+					else
+					{	psf_log_printf (psf, "  Offset     : %u\n", ssnd_fmt.offset) ;
+						psf_log_printf (psf, "  Block Size : %u ???\n", ssnd_fmt.blocksize) ;
+						psf->dataoffset += ssnd_fmt.offset ;
+						psf->datalength -= ssnd_fmt.offset ;
+						} ;
+
+					/* Only set dataend if there really is data at the end. */
+					if (psf->datalength + psf->dataoffset < psf->filelength)
+						psf->dataend = psf->datalength + psf->dataoffset ;
+
+					found_chunk |= HAVE_SSND ;
+
+					if (! psf->sf.seekable)
+						break ;
+
+					/* Seek to end of SSND chunk. */
+					psf_fseek (psf, psf->dataoffset + psf->datalength, SEEK_SET) ;
+					break ;
+
+			case c_MARKER :
+					if (chunk_size == 0)
+						break ;
+					if (chunk_size >= SIGNED_SIZEOF (ubuf.scbuf))
+					{	psf_log_printf (psf, " %M : %d (too big)\n", marker, chunk_size) ;
+						return SFE_INTERNAL ;
+						} ;
+
+					cptr = ubuf.cbuf ;
+					psf_binheader_readf (psf, "b", cptr, chunk_size + (chunk_size & 1)) ;
+					cptr [chunk_size] = 0 ;
+
+					psf_sanitize_string (cptr, chunk_size) ;
+
+					psf_log_printf (psf, " %M : %s\n", marker, cptr) ;
+					psf_store_string (psf, SF_STR_COPYRIGHT, cptr) ;
+					chunk_size += chunk_size & 1 ;
+					break ;
+
+			case AUTH_MARKER :
+					if (chunk_size == 0)
+						break ;
+					if (chunk_size >= SIGNED_SIZEOF (ubuf.scbuf) - 1)
+					{	psf_log_printf (psf, " %M : %d (too big)\n", marker, chunk_size) ;
+						return SFE_INTERNAL ;
+						} ;
+
+					cptr = ubuf.cbuf ;
+					psf_binheader_readf (psf, "b", cptr, chunk_size + (chunk_size & 1)) ;
+					cptr [chunk_size] = 0 ;
+					psf_log_printf (psf, " %M : %s\n", marker, cptr) ;
+					psf_store_string (psf, SF_STR_ARTIST, cptr) ;
+					chunk_size += chunk_size & 1 ;
+					break ;
+
+			case COMT_MARKER :
+				{	uint16_t count, id, len ;
+					uint32_t timestamp, bytes ;
+
+					if (chunk_size == 0)
+						break ;
+					bytes = chunk_size ;
+					bytes -= psf_binheader_readf (psf, "E2", &count) ;
+					psf_log_printf (psf, " %M : %d\n  count  : %d\n", marker, chunk_size, count) ;
+
+					for (k = 0 ; k < count ; k++)
+					{	bytes -= psf_binheader_readf (psf, "E422", &timestamp, &id, &len) ;
+						psf_log_printf (psf, "   time   : 0x%x\n   marker : %x\n   length : %d\n", timestamp, id, len) ;
+
+						if (len + 1 > SIGNED_SIZEOF (ubuf.scbuf))
+						{	psf_log_printf (psf, "\nError : string length (%d) too big.\n", len) ;
+							return SFE_INTERNAL ;
+							} ;
+
+						cptr = ubuf.cbuf ;
+						bytes -= psf_binheader_readf (psf, "b", cptr, len) ;
+						cptr [len] = 0 ;
+						psf_log_printf (psf, "   string : %s\n", cptr) ;
+						} ;
+
+					if (bytes > 0)
+						psf_binheader_readf (psf, "j", bytes) ;
+					} ;
+					break ;
+
+			case APPL_MARKER :
+				{	unsigned appl_marker ;
+
+					if (chunk_size == 0)
+						break ;
+					if (chunk_size >= SIGNED_SIZEOF (ubuf.scbuf) - 1)
+					{	psf_log_printf (psf, " %M : %u (too big, skipping)\n", marker, chunk_size) ;
+						psf_binheader_readf (psf, "j", chunk_size + (chunk_size & 1)) ;
+						break ;
+						} ;
+
+					if (chunk_size < 4)
+					{	psf_log_printf (psf, " %M : %d (too small, skipping)\n", marker, chunk_size) ;
+						psf_binheader_readf (psf, "j", chunk_size + (chunk_size & 1)) ;
+						break ;
+						} ;
+
+					cptr = ubuf.cbuf ;
+					psf_binheader_readf (psf, "mb", &appl_marker, cptr, chunk_size + (chunk_size & 1) - 4) ;
+					cptr [chunk_size] = 0 ;
+
+					for (k = 0 ; k < (int) chunk_size ; k++)
+						if (! psf_isprint (cptr [k]))
+						{	cptr [k] = 0 ;
+							break ;
+							} ;
+
+					psf_log_printf (psf, " %M : %d\n  AppSig : %M\n  Name   : %s\n", marker, chunk_size, appl_marker, cptr) ;
+					psf_store_string (psf, SF_STR_SOFTWARE, cptr) ;
+					chunk_size += chunk_size & 1 ;
+					} ;
+					break ;
+
+			case NAME_MARKER :
+					if (chunk_size == 0)
+						break ;
+					if (chunk_size >= SIGNED_SIZEOF (ubuf.scbuf) - 2)
+					{	psf_log_printf (psf, " %M : %d (too big)\n", marker, chunk_size) ;
+						return SFE_INTERNAL ;
+						} ;
+
+					cptr = ubuf.cbuf ;
+					psf_binheader_readf (psf, "b", cptr, chunk_size + (chunk_size & 1)) ;
+					cptr [chunk_size] = 0 ;
+					psf_log_printf (psf, " %M : %s\n", marker, cptr) ;
+					psf_store_string (psf, SF_STR_TITLE, cptr) ;
+					chunk_size += chunk_size & 1 ;
+					break ;
+
+			case ANNO_MARKER :
+					if (chunk_size == 0)
+						break ;
+					if (chunk_size >= SIGNED_SIZEOF (ubuf.scbuf) - 2)
+					{	psf_log_printf (psf, " %M : %d (too big)\n", marker, chunk_size) ;
+						return SFE_INTERNAL ;
+						} ;
+
+					cptr = ubuf.cbuf ;
+					psf_binheader_readf (psf, "b", cptr, chunk_size + (chunk_size & 1)) ;
+					cptr [chunk_size] = 0 ;
+					psf_log_printf (psf, " %M : %s\n", marker, cptr) ;
+					psf_store_string (psf, SF_STR_COMMENT, cptr) ;
+					chunk_size += chunk_size & 1 ;
+					break ;
+
+			case INST_MARKER :
+					if (chunk_size != SIZEOF_INST_CHUNK)
+					{	psf_log_printf (psf, " %M : %d (should be %d)\n", marker, chunk_size, SIZEOF_INST_CHUNK) ;
+						psf_binheader_readf (psf, "j", chunk_size) ;
+						break ;
+						} ;
+					psf_log_printf (psf, " %M : %d\n", marker, chunk_size) ;
+					{	uint8_t bytes [6] ;
+						int16_t gain ;
+
+						if (psf->instrument == NULL && (psf->instrument = psf_instrument_alloc ()) == NULL)
+							return SFE_MALLOC_FAILED ;
+
+						psf_binheader_readf (psf, "b", bytes, 6) ;
+						psf_log_printf (psf, "  Base Note : %u\n  Detune    : %u\n"
+											"  Low  Note : %u\n  High Note : %u\n"
+											"  Low  Vel. : %u\n  High Vel. : %u\n",
+											bytes [0], bytes [1], bytes [2], bytes [3], bytes [4], bytes [5]) ;
+						psf->instrument->basenote = bytes [0] ;
+						psf->instrument->detune = bytes [1] ;
+						psf->instrument->key_lo = bytes [2] ;
+						psf->instrument->key_hi = bytes [3] ;
+						psf->instrument->velocity_lo = bytes [4] ;
+						psf->instrument->velocity_hi = bytes [5] ;
+						psf_binheader_readf (psf, "E2", &gain) ;
+						psf->instrument->gain = gain ;
+						psf_log_printf (psf, "  Gain (dB) : %d\n", gain) ;
+						} ;
+					{	int16_t	mode ; /* 0 - no loop, 1 - forward looping, 2 - backward looping */
+						const char	*loop_mode ;
+						uint16_t begin, end ;
+
+						psf_binheader_readf (psf, "E222", &mode, &begin, &end) ;
+						loop_mode = get_loop_mode_str (mode) ;
+						mode = get_loop_mode (mode) ;
+						if (mode == SF_LOOP_NONE)
+						{	psf->instrument->loop_count = 0 ;
+							psf->instrument->loops [0].mode = SF_LOOP_NONE ;
+							}
+						else
+						{	psf->instrument->loop_count = 1 ;
+							psf->instrument->loops [0].mode = SF_LOOP_FORWARD ;
+							psf->instrument->loops [0].start = begin ;
+							psf->instrument->loops [0].end = end ;
+							psf->instrument->loops [0].count = 0 ;
+							} ;
+						psf_log_printf (psf, "  Sustain\n   mode  : %d => %s\n   begin : %u\n   end   : %u\n",
+											mode, loop_mode, begin, end) ;
+						psf_binheader_readf (psf, "E222", &mode, &begin, &end) ;
+						loop_mode = get_loop_mode_str (mode) ;
+						mode = get_loop_mode (mode) ;
+						if (mode == SF_LOOP_NONE)
+							psf->instrument->loops [1].mode = SF_LOOP_NONE ;
+						else
+						{	psf->instrument->loop_count += 1 ;
+							psf->instrument->loops [1].mode = SF_LOOP_FORWARD ;
+							psf->instrument->loops [1].start = begin ;
+							psf->instrument->loops [1].end = end ;
+							psf->instrument->loops [1].count = 0 ;
+							} ;
+						psf_log_printf (psf, "  Release\n   mode  : %d => %s\n   begin : %u\n   end   : %u\n",
+										mode, loop_mode, begin, end) ;
+						} ;
+					instr_found++ ;
+					break ;
+
+			case basc_MARKER :
+					psf_log_printf (psf, " basc : %u\n", chunk_size) ;
+
+					if ((error = aiff_read_basc_chunk (psf, chunk_size)))
+						return error ;
+					break ;
+
+			case MARK_MARKER :
+					psf_log_printf (psf, " %M : %d\n", marker, chunk_size) ;
+					{	uint16_t mark_id, n = 0 ;
+						uint32_t position ;
+
+						bytesread = psf_binheader_readf (psf, "E2", &n) ;
+						mark_count = n ;
+						psf_log_printf (psf, "  Count : %u\n", mark_count) ;
+						if (paiff->markstr != NULL)
+						{	psf_log_printf (psf, "*** Second MARK chunk found. Throwing away the first.\n") ;
+							free (paiff->markstr) ;
+							} ;
+						paiff->markstr = calloc (mark_count, sizeof (MARK_ID_POS)) ;
+						if (paiff->markstr == NULL)
+							return SFE_MALLOC_FAILED ;
+
+						if (mark_count > 2500) /* 2500 is close to the largest number of cues possible because of block sizes */
+						{	psf_log_printf (psf, "  More than 2500 markers, skipping!\n") ;
+							psf_binheader_readf (psf, "j", chunk_size - bytesread) ;
+							break ;
+						} ;
+
+						if (psf->cues)
+						{	free (psf->cues) ;
+							psf->cues = NULL ;
+							} ;
+						if ((psf->cues = psf_cues_alloc (mark_count)) == NULL)
+							return SFE_MALLOC_FAILED ;
+
+						for (n = 0 ; n < mark_count && bytesread < chunk_size ; n++)
+						{	uint32_t pstr_len ;
+							uint8_t ch ;
+
+							bytesread += psf_binheader_readf (psf, "E241", &mark_id, &position, &ch) ;
+							psf_log_printf (psf, "   Mark ID  : %u\n   Position : %u\n", mark_id, position) ;
+
+							psf->cues->cue_points [n].indx = mark_id ;
+							psf->cues->cue_points [n].position = 0 ;
+							psf->cues->cue_points [n].fcc_chunk = MAKE_MARKER ('d', 'a', 't', 'a') ; /* always data */
+							psf->cues->cue_points [n].chunk_start = 0 ;
+							psf->cues->cue_points [n].block_start = 0 ;
+							psf->cues->cue_points [n].sample_offset = position ;
+
+							pstr_len = (ch & 1) ? ch : ch + 1 ;
+
+							if (pstr_len < sizeof (ubuf.scbuf) - 1)
+							{	bytesread += psf_binheader_readf (psf, "b", ubuf.scbuf, pstr_len) ;
+								ubuf.scbuf [pstr_len] = 0 ;
+								}
+							else
+							{	uint32_t read_len = pstr_len - (sizeof (ubuf.scbuf) - 1) ;
+								bytesread += psf_binheader_readf (psf, "bj", ubuf.scbuf, read_len, pstr_len - read_len) ;
+								ubuf.scbuf [sizeof (ubuf.scbuf) - 1] = 0 ;
+								}
+
+							psf_log_printf (psf, "   Name     : %s\n", ubuf.scbuf) ;
+
+							psf_strlcpy (psf->cues->cue_points [n].name, sizeof (psf->cues->cue_points [n].name), ubuf.cbuf) ;
+
+							paiff->markstr [n].markerID = mark_id ;
+							paiff->markstr [n].position = position ;
+							/*
+							**	TODO if ubuf.scbuf is equal to
+							**	either Beg_loop, Beg loop or beg loop and spam
+							**	if (psf->instrument == NULL && (psf->instrument = psf_instrument_alloc ()) == NULL)
+							**		return SFE_MALLOC_FAILED ;
+							*/
+							} ;
+						} ;
+					mark_found++ ;
+					psf_binheader_readf (psf, "j", chunk_size - bytesread) ;
+					break ;
+
+			case FVER_MARKER :
+					found_chunk |= HAVE_FVER ;
+					/* Falls through. */
+
+			case SFX_MARKER :
+					psf_log_printf (psf, " %M : %d\n", marker, chunk_size) ;
+					psf_binheader_readf (psf, "j", chunk_size) ;
+					break ;
+
+			case NONE_MARKER :
+					/* Fix for broken AIFC files with incorrect COMM chunk length. */
+					chunk_size = (chunk_size >> 24) - 3 ;
+					psf_log_printf (psf, " %M : %d\n", marker, chunk_size) ;
+					psf_binheader_readf (psf, "j", make_size_t (chunk_size)) ;
+					break ;
+
+			case CHAN_MARKER :
+					if (chunk_size < 12)
+					{	psf_log_printf (psf, " %M : %d (should be >= 12)\n", marker, chunk_size) ;
+						psf_binheader_readf (psf, "j", chunk_size) ;
+						break ;
+						}
+
+					psf_log_printf (psf, " %M : %d\n", marker, chunk_size) ;
+
+					if ((error = aiff_read_chanmap (psf, chunk_size)))
+						return error ;
+					break ;
+
+			default :
+					if (chunk_size >= 0xffff0000)
+					{	done = SF_TRUE ;
+						psf_log_printf (psf, "*** Unknown chunk marker (%X) at position %D with length %u. Exiting parser.\n", marker, psf_ftell (psf) - 8, chunk_size) ;
+						break ;
+						} ;
+
+					if (psf_isprint ((marker >> 24) & 0xFF) && psf_isprint ((marker >> 16) & 0xFF)
+						&& psf_isprint ((marker >> 8) & 0xFF) && psf_isprint (marker & 0xFF))
+					{	psf_log_printf (psf, " %M : %u (unknown marker)\n", marker, chunk_size) ;
+
+						psf_binheader_readf (psf, "j", chunk_size) ;
+						break ;
+						} ;
+
+					if (psf_ftell (psf) & 0x03)
+					{	psf_log_printf (psf, "  Unknown chunk marker at position %D. Resynching.\n", psf_ftell (psf) - 8) ;
+						psf_binheader_readf (psf, "j", -3) ;
+						break ;
+						} ;
+					psf_log_printf (psf, "*** Unknown chunk marker %X at position %D. Exiting parser.\n", marker, psf_ftell (psf)) ;
+					done = SF_TRUE ;
+					break ;
+			} ;	/* switch (marker) */
+
+		if (chunk_size >= psf->filelength)
+		{	psf_log_printf (psf, "*** Chunk size %u > file length %D. Exiting parser.\n", chunk_size, psf->filelength) ;
+			break ;
+			} ;
+
+		if ((! psf->sf.seekable) && (found_chunk & HAVE_SSND))
+			break ;
+
+		if (psf_ftell (psf) >= psf->filelength - (2 * SIGNED_SIZEOF (int32_t)))
+			break ;
+		} ; /* while (1) */
+
+	if (instr_found && mark_found)
+	{	int ji, str_index ;
+		/* Next loop will convert markers to loop positions for internal handling */
+		for (ji = 0 ; ji < psf->instrument->loop_count ; ji ++)
+		{	if (ji < ARRAY_LEN (psf->instrument->loops))
+			{	psf->instrument->loops [ji].start = marker_to_position (paiff->markstr, psf->instrument->loops [ji].start, mark_count) ;
+				psf->instrument->loops [ji].end = marker_to_position (paiff->markstr, psf->instrument->loops [ji].end, mark_count) ;
+				psf->instrument->loops [ji].mode = SF_LOOP_FORWARD ;
+				} ;
+			} ;
+
+		/* The markers that correspond to loop positions can now be removed from cues struct */
+		if (psf->cues->cue_count > (uint32_t) (psf->instrument->loop_count * 2))
+		{	uint32_t j ;
+
+			for (j = 0 ; j < psf->cues->cue_count - (uint32_t) (psf->instrument->loop_count * 2) ; j ++)
+			{	/* This simply copies the information in cues above loop positions and writes it at current count instead */
+				psf->cues->cue_points [j].indx = psf->cues->cue_points [j + psf->instrument->loop_count * 2].indx ;
+				psf->cues->cue_points [j].position = psf->cues->cue_points [j + psf->instrument->loop_count * 2].position ;
+				psf->cues->cue_points [j].fcc_chunk = psf->cues->cue_points [j + psf->instrument->loop_count * 2].fcc_chunk ;
+				psf->cues->cue_points [j].chunk_start = psf->cues->cue_points [j + psf->instrument->loop_count * 2].chunk_start ;
+				psf->cues->cue_points [j].block_start = psf->cues->cue_points [j + psf->instrument->loop_count * 2].block_start ;
+				psf->cues->cue_points [j].sample_offset = psf->cues->cue_points [j + psf->instrument->loop_count * 2].sample_offset ;
+				for (str_index = 0 ; str_index < 256 ; str_index++)
+					psf->cues->cue_points [j].name [str_index] = psf->cues->cue_points [j + psf->instrument->loop_count * 2].name [str_index] ;
+				} ;
+			psf->cues->cue_count -= psf->instrument->loop_count * 2 ;
+			} else
+			{	/* All the cues were in fact loop positions so we can actually remove the cues altogether */
+				free (psf->cues) ;
+				psf->cues = NULL ;
+				}
+		} ;
+
+	if (psf->sf.channels < 1)
+		return SFE_CHANNEL_COUNT_ZERO ;
+
+	if (psf->sf.channels > SF_MAX_CHANNELS)
+		return SFE_CHANNEL_COUNT ;
+
+	if (! (found_chunk & HAVE_FORM))
+		return SFE_AIFF_NO_FORM ;
+
+	if (! (found_chunk & HAVE_AIFF))
+		return SFE_AIFF_COMM_NO_FORM ;
+
+	if (! (found_chunk & HAVE_COMM))
+		return SFE_AIFF_SSND_NO_COMM ;
+
+	if (! psf->dataoffset)
+		return SFE_AIFF_NO_DATA ;
+
+	return 0 ;
+} /* aiff_read_header */
+
+static int
+aiff_close (SF_PRIVATE *psf)
+{	AIFF_PRIVATE *paiff = psf->container_data ;
+
+	if (paiff != NULL && paiff->markstr != NULL)
+	{	free (paiff->markstr) ;
+		paiff->markstr = NULL ;
+		} ;
+
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	aiff_write_tailer (psf) ;
+		aiff_write_header (psf, SF_TRUE) ;
+		} ;
+
+	return 0 ;
+} /* aiff_close */
+
+static int
+aiff_read_comm_chunk (SF_PRIVATE *psf, COMM_CHUNK *comm_fmt)
+{	BUF_UNION	ubuf ;
+	int subformat, samplerate ;
+
+	ubuf.scbuf [0] = 0 ;
+
+	/* The COMM chunk has an int aligned to an odd word boundary. Some
+	** processors are not able to deal with this (ie bus fault) so we have
+	** to take special care.
+	*/
+
+	psf_binheader_readf (psf, "E242b", &(comm_fmt->numChannels), &(comm_fmt->numSampleFrames),
+				&(comm_fmt->sampleSize), &(comm_fmt->sampleRate), SIGNED_SIZEOF (comm_fmt->sampleRate)) ;
+
+	if (comm_fmt->size > 0x10000 && (comm_fmt->size & 0xffff) == 0)
+	{	psf_log_printf (psf, " COMM : %d (0x%x) *** should be ", comm_fmt->size, comm_fmt->size) ;
+		comm_fmt->size = ENDSWAP_32 (comm_fmt->size) ;
+		psf_log_printf (psf, "%d (0x%x)\n", comm_fmt->size, comm_fmt->size) ;
+		}
+	else
+		psf_log_printf (psf, " COMM : %d\n", comm_fmt->size) ;
+
+	if (comm_fmt->size == SIZEOF_AIFF_COMM)
+		comm_fmt->encoding = NONE_MARKER ;
+	else if (comm_fmt->size == SIZEOF_AIFC_COMM_MIN)
+		psf_binheader_readf (psf, "Em", &(comm_fmt->encoding)) ;
+	else if (comm_fmt->size >= SIZEOF_AIFC_COMM)
+	{	uint8_t encoding_len ;
+		unsigned read_len ;
+
+		psf_binheader_readf (psf, "Em1", &(comm_fmt->encoding), &encoding_len) ;
+
+		comm_fmt->size = SF_MIN (sizeof (ubuf.scbuf), make_size_t (comm_fmt->size)) ;
+		memset (ubuf.scbuf, 0, comm_fmt->size) ;
+		read_len = comm_fmt->size - SIZEOF_AIFC_COMM + 1 ;
+		psf_binheader_readf (psf, "b", ubuf.scbuf, read_len) ;
+		ubuf.scbuf [read_len + 1] = 0 ;
+		} ;
+
+	samplerate = tenbytefloat2int (comm_fmt->sampleRate) ;
+
+	psf_log_printf (psf, "  Sample Rate : %d\n", samplerate) ;
+	psf_log_printf (psf, "  Frames      : %u%s\n", comm_fmt->numSampleFrames, (comm_fmt->numSampleFrames == 0 && psf->filelength > 104) ? " (Should not be 0)" : "") ;
+
+	if (comm_fmt->numChannels < 1 || comm_fmt->numChannels > SF_MAX_CHANNELS)
+	{	psf_log_printf (psf, "  Channels    : %d (should be >= 1 and < %d)\n", comm_fmt->numChannels, SF_MAX_CHANNELS) ;
+		return SFE_CHANNEL_COUNT_BAD ;
+		} ;
+
+	psf_log_printf (psf, "  Channels    : %d\n", comm_fmt->numChannels) ;
+
+	/* Found some broken 'fl32' files with comm.samplesize == 16. Fix it here. */
+	if ((comm_fmt->encoding == fl32_MARKER || comm_fmt->encoding == FL32_MARKER) && comm_fmt->sampleSize != 32)
+	{	psf_log_printf (psf, "  Sample Size : %d (should be 32)\n", comm_fmt->sampleSize) ;
+		comm_fmt->sampleSize = 32 ;
+		}
+	else if ((comm_fmt->encoding == fl64_MARKER || comm_fmt->encoding == FL64_MARKER) && comm_fmt->sampleSize != 64)
+	{	psf_log_printf (psf, "  Sample Size : %d (should be 64)\n", comm_fmt->sampleSize) ;
+		comm_fmt->sampleSize = 64 ;
+		}
+	else
+		psf_log_printf (psf, "  Sample Size : %d\n", comm_fmt->sampleSize) ;
+
+
+	if ((psf->sf.channels != comm_fmt->numChannels) && psf->peak_info)
+	{	psf_log_printf (psf, "  *** channel count changed, discarding existing PEAK chunk\n") ;
+		free (psf->peak_info) ;
+		psf->peak_info = NULL ;
+		} ;
+
+	subformat = s_bitwidth_to_subformat (comm_fmt->sampleSize) ;
+
+	psf->sf.samplerate = samplerate ;
+	psf->sf.frames = comm_fmt->numSampleFrames ;
+	psf->sf.channels = comm_fmt->numChannels ;
+	psf->bytewidth = BITWIDTH2BYTES (comm_fmt->sampleSize) ;
+
+	psf->endian = SF_ENDIAN_BIG ;
+
+	switch (comm_fmt->encoding)
+	{	case NONE_MARKER :
+				psf->sf.format = (SF_FORMAT_AIFF | subformat) ;
+				break ;
+
+		case twos_MARKER :
+		case in24_MARKER :
+		case in32_MARKER :
+				psf->sf.format = (SF_ENDIAN_BIG | SF_FORMAT_AIFF | subformat) ;
+				break ;
+
+		case sowt_MARKER :
+		case ni24_MARKER :
+		case ni32_MARKER :
+				psf->endian = SF_ENDIAN_LITTLE ;
+				psf->sf.format = (SF_ENDIAN_LITTLE | SF_FORMAT_AIFF | subformat) ;
+				break ;
+
+		case fl32_MARKER :
+		case FL32_MARKER :
+				psf->sf.format = (SF_FORMAT_AIFF | SF_FORMAT_FLOAT) ;
+				break ;
+
+		case ulaw_MARKER :
+		case ULAW_MARKER :
+				psf->sf.format = (SF_FORMAT_AIFF | SF_FORMAT_ULAW) ;
+				break ;
+
+		case alaw_MARKER :
+		case ALAW_MARKER :
+				psf->sf.format = (SF_FORMAT_AIFF | SF_FORMAT_ALAW) ;
+				break ;
+
+		case fl64_MARKER :
+		case FL64_MARKER :
+				psf->sf.format = (SF_FORMAT_AIFF | SF_FORMAT_DOUBLE) ;
+				break ;
+
+		case raw_MARKER :
+				psf->sf.format = (SF_FORMAT_AIFF | SF_FORMAT_PCM_U8) ;
+				break ;
+
+		case DWVW_MARKER :
+				psf->sf.format = SF_FORMAT_AIFF ;
+				switch (comm_fmt->sampleSize)
+				{	case 12 :
+						psf->sf.format |= SF_FORMAT_DWVW_12 ;
+						break ;
+					case 16 :
+						psf->sf.format |= SF_FORMAT_DWVW_16 ;
+						break ;
+					case 24 :
+						psf->sf.format |= SF_FORMAT_DWVW_24 ;
+						break ;
+
+					default :
+						psf->sf.format |= SF_FORMAT_DWVW_N ;
+						break ;
+					} ;
+				break ;
+
+		case GSM_MARKER :
+				psf->sf.format = (SF_FORMAT_AIFF | SF_FORMAT_GSM610) ;
+				break ;
+
+
+		case ima4_MARKER :
+				psf->endian = SF_ENDIAN_BIG ;
+				psf->sf.format = (SF_FORMAT_AIFF | SF_FORMAT_IMA_ADPCM) ;
+				break ;
+
+		default :
+			psf_log_printf (psf, "AIFC : Unimplemented format : %M\n", comm_fmt->encoding) ;
+			return SFE_UNIMPLEMENTED ;
+		} ;
+
+	if (! ubuf.scbuf [0])
+		psf_log_printf (psf, "  Encoding    : %M\n", comm_fmt->encoding) ;
+	else
+		psf_log_printf (psf, "  Encoding    : %M => %s\n", comm_fmt->encoding, ubuf.scbuf) ;
+
+	return 0 ;
+} /* aiff_read_comm_chunk */
+
+
+/*==========================================================================================
+*/
+
+static int
+aiff_rewrite_header (SF_PRIVATE *psf)
+{	AIFF_PRIVATE *paiff = psf->container_data ;
+
+	/* Assuming here that the header has already been written and just
+	** needs to be corrected for new data length. That means that we
+	** only change the length fields of the FORM and SSND chunks ;
+	** everything else can be skipped over.
+	*/
+	int k, ch, comm_size, comm_frames ;
+	sf_count_t header_len ;
+
+	/* Calculate the header length rather than use dataoffset, as AIFF files
+	** can have additional padding offset bytes which aren't usefully a part of
+	** the header.
+	*/
+	header_len = paiff->ssnd_offset + 8 + SIZEOF_SSND_CHUNK ;
+	if (psf->header.len < header_len || header_len > psf->dataoffset)
+		return SFE_INTERNAL ;
+	psf_fseek (psf, 0, SEEK_SET) ;
+	psf_fread (psf->header.ptr, header_len, 1, psf) ;
+
+	psf->header.indx = 0 ;
+
+	/* FORM chunk. */
+	psf_binheader_writef (psf, "Etm8", BHWm (FORM_MARKER), BHW8 (psf->filelength - 8)) ;
+
+	/* COMM chunk. */
+	if ((k = psf_find_read_chunk_m32 (&psf->rchunks, COMM_MARKER)) >= 0)
+	{	psf->header.indx = psf->rchunks.chunks [k].offset - 8 ;
+		comm_frames = psf->sf.frames ;
+		comm_size = psf->rchunks.chunks [k].len ;
+		psf_binheader_writef (psf, "Em42t4", BHWm (COMM_MARKER), BHW4 (comm_size), BHW2 (psf->sf.channels), BHW4 (comm_frames)) ;
+		} ;
+
+	/* PEAK chunk. */
+	if ((k = psf_find_read_chunk_m32 (&psf->rchunks, PEAK_MARKER)) >= 0)
+	{	psf->header.indx = psf->rchunks.chunks [k].offset - 8 ;
+		psf_binheader_writef (psf, "Em4", BHWm (PEAK_MARKER), BHW4 (AIFF_PEAK_CHUNK_SIZE (psf->sf.channels))) ;
+		psf_binheader_writef (psf, "E44", BHW4 (1), BHW4 (time (NULL))) ;
+		for (ch = 0 ; ch < psf->sf.channels ; ch++)
+			psf_binheader_writef (psf, "Eft8", BHWf ((float) psf->peak_info->peaks [ch].value), BHW8 (psf->peak_info->peaks [ch].position)) ;
+		} ;
+
+
+	/* SSND chunk. */
+	if ((k = psf_find_read_chunk_m32 (&psf->rchunks, SSND_MARKER)) >= 0)
+	{	psf->header.indx = psf->rchunks.chunks [k].offset - 8 ;
+		psf_binheader_writef (psf, "Etm8", BHWm (SSND_MARKER), BHW8 (psf->datalength + SIZEOF_SSND_CHUNK)) ;
+		} ;
+
+	/* Header mangling complete so write it out. */
+	psf_fseek (psf, 0, SEEK_SET) ;
+	psf_fwrite (psf->header.ptr, psf->header.indx, 1, psf) ;
+
+	return 0 ;
+} /* aiff_rewrite_header */
+
+static int
+aiff_write_header (SF_PRIVATE *psf, int calc_length)
+{	sf_count_t		current ;
+	AIFF_PRIVATE	*paiff ;
+	uint8_t	comm_sample_rate [10], comm_zero_bytes [2] = { 0, 0 } ;
+	uint32_t	comm_type, comm_size, comm_encoding, comm_frames = 0, uk ;
+	int				ret, k, endian, has_data = SF_FALSE ;
+	int16_t			bit_width ;
+
+	if ((paiff = psf->container_data) == NULL)
+		return SFE_INTERNAL ;
+
+	current = psf_ftell (psf) ;
+
+	if (current > psf->dataoffset)
+		has_data = SF_TRUE ;
+
+	if (calc_length)
+	{	psf->filelength = psf_get_filelen (psf) ;
+
+		psf->datalength = psf->filelength - psf->dataoffset ;
+		if (psf->dataend)
+			psf->datalength -= psf->filelength - psf->dataend ;
+
+		if (psf->bytewidth > 0)
+			psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ;
+		} ;
+
+	if (psf->file.mode == SFM_RDWR && psf->dataoffset > 0 && psf->rchunks.count > 0)
+	{	if ((ret = aiff_rewrite_header (psf)) != 0)
+			return ret ;
+		if (current > 0)
+			psf_fseek (psf, current, SEEK_SET) ;
+		return 0 ;
+		} ;
+
+	endian = SF_ENDIAN (psf->sf.format) ;
+	if (CPU_IS_LITTLE_ENDIAN && endian == SF_ENDIAN_CPU)
+		endian = SF_ENDIAN_LITTLE ;
+
+	/* Standard value here. */
+	bit_width = psf->bytewidth * 8 ;
+	comm_frames = (psf->sf.frames > 0xFFFFFFFF) ? 0xFFFFFFFF : psf->sf.frames ;
+
+	switch (SF_CODEC (psf->sf.format) | endian)
+	{	case SF_FORMAT_PCM_S8 | SF_ENDIAN_BIG :
+			psf->endian = SF_ENDIAN_BIG ;
+			comm_type = AIFC_MARKER ;
+			comm_size = SIZEOF_AIFC_COMM ;
+			comm_encoding = twos_MARKER ;
+			break ;
+
+		case SF_FORMAT_PCM_S8 | SF_ENDIAN_LITTLE :
+			psf->endian = SF_ENDIAN_LITTLE ;
+			comm_type = AIFC_MARKER ;
+			comm_size = SIZEOF_AIFC_COMM ;
+			comm_encoding = sowt_MARKER ;
+			break ;
+
+		case SF_FORMAT_PCM_16 | SF_ENDIAN_BIG :
+			psf->endian = SF_ENDIAN_BIG ;
+			comm_type = AIFC_MARKER ;
+			comm_size = SIZEOF_AIFC_COMM ;
+			comm_encoding = twos_MARKER ;
+			break ;
+
+		case SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE :
+			psf->endian = SF_ENDIAN_LITTLE ;
+			comm_type = AIFC_MARKER ;
+			comm_size = SIZEOF_AIFC_COMM ;
+			comm_encoding = sowt_MARKER ;
+			break ;
+
+		case SF_FORMAT_PCM_24 | SF_ENDIAN_BIG :
+			psf->endian = SF_ENDIAN_BIG ;
+			comm_type = AIFC_MARKER ;
+			comm_size = SIZEOF_AIFC_COMM ;
+			comm_encoding = in24_MARKER ;
+			break ;
+
+		case SF_FORMAT_PCM_24 | SF_ENDIAN_LITTLE :
+			psf->endian = SF_ENDIAN_LITTLE ;
+			comm_type = AIFC_MARKER ;
+			comm_size = SIZEOF_AIFC_COMM ;
+			comm_encoding = ni24_MARKER ;
+			break ;
+
+		case SF_FORMAT_PCM_32 | SF_ENDIAN_BIG :
+			psf->endian = SF_ENDIAN_BIG ;
+			comm_type = AIFC_MARKER ;
+			comm_size = SIZEOF_AIFC_COMM ;
+			comm_encoding = in32_MARKER ;
+			break ;
+
+		case SF_FORMAT_PCM_32 | SF_ENDIAN_LITTLE :
+			psf->endian = SF_ENDIAN_LITTLE ;
+			comm_type = AIFC_MARKER ;
+			comm_size = SIZEOF_AIFC_COMM ;
+			comm_encoding = ni32_MARKER ;
+			break ;
+
+		case SF_FORMAT_PCM_S8 :			/* SF_ENDIAN_FILE */
+		case SF_FORMAT_PCM_16 :
+		case SF_FORMAT_PCM_24 :
+		case SF_FORMAT_PCM_32 :
+			psf->endian = SF_ENDIAN_BIG ;
+			comm_type = AIFF_MARKER ;
+			comm_size = SIZEOF_AIFF_COMM ;
+			comm_encoding = 0 ;
+			break ;
+
+		case SF_FORMAT_FLOAT :					/* Big endian floating point. */
+				psf->endian = SF_ENDIAN_BIG ;
+				comm_type = AIFC_MARKER ;
+				comm_size = SIZEOF_AIFC_COMM ;
+				comm_encoding = FL32_MARKER ;	/* Use 'FL32' because its easier to read. */
+				break ;
+
+		case SF_FORMAT_DOUBLE :					/* Big endian double precision floating point. */
+				psf->endian = SF_ENDIAN_BIG ;
+				comm_type = AIFC_MARKER ;
+				comm_size = SIZEOF_AIFC_COMM ;
+				comm_encoding = FL64_MARKER ;	/* Use 'FL64' because its easier to read. */
+				break ;
+
+		case SF_FORMAT_ULAW :
+				psf->endian = SF_ENDIAN_BIG ;
+				comm_type = AIFC_MARKER ;
+				comm_size = SIZEOF_AIFC_COMM ;
+				comm_encoding = ulaw_MARKER ;
+				break ;
+
+		case SF_FORMAT_ALAW :
+				psf->endian = SF_ENDIAN_BIG ;
+				comm_type = AIFC_MARKER ;
+				comm_size = SIZEOF_AIFC_COMM ;
+				comm_encoding = alaw_MARKER ;
+				break ;
+
+		case SF_FORMAT_PCM_U8 :
+				psf->endian = SF_ENDIAN_BIG ;
+				comm_type = AIFC_MARKER ;
+				comm_size = SIZEOF_AIFC_COMM ;
+				comm_encoding = raw_MARKER ;
+				break ;
+
+		case SF_FORMAT_DWVW_12 :
+				psf->endian = SF_ENDIAN_BIG ;
+				comm_type = AIFC_MARKER ;
+				comm_size = SIZEOF_AIFC_COMM ;
+				comm_encoding = DWVW_MARKER ;
+
+				/* Override standard value here.*/
+				bit_width = 12 ;
+				break ;
+
+		case SF_FORMAT_DWVW_16 :
+				psf->endian = SF_ENDIAN_BIG ;
+				comm_type = AIFC_MARKER ;
+				comm_size = SIZEOF_AIFC_COMM ;
+				comm_encoding = DWVW_MARKER ;
+
+				/* Override standard value here.*/
+				bit_width = 16 ;
+				break ;
+
+		case SF_FORMAT_DWVW_24 :
+				psf->endian = SF_ENDIAN_BIG ;
+				comm_type = AIFC_MARKER ;
+				comm_size = SIZEOF_AIFC_COMM ;
+				comm_encoding = DWVW_MARKER ;
+
+				/* Override standard value here.*/
+				bit_width = 24 ;
+				break ;
+
+		case SF_FORMAT_GSM610 :
+				psf->endian = SF_ENDIAN_BIG ;
+				comm_type = AIFC_MARKER ;
+				comm_size = SIZEOF_AIFC_COMM ;
+				comm_encoding = GSM_MARKER ;
+
+				/* Override standard value here.*/
+				bit_width = 16 ;
+				break ;
+
+		case SF_FORMAT_IMA_ADPCM :
+				psf->endian = SF_ENDIAN_BIG ;
+				comm_type = AIFC_MARKER ;
+				comm_size = SIZEOF_AIFC_COMM ;
+				comm_encoding = ima4_MARKER ;
+
+				/* Override standard value here.*/
+				bit_width = 16 ;
+				comm_frames = psf->sf.frames / AIFC_IMA4_SAMPLES_PER_BLOCK ;
+				break ;
+
+		default : return SFE_BAD_OPEN_FORMAT ;
+		} ;
+
+	/* Reset the current header length to zero. */
+	psf->header.ptr [0] = 0 ;
+	psf->header.indx = 0 ;
+	psf_fseek (psf, 0, SEEK_SET) ;
+
+	psf_binheader_writef (psf, "Etm8", BHWm (FORM_MARKER), BHW8 (psf->filelength - 8)) ;
+
+	/* Write AIFF/AIFC marker and COM chunk. */
+	if (comm_type == AIFC_MARKER)
+		/* AIFC must have an FVER chunk. */
+		psf_binheader_writef (psf, "Emm44", BHWm (comm_type), BHWm (FVER_MARKER), BHW4 (4), BHW4 (0xA2805140)) ;
+	else
+		psf_binheader_writef (psf, "Em", BHWm (comm_type)) ;
+
+	paiff->comm_offset = psf->header.indx - 8 ;
+
+	memset (comm_sample_rate, 0, sizeof (comm_sample_rate)) ;
+	uint2tenbytefloat (psf->sf.samplerate, comm_sample_rate) ;
+
+	psf_binheader_writef (psf, "Em42t42", BHWm (COMM_MARKER), BHW4 (comm_size), BHW2 (psf->sf.channels), BHW4 (comm_frames), BHW2 (bit_width)) ;
+	psf_binheader_writef (psf, "b", BHWv (comm_sample_rate), BHWz (sizeof (comm_sample_rate))) ;
+
+	/* AIFC chunks have some extra data. */
+	if (comm_type == AIFC_MARKER)
+		psf_binheader_writef (psf, "mb", BHWm (comm_encoding), BHWv (comm_zero_bytes), BHWz (sizeof (comm_zero_bytes))) ;
+
+	if (psf->channel_map && paiff->chanmap_tag)
+		psf_binheader_writef (psf, "Em4444", BHWm (CHAN_MARKER), BHW4 (12), BHW4 (paiff->chanmap_tag), BHW4 (0), BHW4 (0)) ;
+
+	/* Check if there's a INST chunk to write */
+	if (psf->instrument != NULL && psf->cues != NULL)
+	{	/* Huge chunk of code removed here because it had egregious errors that were
+		** not detected by either the compiler or the tests. It was found when updating
+		** the way psf_binheader_writef works.
+		*/
+		}
+	else if (psf->instrument == NULL && psf->cues != NULL)
+	{	/* There are cues but no loops */
+		uint32_t idx ;
+		int totalStringLength = 0, stringLength ;
+
+		/* Here we count how many bytes will the pascal strings need */
+		for (idx = 0 ; idx < psf->cues->cue_count ; idx++)
+		{	stringLength = strlen (psf->cues->cue_points [idx].name) + 1 ; /* We'll count the first byte also of every pascal string */
+			totalStringLength += stringLength + (stringLength % 2 == 0 ? 0 : 1) ;
+			} ;
+
+		psf_binheader_writef (psf, "Em42",
+			BHWm (MARK_MARKER), BHW4 (2 + psf->cues->cue_count * (2 + 4) + totalStringLength), BHW2 (psf->cues->cue_count)) ;
+
+		for (idx = 0 ; idx < psf->cues->cue_count ; idx++)
+			psf_binheader_writef (psf, "E24p", BHW2 (psf->cues->cue_points [idx].indx), BHW4 (psf->cues->cue_points [idx].sample_offset), BHWp (psf->cues->cue_points [idx].name)) ;
+		} ;
+
+	if (psf->strings.flags & SF_STR_LOCATE_START)
+		aiff_write_strings (psf, SF_STR_LOCATE_START) ;
+
+	if (psf->peak_info != NULL && psf->peak_info->peak_loc == SF_PEAK_START)
+	{	psf_binheader_writef (psf, "Em4", BHWm (PEAK_MARKER), BHW4 (AIFF_PEAK_CHUNK_SIZE (psf->sf.channels))) ;
+		psf_binheader_writef (psf, "E44", BHW4 (1), BHW4 (time (NULL))) ;
+		for (k = 0 ; k < psf->sf.channels ; k++)
+			psf_binheader_writef (psf, "Eft8", BHWf ((float) psf->peak_info->peaks [k].value), BHW8 (psf->peak_info->peaks [k].position)) ;
+		} ;
+
+	/* Write custom headers. */
+	for (uk = 0 ; uk < psf->wchunks.used ; uk++)
+		psf_binheader_writef (psf, "Em4b", BHWm (psf->wchunks.chunks [uk].mark32), BHW4 (psf->wchunks.chunks [uk].len), BHWv (psf->wchunks.chunks [uk].data), BHWz (psf->wchunks.chunks [uk].len)) ;
+
+	/* Write SSND chunk. */
+	paiff->ssnd_offset = psf->header.indx ;
+	psf_binheader_writef (psf, "Etm844", BHWm (SSND_MARKER), BHW8 (psf->datalength + SIZEOF_SSND_CHUNK), BHW4 (0), BHW4 (0)) ;
+
+	/* Header construction complete so write it out. */
+	psf_fwrite (psf->header.ptr, psf->header.indx, 1, psf) ;
+
+	if (psf->error)
+		return psf->error ;
+
+	if (has_data && psf->dataoffset != psf->header.indx)
+		return psf->error = SFE_INTERNAL ;
+
+	psf->dataoffset = psf->header.indx ;
+
+	if (! has_data)
+		psf_fseek (psf, psf->dataoffset, SEEK_SET) ;
+	else if (current > 0)
+		psf_fseek (psf, current, SEEK_SET) ;
+
+	return psf->error ;
+} /* aiff_write_header */
+
+static int
+aiff_write_tailer (SF_PRIVATE *psf)
+{	int		k ;
+
+	/* Reset the current header length to zero. */
+	psf->header.ptr [0] = 0 ;
+	psf->header.indx = 0 ;
+
+	psf->dataend = psf_fseek (psf, 0, SEEK_END) ;
+
+	/* Make sure tailer data starts at even byte offset. Pad if necessary. */
+	if (psf->dataend % 2 == 1)
+	{	psf_fwrite (psf->header.ptr, 1, 1, psf) ;
+		psf->dataend ++ ;
+		} ;
+
+	if (psf->peak_info != NULL && psf->peak_info->peak_loc == SF_PEAK_END)
+	{	psf_binheader_writef (psf, "Em4", BHWm (PEAK_MARKER), BHW4 (AIFF_PEAK_CHUNK_SIZE (psf->sf.channels))) ;
+		psf_binheader_writef (psf, "E44", BHW4 (1), BHW4 (time (NULL))) ;
+		for (k = 0 ; k < psf->sf.channels ; k++)
+			psf_binheader_writef (psf, "Eft8", BHWf ((float) psf->peak_info->peaks [k].value), BHW8 (psf->peak_info->peaks [k].position)) ;
+		} ;
+
+	if (psf->strings.flags & SF_STR_LOCATE_END)
+		aiff_write_strings (psf, SF_STR_LOCATE_END) ;
+
+	/* Write the tailer. */
+	if (psf->header.indx > 0)
+		psf_fwrite (psf->header.ptr, psf->header.indx, 1, psf) ;
+
+	return 0 ;
+} /* aiff_write_tailer */
+
+static void
+aiff_write_strings (SF_PRIVATE *psf, int location)
+{	int	k, slen ;
+
+	for (k = 0 ; k < SF_MAX_STRINGS ; k++)
+	{	if (psf->strings.data [k].type == 0)
+			break ;
+
+		if (psf->strings.data [k].flags != location)
+			continue ;
+
+		switch (psf->strings.data [k].type)
+		{	case SF_STR_SOFTWARE :
+				slen = strlen (psf->strings.storage + psf->strings.data [k].offset) ;
+				psf_binheader_writef (psf, "Em4mb", BHWm (APPL_MARKER), BHW4 (slen + 4), BHWm (m3ga_MARKER), BHWv (psf->strings.storage + psf->strings.data [k].offset), BHWz (slen + (slen & 1))) ;
+				break ;
+
+			case SF_STR_TITLE :
+				psf_binheader_writef (psf, "EmS", BHWm (NAME_MARKER), BHWS (psf->strings.storage + psf->strings.data [k].offset)) ;
+				break ;
+
+			case SF_STR_COPYRIGHT :
+				psf_binheader_writef (psf, "EmS", BHWm (c_MARKER), BHWS (psf->strings.storage + psf->strings.data [k].offset)) ;
+				break ;
+
+			case SF_STR_ARTIST :
+				psf_binheader_writef (psf, "EmS", BHWm (AUTH_MARKER), BHWS (psf->strings.storage + psf->strings.data [k].offset)) ;
+				break ;
+
+			case SF_STR_COMMENT :
+				psf_binheader_writef (psf, "EmS", BHWm (ANNO_MARKER), BHWS (psf->strings.storage + psf->strings.data [k].offset)) ;
+				break ;
+
+			/*
+			case SF_STR_DATE :
+				psf_binheader_writef (psf, "Ems", BHWm (ICRD_MARKER), BHWs (psf->strings.data [k].str)) ;
+				break ;
+			*/
+			} ;
+		} ;
+
+	return ;
+} /* aiff_write_strings */
+
+static int
+aiff_command (SF_PRIVATE * psf, int command, void * UNUSED (data), int UNUSED (datasize))
+{	AIFF_PRIVATE	*paiff ;
+
+	if ((paiff = psf->container_data) == NULL)
+		return SFE_INTERNAL ;
+
+	switch (command)
+	{	case SFC_SET_CHANNEL_MAP_INFO :
+			paiff->chanmap_tag = aiff_caf_find_channel_layout_tag (psf->channel_map, psf->sf.channels) ;
+			return (paiff->chanmap_tag != 0) ;
+
+		default :
+			break ;
+	} ;
+
+	return 0 ;
+} /* aiff_command */
+
+static const char*
+get_loop_mode_str (int16_t mode)
+{	switch (mode)
+	{	case 0 : return "none" ;
+		case 1 : return "forward" ;
+		case 2 : return "backward" ;
+		} ;
+
+	return "*** unknown" ;
+} /* get_loop_mode_str */
+
+static int16_t
+get_loop_mode (int16_t mode)
+{	switch (mode)
+	{	case 0 : return SF_LOOP_NONE ;
+		case 1 : return SF_LOOP_FORWARD ;
+		case 2 : return SF_LOOP_BACKWARD ;
+		} ;
+
+	return SF_LOOP_NONE ;
+} /* get_loop_mode */
+
+/*==========================================================================================
+**	Rough hack at converting from 80 bit IEEE float in AIFF header to an int and
+**	back again. It assumes that all sample rates are between 1 and 800MHz, which
+**	should be OK as other sound file formats use a 32 bit integer to store sample
+**	rate.
+**	There is another (probably better) version in the source code to the SoX but it
+**	has a copyright which probably prevents it from being allowable as GPL/LGPL.
+*/
+
+static int
+tenbytefloat2int (uint8_t *bytes)
+{	int val = 3 ;
+
+	if (bytes [0] & 0x80)	/* Negative number. */
+		return 0 ;
+
+	if (bytes [0] <= 0x3F)	/* Less than 1. */
+		return 1 ;
+
+	if (bytes [0] > 0x40)	/* Way too big. */
+		return 0x4000000 ;
+
+	if (bytes [0] == 0x40 && bytes [1] > 0x1C) /* Too big. */
+		return 800000000 ;
+
+	/* Ok, can handle it. */
+
+	val = (bytes [2] << 23) | (bytes [3] << 15) | (bytes [4] << 7) | (bytes [5] >> 1) ;
+
+	val >>= (29 - bytes [1]) ;
+
+	return val ;
+} /* tenbytefloat2int */
+
+static void
+uint2tenbytefloat (uint32_t num, uint8_t *bytes)
+{	uint32_t mask = 0x40000000 ;
+	int	count ;
+
+	if (num <= 1)
+	{	bytes [0] = 0x3F ;
+		bytes [1] = 0xFF ;
+		bytes [2] = 0x80 ;
+		return ;
+		} ;
+
+	bytes [0] = 0x40 ;
+
+	if (num >= mask)
+	{	bytes [1] = 0x1D ;
+		return ;
+		} ;
+
+	for (count = 0 ; count < 32 ; count ++)
+	{	if (num & mask)
+			break ;
+		mask >>= 1 ;
+		} ;
+
+	num = count < 31 ? num << (count + 1) : 0 ;
+	bytes [1] = 29 - count ;
+	bytes [2] = (num >> 24) & 0xFF ;
+	bytes [3] = (num >> 16) & 0xFF ;
+	bytes [4] = (num >> 8) & 0xFF ;
+	bytes [5] = num & 0xFF ;
+
+} /* uint2tenbytefloat */
+
+static int
+aiff_read_basc_chunk (SF_PRIVATE * psf, int datasize)
+{	const char * type_str ;
+	basc_CHUNK bc ;
+	int count ;
+
+	count = psf_binheader_readf (psf, "E442", &bc.version, &bc.numBeats, &bc.rootNote) ;
+	count += psf_binheader_readf (psf, "E222", &bc.scaleType, &bc.sigNumerator, &bc.sigDenominator) ;
+	count += psf_binheader_readf (psf, "E2j", &bc.loopType, datasize - sizeof (bc)) ;
+
+	psf_log_printf (psf, "  Version ? : %u\n  Num Beats : %u\n  Root Note : 0x%x\n",
+						bc.version, bc.numBeats, bc.rootNote) ;
+
+	switch (bc.scaleType)
+	{	case basc_SCALE_MINOR :
+				type_str = "MINOR" ;
+				break ;
+		case basc_SCALE_MAJOR :
+				type_str = "MAJOR" ;
+				break ;
+		case basc_SCALE_NEITHER :
+				type_str = "NEITHER" ;
+				break ;
+		case basc_SCALE_BOTH :
+				type_str = "BOTH" ;
+				break ;
+		default :
+				type_str = "!!WRONG!!" ;
+				break ;
+		} ;
+
+	psf_log_printf (psf, "  ScaleType : 0x%x (%s)\n", bc.scaleType, type_str) ;
+	psf_log_printf (psf, "  Time Sig  : %d/%d\n", bc.sigNumerator, bc.sigDenominator) ;
+
+	switch (bc.loopType)
+	{	case basc_TYPE_ONE_SHOT :
+				type_str = "One Shot" ;
+				break ;
+		case basc_TYPE_LOOP :
+				type_str = "Loop" ;
+				break ;
+		default:
+				type_str = "!!WRONG!!" ;
+				break ;
+		} ;
+
+	psf_log_printf (psf, "  Loop Type : 0x%x (%s)\n", bc.loopType, type_str) ;
+
+	if (psf->loop_info)
+	{	psf_log_printf (psf, "  Found existing loop info, using last one.\n") ;
+		free (psf->loop_info) ;
+		psf->loop_info = NULL ;
+		} ;
+	if ((psf->loop_info = calloc (1, sizeof (SF_LOOP_INFO))) == NULL)
+		return SFE_MALLOC_FAILED ;
+
+	psf->loop_info->time_sig_num	= bc.sigNumerator ;
+	psf->loop_info->time_sig_den	= bc.sigDenominator ;
+	psf->loop_info->loop_mode		= (bc.loopType == basc_TYPE_ONE_SHOT) ? SF_LOOP_NONE : SF_LOOP_FORWARD ;
+	psf->loop_info->num_beats		= bc.numBeats ;
+
+	/* Can always be recalculated from other known fields. */
+	psf->loop_info->bpm = (1.0 / psf->sf.frames) * psf->sf.samplerate
+							* ((bc.numBeats * 4.0) / bc.sigDenominator) * 60.0 ;
+	psf->loop_info->root_key = bc.rootNote ;
+
+	if (count < datasize)
+		psf_binheader_readf (psf, "j", datasize - count) ;
+
+	return 0 ;
+} /* aiff_read_basc_chunk */
+
+
+static int
+aiff_read_chanmap (SF_PRIVATE * psf, unsigned dword)
+{	const AIFF_CAF_CHANNEL_MAP * map_info ;
+	unsigned channel_bitmap, channel_decriptions, bytesread ;
+	int layout_tag ;
+
+	bytesread = psf_binheader_readf (psf, "444", &layout_tag, &channel_bitmap, &channel_decriptions) ;
+
+	if ((map_info = aiff_caf_of_channel_layout_tag (layout_tag)) == NULL)
+		return 0 ;
+
+	psf_log_printf (psf, "  Tag    : %x\n", layout_tag) ;
+	if (map_info)
+		psf_log_printf (psf, "  Layout : %s\n", map_info->name) ;
+
+	if (bytesread < dword)
+		psf_binheader_readf (psf, "j", dword - bytesread) ;
+
+	if (map_info->channel_map != NULL)
+	{	size_t chanmap_size = SF_MIN (psf->sf.channels, layout_tag & 0xffff) * sizeof (psf->channel_map [0]) ;
+
+		free (psf->channel_map) ;
+
+		if ((psf->channel_map = malloc (chanmap_size)) == NULL)
+			return SFE_MALLOC_FAILED ;
+
+		memcpy (psf->channel_map, map_info->channel_map, chanmap_size) ;
+		} ;
+
+	return 0 ;
+} /* aiff_read_chanmap */
+
+/*==============================================================================
+*/
+
+static int
+aiff_set_chunk (SF_PRIVATE *psf, const SF_CHUNK_INFO * chunk_info)
+{	return psf_save_write_chunk (&psf->wchunks, chunk_info) ;
+} /* aiff_set_chunk */
+
+static SF_CHUNK_ITERATOR *
+aiff_next_chunk_iterator (SF_PRIVATE *psf, SF_CHUNK_ITERATOR * iterator)
+{	return psf_next_chunk_iterator (&psf->rchunks, iterator) ;
+} /* aiff_next_chunk_iterator */
+
+static int
+aiff_get_chunk_size (SF_PRIVATE *psf, const SF_CHUNK_ITERATOR * iterator, SF_CHUNK_INFO * chunk_info)
+{	int indx ;
+
+	if ((indx = psf_find_read_chunk_iterator (&psf->rchunks, iterator)) < 0)
+		return SFE_UNKNOWN_CHUNK ;
+
+	chunk_info->datalen = psf->rchunks.chunks [indx].len ;
+
+	return SFE_NO_ERROR ;
+} /* aiff_get_chunk_size */
+
+static int
+aiff_get_chunk_data (SF_PRIVATE *psf, const SF_CHUNK_ITERATOR * iterator, SF_CHUNK_INFO * chunk_info)
+{	sf_count_t pos ;
+	int indx ;
+
+	if ((indx = psf_find_read_chunk_iterator (&psf->rchunks, iterator)) < 0)
+		return SFE_UNKNOWN_CHUNK ;
+
+	if (chunk_info->data == NULL)
+		return SFE_BAD_CHUNK_DATA_PTR ;
+
+	chunk_info->id_size = psf->rchunks.chunks [indx].id_size ;
+	memcpy (chunk_info->id, psf->rchunks.chunks [indx].id, sizeof (chunk_info->id) / sizeof (*chunk_info->id)) ;
+
+	pos = psf_ftell (psf) ;
+	psf_fseek (psf, psf->rchunks.chunks [indx].offset, SEEK_SET) ;
+	psf_fread (chunk_info->data, SF_MIN (chunk_info->datalen, psf->rchunks.chunks [indx].len), 1, psf) ;
+	psf_fseek (psf, pos, SEEK_SET) ;
+
+	return SFE_NO_ERROR ;
+} /* aiff_get_chunk_data */
diff --git a/src/alac.c b/src/alac.c
new file mode 100644
index 0000000..a6668f3
--- /dev/null
+++ b/src/alac.c
@@ -0,0 +1,999 @@
+/*
+** Copyright (C) 2011-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<stdlib.h>
+#include	<string.h>
+#include	<math.h>
+#include	<errno.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+#include	"ALAC/alac_codec.h"
+#include	"ALAC/ALACBitUtilities.h"
+
+#define		ALAC_MAX_FRAME_SIZE		8192
+#define		ALAC_BYTE_BUFFER_SIZE	0x20000
+#define		ALAC_MAX_CHANNEL_COUNT	8	// Same as kALACMaxChannels in /ALACAudioTypes.h
+
+typedef struct
+{	uint32_t	current, count, allocated ;
+	uint32_t	packet_size [] ;
+} PAKT_INFO ;
+
+typedef struct
+{	sf_count_t	input_data_pos ;
+
+	PAKT_INFO	* pakt_info ;
+
+	int			channels, final_write_block ;
+
+	uint32_t	frames_this_block, partial_block_frames, frames_per_block ;
+	uint32_t	bits_per_sample, kuki_size ;
+
+
+	/* Can't have a decoder and an encoder at the same time so stick
+	** them in a union.
+	*/
+	union
+	{	ALAC_DECODER decoder ;
+		ALAC_ENCODER encoder ;
+	} u ;
+
+	char enctmpname [512] ;
+	FILE *enctmp ;
+
+	uint8_t	byte_buffer [ALAC_MAX_CHANNEL_COUNT * ALAC_BYTE_BUFFER_SIZE] ;
+
+	int	buffer	[] ;
+
+} ALAC_PRIVATE ;
+
+/*============================================================================================
+*/
+
+static int alac_reader_init (SF_PRIVATE *psf, const ALAC_DECODER_INFO * info) ;
+static int alac_writer_init (SF_PRIVATE *psf) ;
+
+static sf_count_t alac_reader_calc_frames (SF_PRIVATE *psf, ALAC_PRIVATE *plac) ;
+
+static sf_count_t alac_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
+static sf_count_t alac_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
+static sf_count_t alac_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
+static sf_count_t alac_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
+
+static sf_count_t alac_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
+static sf_count_t alac_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
+static sf_count_t alac_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
+static sf_count_t alac_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
+
+static sf_count_t	alac_seek	(SF_PRIVATE *psf, int mode, sf_count_t offset) ;
+
+static int	alac_close		(SF_PRIVATE *psf) ;
+static int	alac_byterate	(SF_PRIVATE *psf) ;
+
+static int alac_decode_block (SF_PRIVATE *psf, ALAC_PRIVATE *plac) ;
+static int alac_encode_block (ALAC_PRIVATE *plac) ;
+
+static uint32_t alac_kuki_read (SF_PRIVATE * psf, uint32_t kuki_offset, uint8_t * kuki, size_t kuki_maxlen) ;
+
+static PAKT_INFO * alac_pakt_alloc (uint32_t initial_count) ;
+static PAKT_INFO * alac_pakt_read_decode (SF_PRIVATE * psf, uint32_t pakt_offset) ;
+static PAKT_INFO * alac_pakt_append (PAKT_INFO * info, uint32_t value) ;
+static uint8_t * alac_pakt_encode (const SF_PRIVATE *psf, uint32_t * pakt_size) ;
+static sf_count_t alac_pakt_block_offset (const PAKT_INFO *info, uint32_t block) ;
+
+static const char * alac_error_string (int error) ;
+
+/*============================================================================================
+** ALAC Reader initialisation function.
+*/
+
+int
+alac_init (SF_PRIVATE *psf, const ALAC_DECODER_INFO * info)
+{	int error ;
+
+	if ((psf->codec_data = calloc (1, sizeof (ALAC_PRIVATE) + psf->sf.channels * sizeof (int) * ALAC_MAX_FRAME_SIZE)) == NULL)
+		return SFE_MALLOC_FAILED ;
+
+	psf->codec_close = alac_close ;
+
+	switch (psf->file.mode)
+	{	case SFM_RDWR :
+			return SFE_BAD_MODE_RW ;
+
+		case SFM_READ :
+			if ((error = alac_reader_init (psf, info)))
+				return error ;
+			break ;
+
+		case SFM_WRITE :
+			if ((error = alac_writer_init (psf)))
+				return error ;
+			break ;
+
+		default :
+			psf_log_printf (psf, "%s : Bad psf->file.mode.\n", __func__) ;
+			return SFE_INTERNAL ;
+		} ;
+
+	psf->byterate = alac_byterate ;
+
+	return 0 ;
+} /* aiff_alac_init */
+
+void
+alac_get_desc_chunk_items (int subformat, uint32_t *fmt_flags, uint32_t *frames_per_packet)
+{	switch (subformat)
+	{	case SF_FORMAT_ALAC_16 :
+			*fmt_flags = 1 ;
+			break ;
+		case SF_FORMAT_ALAC_20 :
+			*fmt_flags = 2 ;
+			break ;
+		case SF_FORMAT_ALAC_24 :
+			*fmt_flags = 3 ;
+			break ;
+		case SF_FORMAT_ALAC_32 :
+			*fmt_flags = 4 ;
+			break ;
+		default :
+			break ;
+		} ;
+	*frames_per_packet = ALAC_FRAME_LENGTH ;
+} /* alac_get_desc_chunk_items */
+
+static int
+alac_close	(SF_PRIVATE *psf)
+{	ALAC_PRIVATE *plac ;
+	BUF_UNION	ubuf ;
+
+	plac = psf->codec_data ;
+
+	if (psf->file.mode == SFM_WRITE)
+	{	ALAC_ENCODER *penc = &plac->u.encoder ;
+		SF_CHUNK_INFO chunk_info ;
+		sf_count_t readcount ;
+		uint8_t kuki_data [1024] ;
+		uint32_t pakt_size = 0, saved_partial_block_frames ;
+
+		plac->final_write_block = 1 ;
+		saved_partial_block_frames = plac->partial_block_frames ;
+
+		/*	If a block has been partially assembled, write it out as the final block. */
+		if (plac->partial_block_frames && plac->partial_block_frames < plac->frames_per_block)
+			alac_encode_block (plac) ;
+
+		plac->partial_block_frames = saved_partial_block_frames ;
+
+		alac_get_magic_cookie (penc, kuki_data, &plac->kuki_size) ;
+
+		memset (&chunk_info, 0, sizeof (chunk_info)) ;
+		chunk_info.id_size = snprintf (chunk_info.id, sizeof (chunk_info.id), "kuki") ;
+		chunk_info.data = kuki_data ;
+		chunk_info.datalen = plac->kuki_size ;
+		psf_save_write_chunk (&psf->wchunks, &chunk_info) ;
+
+		memset (&chunk_info, 0, sizeof (chunk_info)) ;
+		chunk_info.id_size = snprintf (chunk_info.id, sizeof (chunk_info.id), "pakt") ;
+		chunk_info.data = alac_pakt_encode (psf, &pakt_size) ;
+		chunk_info.datalen = pakt_size ;
+		psf_save_write_chunk (&psf->wchunks, &chunk_info) ;
+
+		free (chunk_info.data) ;
+		chunk_info.data = NULL ;
+
+		psf->write_header (psf, 1) ;
+
+		if (plac->enctmp != NULL)
+		{	fseek (plac->enctmp, 0, SEEK_SET) ;
+
+			while ((readcount = fread (ubuf.ucbuf, 1, sizeof (ubuf.ucbuf), plac->enctmp)) > 0)
+				psf_fwrite (ubuf.ucbuf, 1, readcount, psf) ;
+			fclose (plac->enctmp) ;
+			remove (plac->enctmpname) ;
+			} ;
+		} ;
+
+	if (plac->pakt_info)
+		free (plac->pakt_info) ;
+	plac->pakt_info = NULL ;
+
+	return 0 ;
+} /* alac_close */
+
+static int
+alac_byterate	(SF_PRIVATE *psf)
+{
+	if (psf->file.mode == SFM_READ)
+		return (psf->datalength * psf->sf.samplerate) / psf->sf.frames ;
+
+	return -1 ;
+} /* alac_byterate */
+
+/*============================================================================================
+** ALAC initialisation Functions.
+*/
+
+static int
+alac_reader_init (SF_PRIVATE *psf, const ALAC_DECODER_INFO * info)
+{	ALAC_PRIVATE	*plac ;
+	uint32_t		kuki_size ;
+	int				error ;
+	union			{ uint8_t kuki [512] ; uint32_t alignment ; } u ;
+
+	if (info == NULL)
+	{	psf_log_printf (psf, "%s : ALAC_DECODER_INFO is NULL.\n", __func__) ;
+		return SFE_INTERNAL ;
+		} ;
+
+	if (info->frames_per_packet > ALAC_FRAME_LENGTH)
+	{	psf_log_printf (psf, "*** Error : frames_per_packet (%u) is too big. ***\n", info->frames_per_packet) ;
+		return SFE_INTERNAL ;
+		} ;
+
+	plac = psf->codec_data ;
+
+	plac->channels			= psf->sf.channels ;
+	plac->frames_per_block	= info->frames_per_packet ;
+	plac->bits_per_sample	= info->bits_per_sample ;
+
+	if (plac->pakt_info != NULL)
+		free (plac->pakt_info) ;
+	plac->pakt_info = alac_pakt_read_decode (psf, info->pakt_offset) ;
+
+	if (plac->pakt_info == NULL)
+	{	psf_log_printf (psf, "%s : alac_pkt_read() returns NULL.\n", __func__) ;
+		return SFE_INTERNAL ;
+		} ;
+
+	/* Read in the ALAC cookie data and pass it to the init function. */
+	kuki_size = alac_kuki_read (psf, info->kuki_offset, u.kuki, sizeof (u.kuki)) ;
+
+	if ((error = alac_decoder_init (&plac->u.decoder, u.kuki, kuki_size)) != ALAC_noErr)
+	{	psf_log_printf (psf, "*** alac_decoder_init() returned %s. ***\n", alac_error_string (error)) ;
+		return SFE_INTERNAL ;
+		} ;
+
+
+	if (plac->u.decoder.mNumChannels != (unsigned) psf->sf.channels)
+	{	psf_log_printf (psf, "*** Initialized decoder has %u channels, but it should be %d. ***\n", plac->u.decoder.mNumChannels, psf->sf.channels) ;
+		return SFE_INTERNAL ;
+		} ;
+
+	switch (info->bits_per_sample)
+	{	case 16 :
+		case 20 :
+		case 24 :
+		case 32 :
+			psf->read_short		= alac_read_s ;
+			psf->read_int		= alac_read_i ;
+			psf->read_float		= alac_read_f ;
+			psf->read_double	= alac_read_d ;
+			break ;
+
+		default :
+			printf ("%s : info->bits_per_sample %u\n", __func__, info->bits_per_sample) ;
+			return SFE_UNSUPPORTED_ENCODING ;
+		} ;
+
+	psf->codec_close	= alac_close ;
+	psf->seek			= alac_seek ;
+
+	psf->sf.frames		= alac_reader_calc_frames (psf, plac) ;
+	alac_seek (psf, SFM_READ, 0) ;
+
+	return 0 ;
+} /* alac_reader_init */
+
+static int
+alac_writer_init (SF_PRIVATE *psf)
+{	ALAC_PRIVATE	*plac ;
+	uint32_t		alac_format_flags = 0 ;
+
+	plac = psf->codec_data ;
+
+	if (psf->file.mode != SFM_WRITE)
+		return SFE_BAD_MODE_RW ;
+
+	plac->channels	= psf->sf.channels ;
+	plac->kuki_size = alac_get_magic_cookie_size (psf->sf.channels) ;
+
+	psf->write_short	= alac_write_s ;
+	psf->write_int		= alac_write_i ;
+	psf->write_float	= alac_write_f ;
+	psf->write_double	= alac_write_d ;
+
+	switch (SF_CODEC (psf->sf.format))
+	{	case SF_FORMAT_ALAC_16 :
+			alac_format_flags	= 1 ;
+			plac->bits_per_sample = 16 ;
+			break ;
+
+		case SF_FORMAT_ALAC_20 :
+			alac_format_flags	= 2 ;
+			plac->bits_per_sample = 20 ;
+			break ;
+
+		case SF_FORMAT_ALAC_24 :
+			alac_format_flags	= 3 ;
+			plac->bits_per_sample = 24 ;
+			break ;
+
+		case SF_FORMAT_ALAC_32 :
+			alac_format_flags	= 4 ;
+			plac->bits_per_sample = 32 ;
+			break ;
+
+		default :
+			psf_log_printf (psf, "%s : Can't figure out bits per sample.\n", __func__) ;
+			return SFE_UNIMPLEMENTED ;
+		} ;
+
+	plac->frames_per_block = ALAC_FRAME_LENGTH ;
+
+	plac->pakt_info = alac_pakt_alloc (2000) ;
+
+	if ((plac->enctmp = psf_open_tmpfile (plac->enctmpname, sizeof (plac->enctmpname))) == NULL)
+	{	psf_log_printf (psf, "Error : Failed to open temp file '%s' : \n", plac->enctmpname, strerror (errno)) ;
+		return SFE_ALAC_FAIL_TMPFILE ;
+		} ;
+
+	alac_encoder_init (&plac->u.encoder, psf->sf.samplerate, psf->sf.channels, alac_format_flags, ALAC_FRAME_LENGTH) ;
+
+	return 0 ;
+} /* alac_writer_init */
+
+/*============================================================================================
+** ALAC block decoder and encoder.
+*/
+
+static inline uint32_t
+alac_reader_next_packet_size (PAKT_INFO * info)
+{	if (info->current >= info->count)
+		return 0 ;
+	return info->packet_size [info->current++] ;
+} /* alac_reader_next_packet_size */
+
+static sf_count_t
+alac_reader_calc_frames (SF_PRIVATE *psf, ALAC_PRIVATE *plac)
+{	sf_count_t	frames = 0 ;
+	uint32_t	current_pos = 1, blocks = 0 ;
+
+	plac->pakt_info->current = 0 ;
+
+	while (current_pos < psf->filelength && current_pos > 0)
+	{	current_pos = alac_reader_next_packet_size (plac->pakt_info) ;
+		blocks = current_pos > 0 ? blocks + 1 : blocks ;
+		} ;
+
+	if (blocks == 0)
+		return 0 ;
+
+	/* Only count full blocks. */
+	frames = plac->frames_per_block * (blocks - 1) ;
+
+	alac_seek (psf, SFM_READ, frames) ;
+	alac_decode_block (psf, plac) ;
+	frames += plac->frames_this_block ;
+
+	plac->pakt_info->current = 0 ;
+
+	return frames ;
+} /* alac_reader_calc_frames */
+
+static int
+alac_decode_block (SF_PRIVATE *psf, ALAC_PRIVATE *plac)
+{	ALAC_DECODER *pdec = &plac->u.decoder ;
+	uint32_t	packet_size ;
+	BitBuffer	bit_buffer ;
+
+	packet_size = alac_reader_next_packet_size (plac->pakt_info) ;
+	if (packet_size == 0)
+	{	if (plac->pakt_info->current < plac->pakt_info->count)
+			psf_log_printf (psf, "packet_size is 0 (%d of %d)\n", plac->pakt_info->current, plac->pakt_info->count) ;
+		return 0 ;
+		} ;
+
+	psf_fseek (psf, plac->input_data_pos, SEEK_SET) ;
+
+	if (packet_size > sizeof (plac->byte_buffer))
+	{	psf_log_printf (psf, "%s : bad packet_size (%u)\n", __func__, packet_size) ;
+		return 0 ;
+		} ;
+
+	if ((packet_size != psf_fread (plac->byte_buffer, 1, packet_size, psf)))
+		return 0 ;
+
+	BitBufferInit (&bit_buffer, plac->byte_buffer, packet_size) ;
+
+	plac->input_data_pos += packet_size ;
+	plac->frames_this_block = 0 ;
+	alac_decode (pdec, &bit_buffer, plac->buffer, plac->frames_per_block, &plac->frames_this_block) ;
+
+	plac->partial_block_frames = 0 ;
+
+	return 1 ;
+} /* alac_decode_block */
+
+
+static int
+alac_encode_block (ALAC_PRIVATE *plac)
+{	ALAC_ENCODER *penc = &plac->u.encoder ;
+	uint32_t num_bytes = 0 ;
+
+	alac_encode (penc, plac->partial_block_frames, plac->buffer, plac->byte_buffer, &num_bytes) ;
+
+	if (fwrite (plac->byte_buffer, 1, num_bytes, plac->enctmp) != num_bytes)
+		return 0 ;
+	if ((plac->pakt_info = alac_pakt_append (plac->pakt_info, num_bytes)) == NULL)
+		return 0 ;
+
+	plac->partial_block_frames = 0 ;
+
+	return 1 ;
+} /* alac_encode_block */
+
+/*============================================================================================
+** ALAC read functions.
+*/
+
+static sf_count_t
+alac_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len)
+{	ALAC_PRIVATE *plac ;
+	int			*iptr ;
+	int			k, readcount ;
+	sf_count_t	total = 0 ;
+
+	if ((plac = (ALAC_PRIVATE*) psf->codec_data) == NULL)
+		return 0 ;
+
+	while (len > 0)
+	{	if (plac->partial_block_frames >= plac->frames_this_block && alac_decode_block (psf, plac) == 0)
+			break ;
+
+		readcount = (plac->frames_this_block - plac->partial_block_frames) * plac->channels ;
+		readcount = readcount > len ? (int) len : readcount ;
+
+		iptr = plac->buffer + plac->partial_block_frames * plac->channels ;
+
+		for (k = 0 ; k < readcount ; k++)
+			ptr [total + k] = iptr [k] >> 16 ;
+
+		plac->partial_block_frames += readcount / plac->channels ;
+		total += readcount ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* alac_read_s */
+
+static sf_count_t
+alac_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len)
+{	ALAC_PRIVATE *plac ;
+	int			*iptr ;
+	int			k, readcount ;
+	sf_count_t	total = 0 ;
+
+	if ((plac = (ALAC_PRIVATE*) psf->codec_data) == NULL)
+		return 0 ;
+
+	while (len > 0)
+	{	if (plac->partial_block_frames >= plac->frames_this_block && alac_decode_block (psf, plac) == 0)
+			break ;
+
+		readcount = (plac->frames_this_block - plac->partial_block_frames) * plac->channels ;
+		readcount = readcount > len ? (int) len : readcount ;
+
+		iptr = plac->buffer + plac->partial_block_frames * plac->channels ;
+
+		for (k = 0 ; k < readcount ; k++)
+			ptr [total + k] = iptr [k] ;
+
+		plac->partial_block_frames += readcount / plac->channels ;
+		total += readcount ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* alac_read_i */
+
+static sf_count_t
+alac_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len)
+{	ALAC_PRIVATE *plac ;
+	int			*iptr ;
+	int			k, readcount ;
+	sf_count_t	total = 0 ;
+	float		normfact ;
+
+	if ((plac = (ALAC_PRIVATE*) psf->codec_data) == NULL)
+		return 0 ;
+
+	normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x80000000) : 1.0 ;
+
+	while (len > 0)
+	{	if (plac->partial_block_frames >= plac->frames_this_block && alac_decode_block (psf, plac) == 0)
+			break ;
+
+		readcount = (plac->frames_this_block - plac->partial_block_frames) * plac->channels ;
+		readcount = readcount > len ? (int) len : readcount ;
+
+		iptr = plac->buffer + plac->partial_block_frames * plac->channels ;
+
+		for (k = 0 ; k < readcount ; k++)
+			ptr [total + k] = normfact * iptr [k] ;
+
+		plac->partial_block_frames += readcount / plac->channels ;
+		total += readcount ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* alac_read_f */
+
+static sf_count_t
+alac_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len)
+{	ALAC_PRIVATE *plac ;
+	int			*iptr ;
+	int			k, readcount ;
+	sf_count_t	total = 0 ;
+	double		normfact ;
+
+	if ((plac = (ALAC_PRIVATE*) psf->codec_data) == NULL)
+		return 0 ;
+
+	normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((float) 0x80000000) : 1.0 ;
+
+	while (len > 0)
+	{	if (plac->partial_block_frames >= plac->frames_this_block && alac_decode_block (psf, plac) == 0)
+			break ;
+
+		readcount = (plac->frames_this_block - plac->partial_block_frames) * plac->channels ;
+		readcount = readcount > len ? (int) len : readcount ;
+
+		iptr = plac->buffer + plac->partial_block_frames * plac->channels ;
+
+		for (k = 0 ; k < readcount ; k++)
+			ptr [total + k] = normfact * iptr [k] ;
+
+		plac->partial_block_frames += readcount / plac->channels ;
+		total += readcount ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* alac_read_d */
+
+/*============================================================================================
+*/
+
+static sf_count_t
+alac_seek (SF_PRIVATE *psf, int mode, sf_count_t offset)
+{	ALAC_PRIVATE *plac ;
+	int			newblock, newsample ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	plac = (ALAC_PRIVATE*) psf->codec_data ;
+
+	if (psf->datalength < 0 || psf->dataoffset < 0)
+	{	psf->error = SFE_BAD_SEEK ;
+		return PSF_SEEK_ERROR ;
+		} ;
+
+	if (offset == 0)
+	{	psf_fseek (psf, psf->dataoffset, SEEK_SET) ;
+
+		plac->frames_this_block = 0 ;
+		plac->input_data_pos = psf->dataoffset ;
+		plac->pakt_info->current = 0 ;
+		return 0 ;
+		} ;
+
+	if (offset < 0 || offset > plac->pakt_info->count * plac->frames_per_block)
+	{	psf->error = SFE_BAD_SEEK ;
+		return	PSF_SEEK_ERROR ;
+		} ;
+
+	newblock	= offset / plac->frames_per_block ;
+	newsample	= offset % plac->frames_per_block ;
+
+	if (mode == SFM_READ)
+	{	plac->input_data_pos = psf->dataoffset + alac_pakt_block_offset (plac->pakt_info, newblock) ;
+
+		plac->pakt_info->current = newblock ;
+		alac_decode_block (psf, plac) ;
+		plac->partial_block_frames = newsample ;
+		}
+	else
+	{	/* What to do about write??? */
+		psf->error = SFE_BAD_SEEK ;
+		return	PSF_SEEK_ERROR ;
+		} ;
+
+	return newblock * plac->frames_per_block + newsample ;
+} /* alac_seek */
+
+/*==========================================================================================
+** ALAC Write Functions.
+*/
+
+static sf_count_t
+alac_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len)
+{	ALAC_PRIVATE *plac ;
+	int			*iptr ;
+	int			k, writecount ;
+	sf_count_t	total = 0 ;
+
+	if ((plac = (ALAC_PRIVATE*) psf->codec_data) == NULL)
+		return 0 ;
+
+	while (len > 0)
+	{	writecount = (plac->frames_per_block - plac->partial_block_frames) * plac->channels ;
+		writecount = (writecount == 0 || writecount > len) ? (int) len : writecount ;
+
+		iptr = plac->buffer + plac->partial_block_frames * plac->channels ;
+
+		for (k = 0 ; k < writecount ; k++)
+			iptr [k] = arith_shift_left (ptr [k], 16) ;
+
+		plac->partial_block_frames += writecount / plac->channels ;
+		total += writecount ;
+		len -= writecount ;
+		ptr += writecount ;
+
+		if (plac->partial_block_frames >= plac->frames_per_block)
+			alac_encode_block (plac) ;
+		} ;
+
+	return total ;
+} /* alac_write_s */
+
+static sf_count_t
+alac_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len)
+{	ALAC_PRIVATE *plac ;
+	int			*iptr ;
+	int			k, writecount ;
+	sf_count_t	total = 0 ;
+
+	if ((plac = (ALAC_PRIVATE*) psf->codec_data) == NULL)
+		return 0 ;
+
+	while (len > 0)
+	{	writecount = (plac->frames_per_block - plac->partial_block_frames) * plac->channels ;
+		writecount = (writecount == 0 || writecount > len) ? (int) len : writecount ;
+
+		iptr = plac->buffer + plac->partial_block_frames * plac->channels ;
+
+		for (k = 0 ; k < writecount ; k++)
+			iptr [k] = ptr [k] ;
+
+		plac->partial_block_frames += writecount / plac->channels ;
+		total += writecount ;
+		len -= writecount ;
+		ptr += writecount ;
+
+		if (plac->partial_block_frames >= plac->frames_per_block)
+			alac_encode_block (plac) ;
+		} ;
+
+	return total ;
+} /* alac_write_i */
+
+static sf_count_t
+alac_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len)
+{	ALAC_PRIVATE *plac ;
+	void		(*convert) (const float *, int *t, int, int) ;
+	int			*iptr ;
+	int			writecount ;
+	sf_count_t	total = 0 ;
+
+	if ((plac = (ALAC_PRIVATE*) psf->codec_data) == NULL)
+		return 0 ;
+
+	convert = (psf->add_clipping) ? psf_f2i_clip_array : psf_f2i_array ;
+
+	while (len > 0)
+	{	writecount = (plac->frames_per_block - plac->partial_block_frames) * plac->channels ;
+		writecount = (writecount == 0 || writecount > len) ? (int) len : writecount ;
+
+		iptr = plac->buffer + plac->partial_block_frames * plac->channels ;
+
+		convert (ptr, iptr, writecount, psf->norm_float) ;
+
+		plac->partial_block_frames += writecount / plac->channels ;
+		total += writecount ;
+		len -= writecount ;
+		ptr += writecount ;
+
+		if (plac->partial_block_frames >= plac->frames_per_block)
+			alac_encode_block (plac) ;
+		} ;
+
+	return total ;
+} /* alac_write_f */
+
+static sf_count_t
+alac_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len)
+{	ALAC_PRIVATE *plac ;
+	void		(*convert) (const double *, int *t, int, int) ;
+	int			*iptr ;
+	int			writecount ;
+	sf_count_t	total = 0 ;
+
+	if ((plac = (ALAC_PRIVATE*) psf->codec_data) == NULL)
+		return 0 ;
+
+	convert = (psf->add_clipping) ? psf_d2i_clip_array : psf_d2i_array ;
+
+	while (len > 0)
+	{	writecount = (plac->frames_per_block - plac->partial_block_frames) * plac->channels ;
+		writecount = (writecount == 0 || writecount > len) ? (int) len : writecount ;
+
+		iptr = plac->buffer + plac->partial_block_frames * plac->channels ;
+
+		convert (ptr, iptr, writecount, psf->norm_float) ;
+
+		plac->partial_block_frames += writecount / plac->channels ;
+		total += writecount ;
+		len -= writecount ;
+		ptr += writecount ;
+
+		if (plac->partial_block_frames >= plac->frames_per_block)
+			alac_encode_block (plac) ;
+		} ;
+
+	return total ;
+} /* alac_write_d */
+
+/*==============================================================================
+** PAKT_INFO handling.
+*/
+
+static PAKT_INFO *
+alac_pakt_alloc (uint32_t initial_count)
+{	PAKT_INFO * info ;
+
+	if ((info = calloc (1, sizeof (PAKT_INFO) + initial_count * sizeof (info->packet_size [0]))) == NULL)
+		return NULL ;
+
+	info->allocated = initial_count ;
+	info->current = 0 ;
+	info->count = 0 ;
+
+	return info ;
+} /* alac_pakt_alloc */
+
+static PAKT_INFO *
+alac_pakt_append (PAKT_INFO * info, uint32_t value)
+{
+	if (info->count >= info->allocated)
+	{	PAKT_INFO * temp ;
+		uint32_t newcount = info->allocated + info->allocated / 2 ;
+
+		if ((temp = realloc (info, sizeof (PAKT_INFO) + newcount * sizeof (info->packet_size [0]))) == NULL)
+			return NULL ;
+
+		info = temp ;
+		info->allocated = newcount ;
+		} ;
+
+	info->packet_size [info->count++] = value ;
+	return info ;
+} /* alac_pakt_append */
+
+static PAKT_INFO *
+alac_pakt_read_decode (SF_PRIVATE * psf, uint32_t UNUSED (pakt_offset))
+{	SF_CHUNK_INFO chunk_info ;
+	PAKT_INFO * info = NULL ;
+	uint8_t *pakt_data = NULL ;
+	uint32_t bcount, value = 1, pakt_size ;
+	SF_CHUNK_ITERATOR * chunk_iterator ;
+
+
+	memset (&chunk_info, 0, sizeof (chunk_info)) ;
+	snprintf (chunk_info.id, sizeof (chunk_info.id), "pakt") ;
+	chunk_info.id_size = 4 ;
+
+	if ((chunk_iterator = psf_get_chunk_iterator (psf, chunk_info.id)) == NULL)
+	{	psf_log_printf (psf, "%s : no chunk iterator found\n", __func__) ;
+		free (chunk_info.data) ;
+		chunk_info.data = NULL ;
+		return NULL ;
+		} ;
+
+	psf->get_chunk_size (psf, chunk_iterator, &chunk_info) ;
+
+	pakt_size = chunk_info.datalen ;
+	chunk_info.data = pakt_data = malloc (pakt_size + 5) ;
+	if (!chunk_info.data)
+		return NULL ;
+
+	if ((bcount = psf->get_chunk_data (psf, chunk_iterator, &chunk_info)) != SF_ERR_NO_ERROR)
+	{	while (chunk_iterator)
+			chunk_iterator = psf->next_chunk_iterator (psf, chunk_iterator) ;
+		free (chunk_info.data) ;
+		chunk_info.data = NULL ;
+		return NULL ;
+		} ;
+
+	while (chunk_iterator)
+		chunk_iterator = psf->next_chunk_iterator (psf, chunk_iterator) ;
+
+	info = alac_pakt_alloc (pakt_size / 4) ;
+
+	/* Start at 24 bytes in, skipping over the 'pakt' chunks header. */
+	for (bcount = 24 ; bcount < pakt_size && value != 0 ; )
+	{	uint8_t byte ;
+		int32_t count = 0 ;
+
+		value = 0 ;
+		do
+		{	byte = pakt_data [bcount + count] ;
+			value = (value << 7) + (byte & 0x7F) ;
+
+			count ++ ;
+			if (count > 5 || bcount + count > pakt_size)
+			{	printf ("%s %d : Ooops! count %" PRIi32 "    bcount %" PRIu32 "\n", __func__, __LINE__, count, bcount) ;
+				value = 0 ;
+				break ;
+				} ;
+			}
+			while (byte & 0x80) ;
+
+		bcount += count ;
+
+		if ((info = alac_pakt_append (info, value)) == NULL)
+			goto FreeExit ;
+		} ;
+
+	free (pakt_data) ;
+
+	return info ;
+
+FreeExit :
+	free (pakt_data) ;
+	free (info) ;
+	return NULL ;
+} /* alac_pakt_read_decode */
+
+static uint8_t *
+alac_pakt_encode (const SF_PRIVATE *psf, uint32_t * pakt_size_out)
+{	const ALAC_PRIVATE *plac ;
+	const PAKT_INFO *info ;
+	uint8_t	*data ;
+	uint32_t k, allocated, pakt_size ;
+
+	plac = psf->codec_data ;
+	info = plac->pakt_info ;
+
+	allocated = 100 + 2 * info->count ;
+	if ((data = calloc (1, allocated)) == NULL)
+		return NULL ;
+
+	psf_put_be64 (data, 0, info->count) ;
+	psf_put_be64 (data, 8, psf->sf.frames) ;
+	psf_put_be32 (data, 20, kALACDefaultFramesPerPacket - plac->partial_block_frames) ;
+
+	/* Real 'pakt' data starts after 24 byte header. */
+	pakt_size = 24 ;
+
+	for (k = 0 ; k < info->count ; k++)
+	{	int32_t value = info->packet_size [k] ;
+
+		if ((value & 0x7f) == value)
+		{	data [pakt_size++] = value ;
+			continue ;
+			} ;
+
+		if ((value & 0x3fff) == value)
+		{	data [pakt_size++] = (value >> 7) | 0x80 ;
+			data [pakt_size++] = value & 0x7f ;
+			continue ;
+			} ;
+
+		if ((value & 0x1fffff) == value)
+		{	data [pakt_size++] = (value >> 14) | 0x80 ;
+			data [pakt_size++] = ((value >> 7) & 0x7f) | 0x80 ;
+			data [pakt_size++] = value & 0x7f ;
+			continue ;
+		} ;
+
+		if ((value & 0x0fffffff) == value)
+		{	data [pakt_size++] = (value >> 21) | 0x80 ;
+			data [pakt_size++] = ((value >> 14) & 0x7f) | 0x80 ;
+			data [pakt_size++] = ((value >> 7) & 0x7f) | 0x80 ;
+			data [pakt_size++] = value & 0x7f ;
+			continue ;
+			} ;
+
+		*pakt_size_out = 0 ;
+		free (data) ;
+		return NULL ;
+		} ;
+
+	*pakt_size_out = pakt_size ;
+	return data ;
+} /* alac_pakt_encode */
+
+static sf_count_t
+alac_pakt_block_offset (const PAKT_INFO *info, uint32_t block)
+{	sf_count_t offset = 0 ;
+	uint32_t k ;
+
+	for (k = 0 ; k < block ; k++)
+		offset += info->packet_size [k] ;
+
+	return offset ;
+} /* alac_pakt_block_offset */
+
+static uint32_t
+alac_kuki_read (SF_PRIVATE * psf, uint32_t kuki_offset, uint8_t * kuki, size_t kuki_maxlen)
+{	uint32_t marker ;
+	uint64_t kuki_size ;
+
+	if (psf_fseek (psf, kuki_offset, SEEK_SET) != kuki_offset)
+		return 0 ;
+
+	psf_fread (&marker, 1, sizeof (marker), psf) ;
+	if (marker != MAKE_MARKER ('k', 'u', 'k', 'i'))
+		return 0 ;
+
+	psf_fread (&kuki_size, 1, sizeof (kuki_size), psf) ;
+	kuki_size = BE2H_64 (kuki_size) ;
+
+	if (kuki_size == 0 || kuki_size > kuki_maxlen)
+	{	psf_log_printf (psf, "%s : Bad size (%D) of 'kuki' chunk.\n", __func__, kuki_size) ;
+		return 0 ;
+		} ;
+
+	psf_fread (kuki, 1, kuki_size, psf) ;
+
+	return kuki_size ;
+} /* alac_kuki_read */
+
+#define CASE_NAME(x)	case x : return #x ; break ;
+
+static const char *
+alac_error_string (int error)
+{	static char errstr [128] ;
+	switch (error)
+	{	CASE_NAME (kALAC_UnimplementedError) ;
+		CASE_NAME (kALAC_FileNotFoundError) ;
+		CASE_NAME (kALAC_ParamError) ;
+		CASE_NAME (kALAC_MemFullError) ;
+		CASE_NAME (fALAC_FrameLengthError) ;
+
+		/* Added for libsndfile */
+		CASE_NAME (kALAC_BadBitWidth) ;
+		CASE_NAME (kALAC_IncompatibleVersion) ;
+		CASE_NAME (kALAC_BadSpecificConfigSize) ;
+		CASE_NAME (kALAC_ZeroChannelCount) ;
+		CASE_NAME (kALAC_NumSamplesTooBig) ;
+		CASE_NAME (kALAC_UnsupportedElement) ;
+		default :
+			break ;
+		} ;
+
+	snprintf (errstr, sizeof (errstr), "Unknown error %d", error) ;
+	return errstr ;
+} /* alac_error_string */
+
diff --git a/src/alaw.c b/src/alaw.c
new file mode 100644
index 0000000..4be642e
--- /dev/null
+++ b/src/alaw.c
@@ -0,0 +1,553 @@
+/*
+** Copyright (C) 1999-2013 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<math.h>
+#include	<limits.h>
+
+#include	"sndfile.h"
+#include	"common.h"
+
+static sf_count_t alaw_read_alaw2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
+static sf_count_t alaw_read_alaw2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
+static sf_count_t alaw_read_alaw2f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
+static sf_count_t alaw_read_alaw2d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
+
+static sf_count_t alaw_write_s2alaw (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
+static sf_count_t alaw_write_i2alaw (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
+static sf_count_t alaw_write_f2alaw (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
+static sf_count_t alaw_write_d2alaw (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
+
+static void alaw2s_array (unsigned char *buffer, int count, short *ptr) ;
+static void alaw2i_array (unsigned char *buffer, int count, int *ptr) ;
+static void alaw2f_array (unsigned char *buffer, int count, float *ptr, float normfact) ;
+static void alaw2d_array (unsigned char *buffer, int count, double *ptr, double normfact) ;
+
+static void s2alaw_array (const short *buffer, int count, unsigned char *ptr) ;
+static void i2alaw_array (const int *buffer, int count, unsigned char *ptr) ;
+static void f2alaw_array (const float *buffer, int count, unsigned char *ptr, float normfact) ;
+static void d2alaw_array (const double *buffer, int count, unsigned char *ptr, double normfact) ;
+
+
+int
+alaw_init (SF_PRIVATE *psf)
+{
+	if (psf->file.mode == SFM_READ || psf->file.mode == SFM_RDWR)
+	{	psf->read_short		= alaw_read_alaw2s ;
+		psf->read_int		= alaw_read_alaw2i ;
+		psf->read_float		= alaw_read_alaw2f ;
+		psf->read_double	= alaw_read_alaw2d ;
+		} ;
+
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	psf->write_short	= alaw_write_s2alaw ;
+		psf->write_int		= alaw_write_i2alaw ;
+		psf->write_float	= alaw_write_f2alaw ;
+		psf->write_double	= alaw_write_d2alaw ;
+		} ;
+
+	psf->bytewidth = 1 ;
+	psf->blockwidth = psf->sf.channels ;
+
+	if (psf->filelength > psf->dataoffset)
+		psf->datalength = (psf->dataend) ? psf->dataend - psf->dataoffset : psf->filelength - psf->dataoffset ;
+	else
+		psf->datalength = 0 ;
+
+	psf->sf.frames = psf->blockwidth > 0 ? psf->datalength / psf->blockwidth : 0 ;
+
+	return 0 ;
+} /* alaw_init */
+
+/*==============================================================================
+ *	Private static functions and data.
+ */
+
+static
+short alaw_decode [256] =
+{	-5504,	-5248,	-6016,	-5760,	-4480,	-4224,	-4992,	-4736,
+	-7552,	-7296,	-8064,	-7808,	-6528,	-6272,	-7040,	-6784,
+	-2752,	-2624,	-3008,	-2880,	-2240,	-2112,	-2496,	-2368,
+	-3776,	-3648,	-4032,	-3904,	-3264,	-3136,	-3520,	-3392,
+	-22016,	-20992,	-24064,	-23040,	-17920,	-16896,	-19968,	-18944,
+	-30208,	-29184,	-32256,	-31232,	-26112,	-25088,	-28160,	-27136,
+	-11008,	-10496,	-12032,	-11520,	-8960,	-8448,	-9984,	-9472,
+	-15104,	-14592,	-16128,	-15616,	-13056,	-12544,	-14080,	-13568,
+	-344,	-328,	-376,	-360,	-280,	-264,	-312,	-296,
+	-472,	-456,	-504,	-488,	-408,	-392,	-440,	-424,
+	-88,	-72,	-120,	-104,	-24,	-8,		-56,	-40,
+	-216,	-200,	-248,	-232,	-152,	-136,	-184,	-168,
+	-1376,	-1312,	-1504,	-1440,	-1120,	-1056,	-1248,	-1184,
+	-1888,	-1824,	-2016,	-1952,	-1632,	-1568,	-1760,	-1696,
+	-688,	-656,	-752,	-720,	-560,	-528,	-624,	-592,
+	-944,	-912,	-1008,	-976,	-816,	-784,	-880,	-848,
+	5504,	5248,	6016,	5760,	4480,	4224,	4992,	4736,
+	7552,	7296,	8064,	7808,	6528,	6272,	7040,	6784,
+	2752,	2624,	3008,	2880,	2240,	2112,	2496,	2368,
+	3776,	3648,	4032,	3904,	3264,	3136,	3520,	3392,
+	22016,	20992,	24064,	23040,	17920,	16896,	19968,	18944,
+	30208,	29184,	32256,	31232,	26112,	25088,	28160,	27136,
+	11008,	10496,	12032,	11520,	8960,	8448,	9984,	9472,
+	15104,	14592,	16128,	15616,	13056,	12544,	14080,	13568,
+	344,	328,	376,	360,	280,	264,	312,	296,
+	472,	456,	504,	488,	408,	392,	440,	424,
+	88,		72,		120,	104,	24,		8,		56,		40,
+	216,	200,	248,	232,	152,	136,	184,	168,
+	1376,	1312,	1504,	1440,	1120,	1056,	1248,	1184,
+	1888,	1824,	2016,	1952,	1632,	1568,	1760,	1696,
+	688,	656,	752,	720,	560,	528,	624,	592,
+	944,	912,	1008,	976,	816,	784,	880,	848
+} ; /* alaw_decode */
+
+static
+unsigned char alaw_encode [2048 + 1] =
+{	0xd5, 0xd4, 0xd7, 0xd6, 0xd1, 0xd0, 0xd3, 0xd2, 0xdd, 0xdc, 0xdf, 0xde,
+	0xd9, 0xd8, 0xdb, 0xda, 0xc5, 0xc4, 0xc7, 0xc6, 0xc1, 0xc0, 0xc3, 0xc2,
+	0xcd, 0xcc, 0xcf, 0xce, 0xc9, 0xc8, 0xcb, 0xca, 0xf5, 0xf5, 0xf4, 0xf4,
+	0xf7, 0xf7, 0xf6, 0xf6, 0xf1, 0xf1, 0xf0, 0xf0, 0xf3, 0xf3, 0xf2, 0xf2,
+	0xfd, 0xfd, 0xfc, 0xfc, 0xff, 0xff, 0xfe, 0xfe, 0xf9, 0xf9, 0xf8, 0xf8,
+	0xfb, 0xfb, 0xfa, 0xfa, 0xe5, 0xe5, 0xe5, 0xe5, 0xe4, 0xe4, 0xe4, 0xe4,
+	0xe7, 0xe7, 0xe7, 0xe7, 0xe6, 0xe6, 0xe6, 0xe6, 0xe1, 0xe1, 0xe1, 0xe1,
+	0xe0, 0xe0, 0xe0, 0xe0, 0xe3, 0xe3, 0xe3, 0xe3, 0xe2, 0xe2, 0xe2, 0xe2,
+	0xed, 0xed, 0xed, 0xed, 0xec, 0xec, 0xec, 0xec, 0xef, 0xef, 0xef, 0xef,
+	0xee, 0xee, 0xee, 0xee, 0xe9, 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, 0xe8, 0xe8,
+	0xeb, 0xeb, 0xeb, 0xeb, 0xea, 0xea, 0xea, 0xea, 0x95, 0x95, 0x95, 0x95,
+	0x95, 0x95, 0x95, 0x95, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+	0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x96, 0x96, 0x96, 0x96,
+	0x96, 0x96, 0x96, 0x96, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+	0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x93, 0x93, 0x93, 0x93,
+	0x93, 0x93, 0x93, 0x93, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+	0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9c, 0x9c, 0x9c, 0x9c,
+	0x9c, 0x9c, 0x9c, 0x9c, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+	0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x99, 0x99, 0x99, 0x99,
+	0x99, 0x99, 0x99, 0x99, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+	0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9a, 0x9a, 0x9a, 0x9a,
+	0x9a, 0x9a, 0x9a, 0x9a, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+	0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x84, 0x84, 0x84, 0x84,
+	0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+	0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+	0x87, 0x87, 0x87, 0x87, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+	0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x81, 0x81, 0x81, 0x81,
+	0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+	0x80, 0x80, 0x80, 0x80, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x82, 0x82, 0x82, 0x82,
+	0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+	0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+	0x8d, 0x8d, 0x8d, 0x8d, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+	0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8f, 0x8f, 0x8f, 0x8f,
+	0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+	0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+	0x8e, 0x8e, 0x8e, 0x8e, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+	0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x88, 0x88, 0x88, 0x88,
+	0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+	0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+	0x8b, 0x8b, 0x8b, 0x8b, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+	0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0xb5, 0xb5, 0xb5, 0xb5,
+	0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+	0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+	0xb5, 0xb5, 0xb5, 0xb5, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+	0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+	0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+	0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+	0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+	0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb6, 0xb6, 0xb6, 0xb6,
+	0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+	0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+	0xb6, 0xb6, 0xb6, 0xb6, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+	0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+	0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+	0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+	0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+	0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb3, 0xb3, 0xb3, 0xb3,
+	0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+	0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+	0xb3, 0xb3, 0xb3, 0xb3, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+	0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+	0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+	0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+	0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+	0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbc, 0xbc, 0xbc, 0xbc,
+	0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+	0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+	0xbc, 0xbc, 0xbc, 0xbc, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+	0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+	0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+	0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+	0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+	0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xb9, 0xb9, 0xb9, 0xb9,
+	0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+	0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+	0xb9, 0xb9, 0xb9, 0xb9, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+	0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+	0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+	0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+	0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+	0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xba, 0xba, 0xba, 0xba,
+	0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+	0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+	0xba, 0xba, 0xba, 0xba, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+	0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+	0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+	0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+	0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+	0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa4, 0xa4, 0xa4, 0xa4,
+	0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+	0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+	0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+	0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+	0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+	0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+	0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+	0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+	0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+	0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+	0xa7, 0xa7, 0xa7, 0xa7, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+	0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+	0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+	0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+	0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+	0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa1, 0xa1, 0xa1, 0xa1,
+	0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+	0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+	0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+	0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+	0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+	0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+	0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+	0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+	0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+	0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+	0xa0, 0xa0, 0xa0, 0xa0, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+	0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+	0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+	0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+	0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+	0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, 0xa2, 0xa2, 0xa2,
+	0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+	0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+	0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+	0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+	0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+	0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+	0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+	0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+	0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+	0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+	0xad, 0xad, 0xad, 0xad, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+	0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+	0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+	0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+	0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+	0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xaf, 0xaf, 0xaf, 0xaf,
+	0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+	0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+	0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+	0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+	0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+	0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+	0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+	0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+	0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+	0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+	0xae, 0xae, 0xae, 0xae, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+	0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+	0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+	0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+	0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+	0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa8, 0xa8, 0xa8, 0xa8,
+	0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+	0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+	0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+	0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+	0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+	0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+	0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+	0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+	0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+	0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+	0xab, 0xab, 0xab, 0xab, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+	0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+	0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+	0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+	0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+	0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa
+} ; /* alaw_encode */
+
+static inline void
+alaw2s_array (unsigned char *buffer, int count, short *ptr)
+{	for (int i = 0 ; i < count ; i++)
+		ptr [i] = alaw_decode [(int) buffer [i]] ;
+} /* alaw2s_array */
+
+static inline void
+alaw2i_array (unsigned char *buffer, int count, int *ptr)
+{	for (int i = 0 ; i < count ; i++)
+		ptr [i] = ((uint32_t) alaw_decode [(int) buffer [i]]) << 16 ;
+} /* alaw2i_array */
+
+static inline void
+alaw2f_array (unsigned char *buffer, int count, float *ptr, float normfact)
+{	for (int i = 0 ; i < count ; i++)
+		ptr [i] = normfact * alaw_decode [(int) buffer [i]] ;
+} /* alaw2f_array */
+
+static inline void
+alaw2d_array (unsigned char *buffer, int count, double *ptr, double normfact)
+{	for (int i = 0 ; i < count ; i++)
+		ptr [i] = normfact * alaw_decode [(int) buffer [i]] ;
+} /* alaw2d_array */
+
+static inline void
+s2alaw_array (const short *ptr, int count, unsigned char *buffer)
+{	for (int i = 0 ; i < count ; i++)
+	{	if (ptr [i] >= 0)
+			buffer [i] = alaw_encode [ptr [i] / 16] ;
+		else
+			buffer [i] = 0x7F & alaw_encode [ptr [i] / -16] ;
+		} ;
+} /* s2alaw_array */
+
+static inline void
+i2alaw_array (const int *ptr, int count, unsigned char *buffer)
+{	for (int i = 0 ; i < count ; i++)
+	{	if (ptr [i] == INT_MIN)
+			buffer [i] = alaw_encode [INT_MAX >> (16 + 4)] ;
+		else if (ptr [i] >= 0)
+			buffer [i] = alaw_encode [ptr [i] >> (16 + 4)] ;
+		else
+			buffer [i] = 0x7F & alaw_encode [- ptr [i] >> (16 + 4)] ;
+		} ;
+} /* i2alaw_array */
+
+static inline void
+f2alaw_array (const float *ptr, int count, unsigned char *buffer, float normfact)
+{	for (int i = 0 ; i < count ; i++)
+	{	if (ptr [i] >= 0)
+			buffer [i] = alaw_encode [psf_lrintf (normfact * ptr [i])] ;
+		else
+			buffer [i] = 0x7F & alaw_encode [- psf_lrintf (normfact * ptr [i])] ;
+		} ;
+} /* f2alaw_array */
+
+static inline void
+d2alaw_array (const double *ptr, int count, unsigned char *buffer, double normfact)
+{	for (int i = 0 ; i < count ; i++)
+	{	if (!isfinite (ptr [i]))
+			buffer [i] = 0 ;
+		else if (ptr [i] >= 0)
+			buffer [i] = alaw_encode [psf_lrint (normfact * ptr [i])] ;
+		else
+			buffer [i] = 0x7F & alaw_encode [- psf_lrint (normfact * ptr [i])] ;
+		} ;
+} /* d2alaw_array */
+
+/*==============================================================================
+*/
+
+static sf_count_t
+alaw_read_alaw2s (SF_PRIVATE *psf, short *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ucbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.ucbuf, 1, bufferlen, psf) ;
+		alaw2s_array (ubuf.ucbuf, readcount, ptr + total) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* alaw_read_alaw2s */
+
+static sf_count_t
+alaw_read_alaw2i (SF_PRIVATE *psf, int *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ucbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.ucbuf, 1, bufferlen, psf) ;
+		alaw2i_array (ubuf.ucbuf, readcount, ptr + total) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* alaw_read_alaw2i */
+
+static sf_count_t
+alaw_read_alaw2f (SF_PRIVATE *psf, float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+	float	normfact ;
+
+	normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x8000) : 1.0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ucbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.ucbuf, 1, bufferlen, psf) ;
+		alaw2f_array (ubuf.ucbuf, readcount, ptr + total, normfact) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* alaw_read_alaw2f */
+
+static sf_count_t
+alaw_read_alaw2d (SF_PRIVATE *psf, double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+	double	normfact ;
+
+	normfact = (psf->norm_double) ? 1.0 / ((double) 0x8000) : 1.0 ;
+	bufferlen = ARRAY_LEN (ubuf.ucbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.ucbuf, 1, bufferlen, psf) ;
+		alaw2d_array (ubuf.ucbuf, readcount, ptr + total, normfact) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* alaw_read_alaw2d */
+
+/*=============================================================================================
+*/
+
+static sf_count_t
+alaw_write_s2alaw	(SF_PRIVATE *psf, const short *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ucbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		s2alaw_array (ptr + total, bufferlen, ubuf.ucbuf) ;
+		writecount = (int) psf_fwrite (ubuf.ucbuf, 1, bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* alaw_write_s2alaw */
+
+static sf_count_t
+alaw_write_i2alaw	(SF_PRIVATE *psf, const int *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ucbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		i2alaw_array (ptr + total, bufferlen, ubuf.ucbuf) ;
+		writecount = (int) psf_fwrite (ubuf.ucbuf, 1, bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* alaw_write_i2alaw */
+
+static sf_count_t
+alaw_write_f2alaw	(SF_PRIVATE *psf, const float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+	float	normfact ;
+
+	normfact = (psf->norm_float == SF_TRUE) ? (1.0 * 0x7FFF) / 16.0 : 1.0 / 16 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ucbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		f2alaw_array (ptr + total, bufferlen, ubuf.ucbuf, normfact) ;
+		writecount = (int) psf_fwrite (ubuf.ucbuf, 1, bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* alaw_write_f2alaw */
+
+static sf_count_t
+alaw_write_d2alaw	(SF_PRIVATE *psf, const double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+	double	normfact ;
+
+	normfact = (psf->norm_double) ? (1.0 * 0x7FFF) / 16.0 : 1.0 / 16.0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ucbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		d2alaw_array (ptr + total, bufferlen, ubuf.ucbuf, normfact) ;
+		writecount = (int) psf_fwrite (ubuf.ucbuf, 1, bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* alaw_write_d2alaw */
+
diff --git a/src/au.c b/src/au.c
new file mode 100644
index 0000000..62bd691
--- /dev/null
+++ b/src/au.c
@@ -0,0 +1,454 @@
+/*
+** Copyright (C) 1999-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<fcntl.h>
+#include	<string.h>
+#include	<ctype.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+/*------------------------------------------------------------------------------
+** Macros to handle big/little endian issues.
+*/
+
+#define DOTSND_MARKER	(MAKE_MARKER ('.', 's', 'n', 'd'))
+#define DNSDOT_MARKER	(MAKE_MARKER ('d', 'n', 's', '.'))
+
+#define AU_DATA_OFFSET	24
+
+/*------------------------------------------------------------------------------
+** Known AU file encoding types.
+*/
+
+enum
+{	AU_ENCODING_ULAW_8					= 1,	/* 8-bit u-law samples */
+	AU_ENCODING_PCM_8					= 2,	/* 8-bit linear samples */
+	AU_ENCODING_PCM_16					= 3,	/* 16-bit linear samples */
+	AU_ENCODING_PCM_24					= 4,	/* 24-bit linear samples */
+	AU_ENCODING_PCM_32					= 5,	/* 32-bit linear samples */
+
+	AU_ENCODING_FLOAT					= 6,	/* floating-point samples */
+	AU_ENCODING_DOUBLE					= 7,	/* double-precision float samples */
+	AU_ENCODING_INDIRECT				= 8,	/* fragmented sampled data */
+	AU_ENCODING_NESTED					= 9,	/* ? */
+	AU_ENCODING_DSP_CORE				= 10,	/* DSP program */
+	AU_ENCODING_DSP_DATA_8				= 11,	/* 8-bit fixed-point samples */
+	AU_ENCODING_DSP_DATA_16				= 12,	/* 16-bit fixed-point samples */
+	AU_ENCODING_DSP_DATA_24				= 13,	/* 24-bit fixed-point samples */
+	AU_ENCODING_DSP_DATA_32				= 14,	/* 32-bit fixed-point samples */
+
+	AU_ENCODING_DISPLAY					= 16,	/* non-audio display data */
+	AU_ENCODING_MULAW_SQUELCH			= 17,	/* ? */
+	AU_ENCODING_EMPHASIZED				= 18,	/* 16-bit linear with emphasis */
+	AU_ENCODING_NEXT					= 19,	/* 16-bit linear with compression (NEXT) */
+	AU_ENCODING_COMPRESSED_EMPHASIZED	= 20,	/* A combination of the two above */
+	AU_ENCODING_DSP_COMMANDS			= 21,	/* Music Kit DSP commands */
+	AU_ENCODING_DSP_COMMANDS_SAMPLES	= 22,	/* ? */
+
+	AU_ENCODING_ADPCM_G721_32			= 23,	/* G721 32 kbs ADPCM - 4 bits per sample. */
+	AU_ENCODING_ADPCM_G722				= 24,	/* G722 64 kbs ADPCM */
+	AU_ENCODING_ADPCM_G723_24			= 25,	/* G723 24 kbs ADPCM - 3 bits per sample. */
+	AU_ENCODING_ADPCM_G723_40			= 26,	/* G723 40 kbs ADPCM - 5 bits per sample. */
+
+	AU_ENCODING_ALAW_8					= 27
+} ;
+
+/*------------------------------------------------------------------------------
+** Typedefs.
+*/
+
+typedef	struct
+{	int		dataoffset ;
+	int		datasize ;
+	int		encoding ;
+	int		samplerate ;
+	int		channels ;
+} AU_FMT ;
+
+
+/*------------------------------------------------------------------------------
+** Private static functions.
+*/
+
+static	int		au_close		(SF_PRIVATE *psf) ;
+
+static	int 	au_format_to_encoding	(int format) ;
+
+static int		au_write_header (SF_PRIVATE *psf, int calc_length) ;
+static int		au_read_header (SF_PRIVATE *psf) ;
+
+/*------------------------------------------------------------------------------
+** Public function.
+*/
+
+int
+au_open	(SF_PRIVATE *psf)
+{	int		subformat ;
+	int		error = 0 ;
+
+	if (psf->file.mode == SFM_READ || (psf->file.mode == SFM_RDWR && psf->filelength > 0))
+	{	if ((error = au_read_header (psf)))
+			return error ;
+		} ;
+
+	if ((SF_CONTAINER (psf->sf.format)) != SF_FORMAT_AU)
+		return	SFE_BAD_OPEN_FORMAT ;
+
+	subformat = SF_CODEC (psf->sf.format) ;
+
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	psf->endian = SF_ENDIAN (psf->sf.format) ;
+		if (CPU_IS_LITTLE_ENDIAN && psf->endian == SF_ENDIAN_CPU)
+			psf->endian = SF_ENDIAN_LITTLE ;
+		else if (psf->endian != SF_ENDIAN_LITTLE)
+			psf->endian = SF_ENDIAN_BIG ;
+
+		if (au_write_header (psf, SF_FALSE))
+			return psf->error ;
+
+		psf->write_header = au_write_header ;
+		} ;
+
+	psf->container_close = au_close ;
+
+	psf->blockwidth = psf->bytewidth * psf->sf.channels ;
+
+	switch (subformat)
+	{	case SF_FORMAT_ULAW :	/* 8-bit Ulaw encoding. */
+				ulaw_init (psf) ;
+				break ;
+
+		case SF_FORMAT_PCM_S8 :	/* 8-bit linear PCM. */
+				error = pcm_init (psf) ;
+				break ;
+
+		case SF_FORMAT_PCM_16 :	/* 16-bit linear PCM. */
+		case SF_FORMAT_PCM_24 :	/* 24-bit linear PCM */
+		case SF_FORMAT_PCM_32 :	/* 32-bit linear PCM. */
+				error = pcm_init (psf) ;
+				break ;
+
+		case SF_FORMAT_ALAW :	/* 8-bit Alaw encoding. */
+				alaw_init (psf) ;
+				break ;
+
+		/* Lite remove start */
+		case SF_FORMAT_FLOAT :	/* 32-bit floats. */
+				error = float32_init (psf) ;
+				break ;
+
+		case SF_FORMAT_DOUBLE :	/* 64-bit double precision floats. */
+				error = double64_init (psf) ;
+				break ;
+
+		case SF_FORMAT_G721_32 :
+				error = g72x_init (psf) ;
+				psf->sf.seekable = SF_FALSE ;
+				break ;
+
+		case SF_FORMAT_G723_24 :
+				error = g72x_init (psf) ;
+				psf->sf.seekable = SF_FALSE ;
+				break ;
+
+		case SF_FORMAT_G723_40 :
+				error = g72x_init (psf) ;
+				psf->sf.seekable = SF_FALSE ;
+				break ;
+		/* Lite remove end */
+
+		default :	break ;
+		} ;
+
+	return error ;
+} /* au_open */
+
+/*------------------------------------------------------------------------------
+*/
+
+static int
+au_close	(SF_PRIVATE *psf)
+{
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+		au_write_header (psf, SF_TRUE) ;
+
+	return 0 ;
+} /* au_close */
+
+static int
+au_write_header (SF_PRIVATE *psf, int calc_length)
+{	sf_count_t	current ;
+	int			encoding, datalength ;
+
+	if (psf->pipeoffset > 0)
+		return 0 ;
+
+	current = psf_ftell (psf) ;
+
+	if (calc_length)
+	{	psf->filelength = psf_get_filelen (psf) ;
+
+		psf->datalength = psf->filelength - psf->dataoffset ;
+		if (psf->dataend)
+			psf->datalength -= psf->filelength - psf->dataend ;
+		} ;
+
+	encoding = au_format_to_encoding (SF_CODEC (psf->sf.format)) ;
+	if (! encoding)
+		return (psf->error = SFE_BAD_OPEN_FORMAT) ;
+
+	/* Reset the current header length to zero. */
+	psf->header.ptr [0] = 0 ;
+	psf->header.indx = 0 ;
+
+	/*
+	** Only attempt to seek if we are not writng to a pipe. If we are
+	** writing to a pipe we shouldn't be here anyway.
+	*/
+	if (psf->is_pipe == SF_FALSE)
+		psf_fseek (psf, 0, SEEK_SET) ;
+
+	/*
+	**	AU format files allow a datalength value of -1 if the datalength
+	**	is not know at the time the header is written.
+	**	Also use this value of -1 if the datalength > 2 gigabytes.
+	*/
+	if (psf->datalength	< 0 || psf->datalength > 0x7FFFFFFF)
+		datalength = -1 ;
+	else
+		datalength = (int) (psf->datalength & 0x7FFFFFFF) ;
+
+	if (psf->endian == SF_ENDIAN_BIG)
+	{	psf_binheader_writef (psf, "Em4", BHWm (DOTSND_MARKER), BHW4 (AU_DATA_OFFSET)) ;
+		psf_binheader_writef (psf, "E4444", BHW4 (datalength), BHW4 (encoding), BHW4 (psf->sf.samplerate), BHW4 (psf->sf.channels)) ;
+		}
+	else if (psf->endian == SF_ENDIAN_LITTLE)
+	{	psf_binheader_writef (psf, "em4", BHWm (DNSDOT_MARKER), BHW4 (AU_DATA_OFFSET)) ;
+		psf_binheader_writef (psf, "e4444", BHW4 (datalength), BHW4 (encoding), BHW4 (psf->sf.samplerate), BHW4 (psf->sf.channels)) ;
+		}
+	else
+		return (psf->error = SFE_BAD_OPEN_FORMAT) ;
+
+	/* Header construction complete so write it out. */
+	psf_fwrite (psf->header.ptr, psf->header.indx, 1, psf) ;
+
+	if (psf->error)
+		return psf->error ;
+
+	psf->dataoffset = psf->header.indx ;
+
+	if (current > 0)
+		psf_fseek (psf, current, SEEK_SET) ;
+
+	return psf->error ;
+} /* au_write_header */
+
+static int
+au_format_to_encoding (int format)
+{
+	switch (format)
+	{	case SF_FORMAT_PCM_S8 : 	return AU_ENCODING_PCM_8 ;
+		case SF_FORMAT_PCM_16 :		return AU_ENCODING_PCM_16 ;
+		case SF_FORMAT_PCM_24 : 	return AU_ENCODING_PCM_24 ;
+		case SF_FORMAT_PCM_32 : 	return AU_ENCODING_PCM_32 ;
+
+		case SF_FORMAT_FLOAT :		return AU_ENCODING_FLOAT ;
+		case SF_FORMAT_DOUBLE :		return AU_ENCODING_DOUBLE ;
+
+		case SF_FORMAT_ULAW :		return AU_ENCODING_ULAW_8 ;
+		case SF_FORMAT_ALAW :		return AU_ENCODING_ALAW_8 ;
+
+		case SF_FORMAT_G721_32 : 	return AU_ENCODING_ADPCM_G721_32 ;
+		case SF_FORMAT_G723_24 :	return AU_ENCODING_ADPCM_G723_24 ;
+		case SF_FORMAT_G723_40 :	return AU_ENCODING_ADPCM_G723_40 ;
+
+		default : break ;
+		} ;
+	return 0 ;
+} /* au_format_to_encoding */
+
+static int
+au_read_header (SF_PRIVATE *psf)
+{	AU_FMT	au_fmt ;
+	int		marker, dword ;
+
+	memset (&au_fmt, 0, sizeof (au_fmt)) ;
+	psf_binheader_readf (psf, "pm", 0, &marker) ;
+	psf_log_printf (psf, "%M\n", marker) ;
+
+	if (marker == DOTSND_MARKER)
+	{	psf->endian = SF_ENDIAN_BIG ;
+
+		psf_binheader_readf (psf, "E44444", &(au_fmt.dataoffset), &(au_fmt.datasize),
+					&(au_fmt.encoding), &(au_fmt.samplerate), &(au_fmt.channels)) ;
+		}
+	else if (marker == DNSDOT_MARKER)
+	{	psf->endian = SF_ENDIAN_LITTLE ;
+		psf_binheader_readf (psf, "e44444", &(au_fmt.dataoffset), &(au_fmt.datasize),
+					&(au_fmt.encoding), &(au_fmt.samplerate), &(au_fmt.channels)) ;
+		}
+	else
+		return SFE_AU_NO_DOTSND ;
+
+	psf_log_printf (psf, "  Data Offset : %d\n", au_fmt.dataoffset) ;
+
+	if (psf->fileoffset > 0 && au_fmt.datasize == -1)
+	{	psf_log_printf (psf, "  Data Size   : -1\n") ;
+		return SFE_AU_EMBED_BAD_LEN ;
+		} ;
+
+	if (psf->fileoffset > 0)
+	{	psf->filelength = au_fmt.dataoffset + au_fmt.datasize ;
+		psf_log_printf (psf, "  Data Size   : %d\n", au_fmt.datasize) ;
+		}
+	else if (au_fmt.datasize == -1 || au_fmt.dataoffset + au_fmt.datasize == psf->filelength)
+		psf_log_printf (psf, "  Data Size   : %d\n", au_fmt.datasize) ;
+	else if (au_fmt.dataoffset + au_fmt.datasize < psf->filelength)
+	{	psf->filelength = au_fmt.dataoffset + au_fmt.datasize ;
+		psf_log_printf (psf, "  Data Size   : %d\n", au_fmt.datasize) ;
+		}
+	else
+	{	dword = psf->filelength - au_fmt.dataoffset ;
+		psf_log_printf (psf, "  Data Size   : %d (should be %d)\n", au_fmt.datasize, dword) ;
+		au_fmt.datasize = dword ;
+		} ;
+
+ 	psf->dataoffset = au_fmt.dataoffset ;
+	psf->datalength = psf->filelength - psf->dataoffset ;
+
+	if (psf_ftell (psf) < psf->dataoffset)
+		psf_binheader_readf (psf, "j", psf->dataoffset - psf_ftell (psf)) ;
+
+	psf->sf.samplerate	= au_fmt.samplerate ;
+	psf->sf.channels 	= au_fmt.channels ;
+
+	/* Only fill in type major. */
+	if (psf->endian == SF_ENDIAN_BIG)
+		psf->sf.format = SF_FORMAT_AU ;
+	else if (psf->endian == SF_ENDIAN_LITTLE)
+		psf->sf.format = SF_ENDIAN_LITTLE | SF_FORMAT_AU ;
+
+	psf_log_printf (psf, "  Encoding    : %d => ", au_fmt.encoding) ;
+
+	psf->sf.format = SF_ENDIAN (psf->sf.format) ;
+
+	switch (au_fmt.encoding)
+	{	case AU_ENCODING_ULAW_8 :
+				psf->sf.format |= SF_FORMAT_AU | SF_FORMAT_ULAW ;
+				psf->bytewidth = 1 ;	/* Before decoding */
+				psf_log_printf (psf, "8-bit ISDN u-law\n") ;
+				break ;
+
+		case AU_ENCODING_PCM_8 :
+				psf->sf.format |= SF_FORMAT_AU | SF_FORMAT_PCM_S8 ;
+				psf->bytewidth = 1 ;
+				psf_log_printf (psf, "8-bit linear PCM\n") ;
+				break ;
+
+		case AU_ENCODING_PCM_16 :
+				psf->sf.format |= SF_FORMAT_AU | SF_FORMAT_PCM_16 ;
+				psf->bytewidth = 2 ;
+				psf_log_printf (psf, "16-bit linear PCM\n") ;
+				break ;
+
+		case AU_ENCODING_PCM_24 :
+				psf->sf.format |= SF_FORMAT_AU | SF_FORMAT_PCM_24 ;
+				psf->bytewidth = 3 ;
+				psf_log_printf (psf, "24-bit linear PCM\n") ;
+				break ;
+
+		case AU_ENCODING_PCM_32 :
+				psf->sf.format |= SF_FORMAT_AU | SF_FORMAT_PCM_32 ;
+				psf->bytewidth = 4 ;
+				psf_log_printf (psf, "32-bit linear PCM\n") ;
+				break ;
+
+		case AU_ENCODING_FLOAT :
+				psf->sf.format |= SF_FORMAT_AU | SF_FORMAT_FLOAT ;
+				psf->bytewidth = 4 ;
+				psf_log_printf (psf, "32-bit float\n") ;
+				break ;
+
+		case AU_ENCODING_DOUBLE :
+				psf->sf.format |= SF_FORMAT_AU | SF_FORMAT_DOUBLE ;
+				psf->bytewidth = 8 ;
+				psf_log_printf (psf, "64-bit double precision float\n") ;
+				break ;
+
+		case AU_ENCODING_ALAW_8 :
+				psf->sf.format |= SF_FORMAT_AU | SF_FORMAT_ALAW ;
+				psf->bytewidth = 1 ;	/* Before decoding */
+				psf_log_printf (psf, "8-bit ISDN A-law\n") ;
+				break ;
+
+		case AU_ENCODING_ADPCM_G721_32 :
+				psf->sf.format |= SF_FORMAT_AU | SF_FORMAT_G721_32 ;
+				psf->bytewidth = 0 ;
+				psf_log_printf (psf, "G721 32kbs ADPCM\n") ;
+				break ;
+
+		case AU_ENCODING_ADPCM_G723_24 :
+				psf->sf.format |= SF_FORMAT_AU | SF_FORMAT_G723_24 ;
+				psf->bytewidth = 0 ;
+				psf_log_printf (psf, "G723 24kbs ADPCM\n") ;
+				break ;
+
+		case AU_ENCODING_ADPCM_G723_40 :
+				psf->sf.format |= SF_FORMAT_AU | SF_FORMAT_G723_40 ;
+				psf->bytewidth = 0 ;
+				psf_log_printf (psf, "G723 40kbs ADPCM\n") ;
+				break ;
+
+		case AU_ENCODING_ADPCM_G722 :
+				psf_log_printf (psf, "G722 64 kbs ADPCM (unsupported)\n") ;
+				break ;
+
+		case AU_ENCODING_NEXT :
+				psf_log_printf (psf, "Weird NeXT encoding format (unsupported)\n") ;
+				break ;
+
+		default :
+				psf_log_printf (psf, "Unknown!!\n") ;
+				break ;
+		} ;
+
+	psf_log_printf (psf, "  Sample Rate : %d\n", au_fmt.samplerate) ;
+	if (au_fmt.channels < 1)
+	{	psf_log_printf (psf, "  Channels    : %d  **** should be >= 1\n", au_fmt.channels) ;
+		return SFE_CHANNEL_COUNT_ZERO ;
+		}
+	else if (au_fmt.channels > SF_MAX_CHANNELS)
+	{	psf_log_printf (psf, "  Channels    : %d  **** should be <= %d\n", au_fmt.channels, SF_MAX_CHANNELS) ;
+		return SFE_CHANNEL_COUNT ;
+		} ;
+
+	psf_log_printf (psf, "  Channels    : %d\n", au_fmt.channels) ;
+
+	psf->blockwidth = psf->sf.channels * psf->bytewidth ;
+
+	if (! psf->sf.frames && psf->blockwidth)
+		psf->sf.frames = (psf->filelength - psf->dataoffset) / psf->blockwidth ;
+
+	return 0 ;
+} /* au_read_header */
+
diff --git a/src/audio_detect.c b/src/audio_detect.c
new file mode 100644
index 0000000..b2671e8
--- /dev/null
+++ b/src/audio_detect.c
@@ -0,0 +1,107 @@
+/*
+** Copyright (C) 1999-2012 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#else
+#include "sf_unistd.h"
+#endif
+
+#include <string.h>
+#include <errno.h>
+
+#include "common.h"
+
+typedef struct
+{	int le_float ;
+	int be_float ;
+	int le_int_24_32 ;
+	int be_int_24_32 ;
+} VOTE ;
+
+
+static void vote_for_format (VOTE * vote, const unsigned char * data, int datalen) ;
+
+int
+audio_detect (SF_PRIVATE * psf, AUDIO_DETECT *ad, const unsigned char * data, int datalen)
+{	VOTE vote ;
+
+	if (psf == NULL)
+		return 0 ;
+
+	if (ad == NULL || datalen < 256)
+		return 0 ;
+
+	vote_for_format (&vote, data, datalen) ;
+
+	psf_log_printf (psf, "audio_detect :\n"
+			"    le_float     : %d\n"
+			"    be_float     : %d\n"
+			"    le_int_24_32 : %d\n"
+			"    be_int_24_32 : %d\n",
+			vote.le_float, vote.be_float, vote.le_int_24_32, vote.be_int_24_32) ;
+
+	if (0) puts (psf->parselog.buf) ;
+
+	if (ad->endianness == SF_ENDIAN_LITTLE && vote.le_float > (3 * datalen) / 4)
+	{	/* Almost certainly 32 bit floats. */
+		return SF_FORMAT_FLOAT ;
+		} ;
+
+	if (ad->endianness == SF_ENDIAN_LITTLE && vote.le_int_24_32 > (3 * datalen) / 4)
+	{	/* Almost certainly 24 bit data stored in 32 bit ints. */
+		return SF_FORMAT_PCM_32 ;
+		} ;
+
+	return 0 ;
+} /* data_detect */
+
+static void
+vote_for_format (VOTE * vote, const unsigned char * data, int datalen)
+{
+	int k ;
+
+	memset (vote, 0, sizeof (VOTE)) ;
+
+	datalen -= datalen % 4 ;
+
+	for (k = 0 ; k < datalen ; k ++)
+	{	if ((k % 4) == 0)
+		{	if (data [k] == 0 && data [k + 1] != 0)
+				vote->le_int_24_32 += 4 ;
+
+			if (data [2] != 0 && data [3] == 0)
+				vote->le_int_24_32 += 4 ;
+
+			if (data [0] != 0 && data [3] > 0x43 && data [3] < 0x4B)
+				vote->le_float += 4 ;
+
+			if (data [3] != 0 && data [0] > 0x43 && data [0] < 0x4B)
+				vote->be_float += 4 ;
+			} ;
+		} ;
+
+	return ;
+} /* vote_for_format */
+
diff --git a/src/avr.c b/src/avr.c
new file mode 100644
index 0000000..6c78ff6
--- /dev/null
+++ b/src/avr.c
@@ -0,0 +1,244 @@
+/*
+** Copyright (C) 2004-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "sndfile.h"
+#include "sfendian.h"
+#include "common.h"
+
+#define TWOBIT_MARKER	(MAKE_MARKER ('2', 'B', 'I', 'T'))
+#define	AVR_HDR_SIZE	128
+
+/*
+** From: hyc@hanauma.Jpl.Nasa.Gov (Howard Chu)
+**
+** A lot of PD software exists to play Mac .snd files on the ST. One other
+** format that seems pretty popular (used by a number of commercial packages)
+** is the AVR format (from Audio Visual Research). This format has a 128 byte
+** header that looks like this (its actually packed, but thats not portable):
+*/
+
+typedef struct
+{	int		marker ;	/* 2BIT */
+	char	name [8] ;	/* null-padded sample name */
+	short	mono ;		/* 0 = mono, 0xffff = stereo */
+	short	rez ;		/* 8 = 8 bit, 16 = 16 bit */
+	short	sign ;		/* 0 = unsigned, 0xffff = signed */
+
+	short	loop ;		/* 0 = no loop, 0xffff = looping sample */
+	short	midi ;		/* 0xffff = no MIDI note assigned,  */
+						/*	0xffXX = single key note assignment */
+						/*	0xLLHH = key split, low/hi note */
+	int		srate ;		/* sample frequency in hertz */
+	int		frames ;	/* sample length in bytes or words (see rez) */
+	int		lbeg ;		/* offset to start of loop in bytes or words. */
+						/* set to zero if unused */
+	int		lend ;		/* offset to end of loop in bytes or words. */
+						/* set to sample length if unused */
+	short	res1 ;		/* Reserved, MIDI keyboard split */
+	short	res2 ;		/* Reserved, sample compression */
+	short	res3 ;		/* Reserved */
+	char	ext [20] ;	/* Additional filename space, used if (name[7] != 0) */
+	char	user [64] ; /* User defined. Typically ASCII message */
+} AVR_HEADER ;
+
+/*------------------------------------------------------------------------------
+** Private static functions.
+*/
+
+static int		avr_close (SF_PRIVATE *psf) ;
+
+static int		avr_read_header (SF_PRIVATE *psf) ;
+static int		avr_write_header (SF_PRIVATE *psf, int calc_length) ;
+
+/*------------------------------------------------------------------------------
+** Public function.
+*/
+
+int
+avr_open	(SF_PRIVATE *psf)
+{	int		error = 0 ;
+
+	if (psf->file.mode == SFM_READ || (psf->file.mode == SFM_RDWR && psf->filelength > 0))
+	{	if ((error = avr_read_header (psf)))
+			return error ;
+		} ;
+
+	if ((SF_CONTAINER (psf->sf.format)) != SF_FORMAT_AVR)
+		return	SFE_BAD_OPEN_FORMAT ;
+
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	psf->endian = SF_ENDIAN_BIG ;
+
+		if (avr_write_header (psf, SF_FALSE))
+			return psf->error ;
+
+		psf->write_header = avr_write_header ;
+		} ;
+
+	psf->container_close = avr_close ;
+
+	psf->blockwidth = psf->bytewidth * psf->sf.channels ;
+
+	error = pcm_init (psf) ;
+
+	return error ;
+} /* avr_open */
+
+static int
+avr_read_header (SF_PRIVATE *psf)
+{	AVR_HEADER	hdr ;
+
+	memset (&hdr, 0, sizeof (hdr)) ;
+
+	psf_binheader_readf (psf, "pmb", 0, &hdr.marker, &hdr.name, sizeof (hdr.name)) ;
+	psf_log_printf (psf, "%M\n", hdr.marker) ;
+
+	if (hdr.marker != TWOBIT_MARKER)
+		return SFE_AVR_NOT_AVR ;
+
+	psf_log_printf (psf, "  Name        : %s\n", hdr.name) ;
+
+	psf_binheader_readf (psf, "E22222", &hdr.mono, &hdr.rez, &hdr.sign, &hdr.loop, &hdr.midi) ;
+
+	psf->sf.channels = (hdr.mono & 1) + 1 ;
+
+	psf_log_printf (psf, "  Channels    : %d\n  Bit width   : %d\n  Signed      : %s\n",
+			(hdr.mono & 1) + 1, hdr.rez, hdr.sign ? "yes" : "no") ;
+
+	switch (arith_shift_left (hdr.rez, 16) + (hdr.sign & 1))
+	{	case ((8 << 16) + 0) :
+			psf->sf.format = SF_FORMAT_AVR | SF_FORMAT_PCM_U8 ;
+			psf->bytewidth = 1 ;
+			break ;
+
+		case ((8 << 16) + 1) :
+			psf->sf.format = SF_FORMAT_AVR | SF_FORMAT_PCM_S8 ;
+			psf->bytewidth = 1 ;
+			break ;
+
+		case ((16 << 16) + 1) :
+			psf->sf.format = SF_FORMAT_AVR | SF_FORMAT_PCM_16 ;
+			psf->bytewidth = 2 ;
+			break ;
+
+		default :
+			psf_log_printf (psf, "Error : bad rez/sign combination.\n") ;
+			return SFE_AVR_BAD_REZ_SIGN ;
+		} ;
+
+	psf_binheader_readf (psf, "E4444", &hdr.srate, &hdr.frames, &hdr.lbeg, &hdr.lend) ;
+
+	psf->sf.frames = hdr.frames ;
+	psf->sf.samplerate = hdr.srate ;
+
+	psf_log_printf (psf, "  Frames      : %D\n", psf->sf.frames) ;
+	psf_log_printf (psf, "  Sample rate : %d\n", psf->sf.samplerate) ;
+
+	psf_binheader_readf (psf, "E222", &hdr.res1, &hdr.res2, &hdr.res3) ;
+	psf_binheader_readf (psf, "bb", hdr.ext, sizeof (hdr.ext), hdr.user, sizeof (hdr.user)) ;
+
+	psf_log_printf (psf, "  Ext         : %s\n  User        : %s\n", hdr.ext, hdr.user) ;
+
+	psf->endian = SF_ENDIAN_BIG ;
+
+ 	psf->dataoffset = AVR_HDR_SIZE ;
+	psf->datalength = hdr.frames * (hdr.rez / 8) ;
+
+	if (psf->fileoffset > 0)
+		psf->filelength = AVR_HDR_SIZE + psf->datalength ;
+
+	if (psf_ftell (psf) != psf->dataoffset)
+		psf_binheader_readf (psf, "j", psf->dataoffset - psf_ftell (psf)) ;
+
+	psf->blockwidth = psf->sf.channels * psf->bytewidth ;
+
+	if (psf->sf.frames == 0 && psf->blockwidth)
+		psf->sf.frames = (psf->filelength - psf->dataoffset) / psf->blockwidth ;
+
+	return 0 ;
+} /* avr_read_header */
+
+static int
+avr_write_header (SF_PRIVATE *psf, int calc_length)
+{	sf_count_t	current ;
+	int			sign ;
+
+	if (psf->pipeoffset > 0)
+		return 0 ;
+
+	current = psf_ftell (psf) ;
+
+	if (calc_length)
+	{	psf->filelength = psf_get_filelen (psf) ;
+
+		psf->datalength = psf->filelength - psf->dataoffset ;
+		if (psf->dataend)
+			psf->datalength -= psf->filelength - psf->dataend ;
+
+		psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ;
+		} ;
+
+	/* Reset the current header length to zero. */
+	psf->header.ptr [0] = 0 ;
+	psf->header.indx = 0 ;
+
+	/*
+	** Only attempt to seek if we are not writng to a pipe. If we are
+	** writing to a pipe we shouldn't be here anyway.
+	*/
+	if (psf->is_pipe == SF_FALSE)
+		psf_fseek (psf, 0, SEEK_SET) ;
+
+	psf_binheader_writef (psf, "Emz22", BHWm (TWOBIT_MARKER), BHWz (8),
+			BHW2 (psf->sf.channels == 2 ? 0xFFFF : 0), BHW2 (psf->bytewidth * 8)) ;
+
+	sign = ((SF_CODEC (psf->sf.format)) == SF_FORMAT_PCM_U8) ? 0 : 0xFFFF ;
+
+	psf_binheader_writef (psf, "E222", BHW2 (sign), BHW2 (0), BHW2 (0xFFFF)) ;
+	psf_binheader_writef (psf, "E4444", BHW4 (psf->sf.samplerate), BHW4 (psf->sf.frames), BHW4 (0), BHW4 (0)) ;
+
+	psf_binheader_writef (psf, "E222zz", BHW2 (0), BHW2 (0), BHW2 (0), BHWz (20), BHWz (64)) ;
+
+	/* Header construction complete so write it out. */
+	psf_fwrite (psf->header.ptr, psf->header.indx, 1, psf) ;
+
+	if (psf->error)
+		return psf->error ;
+
+	psf->dataoffset = psf->header.indx ;
+
+	if (current > 0)
+		psf_fseek (psf, current, SEEK_SET) ;
+
+	return psf->error ;
+} /* avr_write_header */
+
+static int
+avr_close (SF_PRIVATE *psf)
+{
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+		avr_write_header (psf, SF_TRUE) ;
+
+	return 0 ;
+} /* avr_close */
+
diff --git a/src/binheader_writef_check.py b/src/binheader_writef_check.py
new file mode 100755
index 0000000..09ebc92
--- /dev/null
+++ b/src/binheader_writef_check.py
@@ -0,0 +1,114 @@
+#!/usr/bin/python
+
+# Copyright (C) 2006-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#     * Neither the author nor the names of any contributors may be used
+#       to endorse or promote products derived from this software without
+#       specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+# This parses C code using regexes (yes, thats horrible) and makes sure
+# that calling conventions to the function psf_binheader_writef are
+# correct.
+
+
+
+import re, string, sys
+
+_whitespace_re = re.compile ("\s+", re.MULTILINE)
+
+def find_binheader_writefs (data):
+    lst = re.findall ('psf_binheader_writef\s*\(\s*[a-zA-Z_]+\s*,\s*\"[^;]+;', data, re.MULTILINE)
+    return [_whitespace_re.sub (" ", x) for x in lst]
+
+def find_format_string (s):
+    fmt = re.search ('"([^"]+)"', s)
+    if not fmt:
+        print ("Bad format in :\n\n\t%s\n\n" % s)
+        sys.exit (1)
+    fmt = fmt.groups ()
+    if len (fmt) != 1:
+        print ("Bad format in :\n\n\t%s\n\n" % s)
+        sys.exit (1)
+    return _whitespace_re.sub ("", fmt [0])
+
+def get_param_list (data):
+    dlist = re.search ("\((.+)\)\s*;", data)
+    dlist = dlist.groups ()[0]
+    dlist = dlist.split(",")
+    dlist = [x.strip() for x in dlist]
+    return dlist [2:]
+
+def handle_file (fname):
+    errors = 0
+    data = open (fname, "r").read ()
+
+    # return errors
+
+    writefs = find_binheader_writefs (data)
+    for item in writefs:
+        fmt = find_format_string (item)
+        params = get_param_list (item)
+        param_index = 0
+
+        # print item
+
+        for ch in fmt:
+            if ch in 'Eet ':
+                continue
+
+            if ch == 'b':
+                if params [param_index][:4] == "BHWv" and params [param_index + 1][:4] == "BHWz":
+                    param_index += 2
+                    continue
+
+            if "BHW" + ch == params [param_index][:4]:
+                param_index += 1
+                continue
+
+            if errors == 0: sys.stdout.write ("\n")
+            print ("\n%s: error for format specifier '%c' (index %d) in:\n    %s\n" % (fname, ch, param_index, item))
+            errors += 1
+            # Break out of 'for ch in fmt' loop
+            break
+
+    return errors
+
+#===============================================================================
+
+if len (sys.argv) > 1:
+    sys.stdout.write ("\n    binheader_writef_check                   : ")
+    sys.stdout.flush ()
+    errors = 0
+    for fname in sys.argv [1:]:
+        errors += handle_file (fname)
+    if errors > 0:
+        print ("\nErrors : %d\n" % errors)
+        sys.exit (1)
+
+print ("ok\n")
+
diff --git a/src/broadcast.c b/src/broadcast.c
new file mode 100644
index 0000000..aad5f88
--- /dev/null
+++ b/src/broadcast.c
@@ -0,0 +1,190 @@
+/*
+** Copyright (C) 2006-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2006 Paul Davis <paul@linuxaudiosystems.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "common.h"
+
+
+static int gen_coding_history (char * added_history, int added_history_max, const SF_INFO * psfinfo) ;
+
+static inline size_t
+bc_min_size (const SF_BROADCAST_INFO* info)
+{	if (info == NULL)
+		return 0 ;
+
+	return offsetof (SF_BROADCAST_INFO, coding_history) + info->coding_history_size ;
+} /* bc_min_size */
+
+SF_BROADCAST_INFO_16K*
+broadcast_var_alloc (void)
+{	return calloc (1, sizeof (SF_BROADCAST_INFO_16K)) ;
+} /* broadcast_var_alloc */
+
+int
+broadcast_var_set (SF_PRIVATE *psf, const SF_BROADCAST_INFO * info, size_t datasize)
+{	size_t len ;
+
+	if (info == NULL)
+		return SF_FALSE ;
+
+	if (bc_min_size (info) > datasize)
+	{	psf->error = SFE_BAD_BROADCAST_INFO_SIZE ;
+		return SF_FALSE ;
+		} ;
+
+	if (datasize >= sizeof (SF_BROADCAST_INFO_16K))
+	{	psf->error = SFE_BAD_BROADCAST_INFO_TOO_BIG ;
+		return SF_FALSE ;
+		} ;
+
+	if (psf->broadcast_16k == NULL)
+	{	if ((psf->broadcast_16k = broadcast_var_alloc ()) == NULL)
+		{	psf->error = SFE_MALLOC_FAILED ;
+			return SF_FALSE ;
+			} ;
+		} ;
+
+	/* Only copy the first part of the struct. */
+	memcpy (psf->broadcast_16k, info, offsetof (SF_BROADCAST_INFO, coding_history)) ;
+
+	psf_strlcpy_crlf (psf->broadcast_16k->coding_history, info->coding_history, sizeof (psf->broadcast_16k->coding_history), datasize - offsetof (SF_BROADCAST_INFO, coding_history)) ;
+	len = strlen (psf->broadcast_16k->coding_history) ;
+
+	if (len > 0 && psf->broadcast_16k->coding_history [len - 1] != '\n')
+		psf_strlcat (psf->broadcast_16k->coding_history, sizeof (psf->broadcast_16k->coding_history), "\r\n") ;
+
+	if (psf->file.mode == SFM_WRITE)
+	{	char added_history [256] ;
+
+		gen_coding_history (added_history, sizeof (added_history), &(psf->sf)) ;
+		psf_strlcat (psf->broadcast_16k->coding_history, sizeof (psf->broadcast_16k->coding_history), added_history) ;
+		} ;
+
+	/* Force coding_history_size to be even. */
+	len = strlen (psf->broadcast_16k->coding_history) ;
+	len += (len & 1) ? 1 : 0 ;
+	psf->broadcast_16k->coding_history_size = (uint32_t) len ;
+
+	/* Currently writing this version. */
+	psf->broadcast_16k->version = 2 ;
+
+	return SF_TRUE ;
+} /* broadcast_var_set */
+
+
+int
+broadcast_var_get (SF_PRIVATE *psf, SF_BROADCAST_INFO * data, size_t datasize)
+{	size_t size ;
+
+	if (psf->broadcast_16k == NULL)
+		return SF_FALSE ;
+
+	size = SF_MIN (datasize, bc_min_size ((const SF_BROADCAST_INFO *) psf->broadcast_16k)) ;
+
+	memcpy (data, psf->broadcast_16k, size) ;
+
+	return SF_TRUE ;
+} /* broadcast_var_get */
+
+/*------------------------------------------------------------------------------
+*/
+
+static int
+gen_coding_history (char * added_history, int added_history_max, const SF_INFO * psfinfo)
+{	char chnstr [16] ;
+	int count, width ;
+
+	/*
+	**	From : http://www.sr.se/utveckling/tu/bwf/docs/codhist2.htm
+	**
+	**	Parameter            Variable string <allowed option>                 Unit
+	**	==========================================================================================
+	**	Coding Algorithm     A=<ANALOGUE, PCM, MPEG1L1, MPEG1L2, MPEG1L3,
+	**	                    MPEG2L1, MPEG2L2, MPEG2L3>
+	**	Sampling frequency   F=<11000,22050,24000,32000,44100,48000>          [Hz]
+	**	Bit-rate             B=<any bit-rate allowed in MPEG 2 (ISO/IEC       [kbit/s per channel]
+	**	                    13818-3)>
+	**	Word Length          W=<8, 12, 14, 16, 18, 20, 22, 24>                [bits]
+	**	Mode                 M=<mono, stereo, dual-mono, joint-stereo>
+	**	Text, free string    T=<a free ASCII-text string for in house use.
+	**	                    This string should contain no commas (ASCII
+	**	                    2Chex). Examples of the contents: ID-No; codec
+	**	                    type; A/D type>
+	*/
+
+	switch (psfinfo->channels)
+	{	case 0 :
+			return SF_FALSE ;
+
+		case 1 :
+			psf_strlcpy (chnstr, sizeof (chnstr), "mono") ;
+			break ;
+
+		case 2 :
+			psf_strlcpy (chnstr, sizeof (chnstr), "stereo") ;
+			break ;
+
+		default :
+			snprintf (chnstr, sizeof (chnstr), "%dchn", psfinfo->channels) ;
+			break ;
+		} ;
+
+	switch (SF_CODEC (psfinfo->format))
+	{	case SF_FORMAT_PCM_U8 :
+		case SF_FORMAT_PCM_S8 :
+			width = 8 ;
+			break ;
+		case SF_FORMAT_PCM_16 :
+			width = 16 ;
+			break ;
+		case SF_FORMAT_PCM_24 :
+			width = 24 ;
+			break ;
+		case SF_FORMAT_PCM_32 :
+			width = 32 ;
+			break ;
+		case SF_FORMAT_FLOAT :
+			width = 24 ; /* Bits in the mantissa + 1 */
+			break ;
+		case SF_FORMAT_DOUBLE :
+			width = 53 ; /* Bits in the mantissa + 1 */
+			break ;
+		case SF_FORMAT_ULAW :
+		case SF_FORMAT_ALAW :
+			width = 12 ;
+			break ;
+		default :
+			width = 42 ;
+			break ;
+		} ;
+
+	count = snprintf (added_history, added_history_max,
+							"A=PCM,F=%d,W=%d,M=%s,T=%s-%s\r\n",
+							psfinfo->samplerate, width, chnstr, PACKAGE_NAME, PACKAGE_VERSION) ;
+
+	if (count >= added_history_max)
+		return 0 ;
+
+	return count ;
+} /* gen_coding_history */
diff --git a/src/caf.c b/src/caf.c
new file mode 100644
index 0000000..a886bf5
--- /dev/null
+++ b/src/caf.c
@@ -0,0 +1,1034 @@
+/*
+** Copyright (C) 2005-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<stdlib.h>
+#include	<string.h>
+#include	<ctype.h>
+#include	<math.h>
+#include	<inttypes.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+#include	"chanmap.h"
+
+/*------------------------------------------------------------------------------
+** Macros to handle big/little endian issues.
+*/
+
+#define aac_MARKER		MAKE_MARKER ('a', 'a', 'c', ' ')
+#define alac_MARKER		MAKE_MARKER ('a', 'l', 'a', 'c')
+#define alaw_MARKER		MAKE_MARKER ('a', 'l', 'a', 'w')
+#define caff_MARKER		MAKE_MARKER ('c', 'a', 'f', 'f')
+#define chan_MARKER		MAKE_MARKER ('c', 'h', 'a', 'n')
+#define data_MARKER		MAKE_MARKER ('d', 'a', 't', 'a')
+#define desc_MARKER		MAKE_MARKER ('d', 'e', 's', 'c')
+#define edct_MARKER		MAKE_MARKER ('e', 'd', 'c', 't')
+#define free_MARKER		MAKE_MARKER ('f', 'r', 'e', 'e')
+#define ima4_MARKER		MAKE_MARKER ('i', 'm', 'a', '4')
+#define info_MARKER		MAKE_MARKER ('i', 'n', 'f', 'o')
+#define inst_MARKER		MAKE_MARKER ('i', 'n', 's', 't')
+#define kuki_MARKER		MAKE_MARKER ('k', 'u', 'k', 'i')
+#define lpcm_MARKER		MAKE_MARKER ('l', 'p', 'c', 'm')
+#define mark_MARKER		MAKE_MARKER ('m', 'a', 'r', 'k')
+#define midi_MARKER		MAKE_MARKER ('m', 'i', 'd', 'i')
+#define mp1_MARKER		MAKE_MARKER ('.', 'm', 'p', '1')
+#define mp2_MARKER		MAKE_MARKER ('.', 'm', 'p', '2')
+#define mp3_MARKER		MAKE_MARKER ('.', 'm', 'p', '3')
+#define ovvw_MARKER		MAKE_MARKER ('o', 'v', 'v', 'w')
+#define pakt_MARKER		MAKE_MARKER ('p', 'a', 'k', 't')
+#define peak_MARKER		MAKE_MARKER ('p', 'e', 'a', 'k')
+#define regn_MARKER		MAKE_MARKER ('r', 'e', 'g', 'n')
+#define strg_MARKER		MAKE_MARKER ('s', 't', 'r', 'g')
+#define umid_MARKER		MAKE_MARKER ('u', 'm', 'i', 'd')
+#define uuid_MARKER		MAKE_MARKER ('u', 'u', 'i', 'd')
+#define ulaw_MARKER		MAKE_MARKER ('u', 'l', 'a', 'w')
+#define MAC3_MARKER		MAKE_MARKER ('M', 'A', 'C', '3')
+#define MAC6_MARKER		MAKE_MARKER ('M', 'A', 'C', '6')
+
+#define CAF_PEAK_CHUNK_SIZE(ch) 	((int) (sizeof (int) + ch * (sizeof (float) + 8)))
+
+/*------------------------------------------------------------------------------
+** Typedefs.
+*/
+
+typedef struct
+{	uint8_t		srate [8] ;
+	uint32_t	fmt_id ;
+	uint32_t	fmt_flags ;
+	uint32_t	pkt_bytes ;
+	uint32_t	frames_per_packet ;
+	uint32_t	channels_per_frame ;
+	uint32_t	bits_per_chan ;
+} DESC_CHUNK ;
+
+typedef struct
+{	int			chanmap_tag ;
+
+	ALAC_DECODER_INFO	alac ;
+} CAF_PRIVATE ;
+
+/*------------------------------------------------------------------------------
+** Private static functions.
+*/
+
+static int	caf_close (SF_PRIVATE *psf) ;
+static int	caf_read_header (SF_PRIVATE *psf) ;
+static int	caf_write_header (SF_PRIVATE *psf, int calc_length) ;
+static int	caf_write_tailer (SF_PRIVATE *psf) ;
+static int	caf_command (SF_PRIVATE *psf, int command, void *data, int datasize) ;
+static int	caf_read_chanmap (SF_PRIVATE * psf, sf_count_t chunk_size) ;
+static int	caf_read_strings (SF_PRIVATE * psf, sf_count_t chunk_size) ;
+static void	caf_write_strings (SF_PRIVATE * psf, int location) ;
+
+
+static int caf_set_chunk (SF_PRIVATE *psf, const SF_CHUNK_INFO * chunk_info) ;
+static SF_CHUNK_ITERATOR * caf_next_chunk_iterator (SF_PRIVATE *psf, SF_CHUNK_ITERATOR * iterator) ;
+static int caf_get_chunk_size (SF_PRIVATE *psf, const SF_CHUNK_ITERATOR * iterator, SF_CHUNK_INFO * chunk_info) ;
+static int caf_get_chunk_data (SF_PRIVATE *psf, const SF_CHUNK_ITERATOR * iterator, SF_CHUNK_INFO * chunk_info) ;
+
+/*------------------------------------------------------------------------------
+** Public function.
+*/
+
+int
+caf_open (SF_PRIVATE *psf)
+{	CAF_PRIVATE * pcaf ;
+	int	subformat, format, error = 0 ;
+
+	if ((psf->container_data = calloc (1, sizeof (CAF_PRIVATE))) == NULL)
+		return SFE_MALLOC_FAILED ;
+
+	pcaf = psf->container_data ;
+
+	if (psf->file.mode == SFM_READ || (psf->file.mode == SFM_RDWR && psf->filelength > 0))
+	{	if ((error = caf_read_header (psf)))
+			return error ;
+
+		psf->next_chunk_iterator = caf_next_chunk_iterator ;
+		psf->get_chunk_size = caf_get_chunk_size ;
+		psf->get_chunk_data = caf_get_chunk_data ;
+		} ;
+
+	subformat = SF_CODEC (psf->sf.format) ;
+
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	if (psf->is_pipe)
+			return SFE_NO_PIPE_WRITE ;
+
+		format = SF_CONTAINER (psf->sf.format) ;
+		if (format != SF_FORMAT_CAF)
+			return	SFE_BAD_OPEN_FORMAT ;
+
+		psf->blockwidth = psf->bytewidth * psf->sf.channels ;
+
+		if (psf->file.mode != SFM_RDWR || psf->filelength < 44)
+		{	psf->filelength = 0 ;
+			psf->datalength = 0 ;
+			psf->dataoffset = 0 ;
+			psf->sf.frames = 0 ;
+			} ;
+
+		psf->strings.flags = SF_STR_ALLOW_START | SF_STR_ALLOW_END ;
+
+		/*
+		**	By default, add the peak chunk to floating point files. Default behaviour
+		**	can be switched off using sf_command (SFC_SET_PEAK_CHUNK, SF_FALSE).
+		*/
+		if (psf->file.mode == SFM_WRITE && (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE))
+		{	if ((psf->peak_info = peak_info_calloc (psf->sf.channels)) == NULL)
+				return SFE_MALLOC_FAILED ;
+			psf->peak_info->peak_loc = SF_PEAK_START ;
+			} ;
+
+		if ((error = caf_write_header (psf, SF_FALSE)) != 0)
+			return error ;
+
+		psf->write_header	= caf_write_header ;
+		psf->set_chunk		= caf_set_chunk ;
+		} ;
+
+	psf->container_close = caf_close ;
+	psf->command = caf_command ;
+
+	switch (subformat)
+	{	case SF_FORMAT_PCM_S8 :
+		case SF_FORMAT_PCM_16 :
+		case SF_FORMAT_PCM_24 :
+		case SF_FORMAT_PCM_32 :
+					error = pcm_init (psf) ;
+					break ;
+
+		case SF_FORMAT_ULAW :
+					error = ulaw_init (psf) ;
+					break ;
+
+		case SF_FORMAT_ALAW :
+					error = alaw_init (psf) ;
+					break ;
+
+		/* Lite remove start */
+		case SF_FORMAT_FLOAT :
+					error = float32_init (psf) ;
+					break ;
+
+		case SF_FORMAT_DOUBLE :
+					error = double64_init (psf) ;
+					break ;
+
+		case SF_FORMAT_ALAC_16 :
+		case SF_FORMAT_ALAC_20 :
+		case SF_FORMAT_ALAC_24 :
+		case SF_FORMAT_ALAC_32 :
+					if (psf->file.mode == SFM_READ)
+						/* Only pass the ALAC_DECODER_INFO in read mode. */
+						error = alac_init (psf, &pcaf->alac) ;
+					else
+						error = alac_init (psf, NULL) ;
+					break ;
+
+		/* Lite remove end */
+
+		default :
+			return SFE_UNSUPPORTED_ENCODING ;
+		} ;
+
+	return error ;
+} /* caf_open */
+
+static int
+caf_close (SF_PRIVATE *psf)
+{
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	caf_write_tailer (psf) ;
+		caf_write_header (psf, SF_TRUE) ;
+		} ;
+
+	return 0 ;
+} /* caf_close */
+
+static int
+caf_command (SF_PRIVATE * psf, int command, void * UNUSED (data), int UNUSED (datasize))
+{	CAF_PRIVATE	*pcaf ;
+
+	if ((pcaf = psf->container_data) == NULL)
+		return SFE_INTERNAL ;
+
+	switch (command)
+	{	case SFC_SET_CHANNEL_MAP_INFO :
+			pcaf->chanmap_tag = aiff_caf_find_channel_layout_tag (psf->channel_map, psf->sf.channels) ;
+			return (pcaf->chanmap_tag != 0) ;
+
+		default :
+			break ;
+	} ;
+
+	return 0 ;
+} /* caf_command */
+
+/*------------------------------------------------------------------------------
+*/
+
+static int
+decode_desc_chunk (SF_PRIVATE *psf, const DESC_CHUNK *desc)
+{	int format = SF_FORMAT_CAF ;
+
+	psf->sf.channels = desc->channels_per_frame ;
+
+	if (desc->fmt_id == alac_MARKER)
+	{	CAF_PRIVATE	*pcaf ;
+
+		if ((pcaf = psf->container_data) != NULL)
+		{	switch (desc->fmt_flags)
+			{	case 1 :
+					pcaf->alac.bits_per_sample = 16 ;
+					format |= SF_FORMAT_ALAC_16 ;
+					break ;
+				case 2 :
+					pcaf->alac.bits_per_sample = 20 ;
+					format |= SF_FORMAT_ALAC_20 ;
+					break ;
+				case 3 :
+					pcaf->alac.bits_per_sample = 24 ;
+					format |= SF_FORMAT_ALAC_24 ;
+					break ;
+				case 4 :
+					pcaf->alac.bits_per_sample = 32 ;
+					format |= SF_FORMAT_ALAC_32 ;
+					break ;
+				default :
+					psf_log_printf (psf, "Bad ALAC format flag value of %d\n", desc->fmt_flags) ;
+				} ;
+
+			pcaf->alac.frames_per_packet = desc->frames_per_packet ;
+			} ;
+
+		return format ;
+		} ;
+
+	format |= psf->endian == SF_ENDIAN_LITTLE ? SF_ENDIAN_LITTLE : 0 ;
+
+	if (desc->fmt_id == lpcm_MARKER && desc->fmt_flags & 1)
+	{	/* Floating point data. */
+		if (desc->bits_per_chan == 32 && desc->pkt_bytes == 4 * desc->channels_per_frame)
+		{	psf->bytewidth = 4 ;
+			return format | SF_FORMAT_FLOAT ;
+			} ;
+		if (desc->bits_per_chan == 64 && desc->pkt_bytes == 8 * desc->channels_per_frame)
+		{	psf->bytewidth = 8 ;
+			return format | SF_FORMAT_DOUBLE ;
+			} ;
+		} ;
+
+	if (desc->fmt_id == lpcm_MARKER && (desc->fmt_flags & 1) == 0)
+	{	/* Integer data. */
+		if (desc->bits_per_chan == 32 && desc->pkt_bytes == 4 * desc->channels_per_frame)
+		{	psf->bytewidth = 4 ;
+			return format | SF_FORMAT_PCM_32 ;
+			} ;
+		if (desc->bits_per_chan == 24 && desc->pkt_bytes == 3 * desc->channels_per_frame)
+		{	psf->bytewidth = 3 ;
+			return format | SF_FORMAT_PCM_24 ;
+			} ;
+		if (desc->bits_per_chan == 16 && desc->pkt_bytes == 2 * desc->channels_per_frame)
+		{	psf->bytewidth = 2 ;
+			return format | SF_FORMAT_PCM_16 ;
+			} ;
+		if (desc->bits_per_chan == 8 && desc->pkt_bytes == 1 * desc->channels_per_frame)
+		{	psf->bytewidth = 1 ;
+			return format | SF_FORMAT_PCM_S8 ;
+			} ;
+		} ;
+
+	if (desc->fmt_id == alaw_MARKER && desc->bits_per_chan == 8)
+	{	psf->bytewidth = 1 ;
+		return format | SF_FORMAT_ALAW ;
+		} ;
+
+	if (desc->fmt_id == ulaw_MARKER && desc->bits_per_chan == 8)
+	{	psf->bytewidth = 1 ;
+		return format | SF_FORMAT_ULAW ;
+		} ;
+
+	psf_log_printf (psf, "**** Unknown format identifier.\n") ;
+
+	return 0 ;
+} /* decode_desc_chunk */
+
+static int
+caf_read_header (SF_PRIVATE *psf)
+{	CAF_PRIVATE	*pcaf ;
+	BUF_UNION	ubuf ;
+	DESC_CHUNK desc ;
+	sf_count_t chunk_size ;
+	double srate ;
+	short version, flags ;
+	int marker, k, have_data = 0, error ;
+
+	if ((pcaf = psf->container_data) == NULL)
+		return SFE_INTERNAL ;
+
+	memset (&desc, 0, sizeof (desc)) ;
+
+	/* Set position to start of file to begin reading header. */
+	psf_binheader_readf (psf, "pmE2E2", 0, &marker, &version, &flags) ;
+	psf_log_printf (psf, "%M\n  Version : %d\n  Flags   : %x\n", marker, version, flags) ;
+	if (marker != caff_MARKER)
+		return SFE_CAF_NOT_CAF ;
+
+	psf_binheader_readf (psf, "mE8b", &marker, &chunk_size, ubuf.ucbuf, 8) ;
+	srate = double64_be_read (ubuf.ucbuf) ;
+	snprintf (ubuf.cbuf, sizeof (ubuf.cbuf), "%5.3f", srate) ;
+	psf_log_printf (psf, "%M : %D\n  Sample rate  : %s\n", marker, chunk_size, ubuf.cbuf) ;
+	if (marker != desc_MARKER)
+		return SFE_CAF_NO_DESC ;
+
+	if (chunk_size < SIGNED_SIZEOF (DESC_CHUNK))
+	{	psf_log_printf (psf, "**** Chunk size too small. Should be > 32 bytes.\n") ;
+		return SFE_MALFORMED_FILE ;
+		} ;
+
+	psf->sf.samplerate = psf_lrint (srate) ;
+
+	psf_binheader_readf (psf, "mE44444", &desc.fmt_id, &desc.fmt_flags, &desc.pkt_bytes, &desc.frames_per_packet,
+			&desc.channels_per_frame, &desc.bits_per_chan) ;
+	psf_log_printf (psf, "  Format id    : %M\n  Format flags : %x\n  Bytes / packet   : %u\n"
+			"  Frames / packet  : %u\n  Channels / frame : %u\n  Bits / channel   : %u\n",
+			desc.fmt_id, desc.fmt_flags, desc.pkt_bytes, desc.frames_per_packet, desc.channels_per_frame, desc.bits_per_chan) ;
+
+	if (desc.channels_per_frame > SF_MAX_CHANNELS)
+	{	psf_log_printf (psf, "**** Bad channels per frame value %u.\n", desc.channels_per_frame) ;
+		return SFE_MALFORMED_FILE ;
+		} ;
+
+	if (chunk_size > SIGNED_SIZEOF (DESC_CHUNK))
+		psf_binheader_readf (psf, "j", (int) (chunk_size - sizeof (DESC_CHUNK))) ;
+
+	psf->sf.channels = desc.channels_per_frame ;
+
+	while (1)
+	{	marker = 0 ;
+		chunk_size = 0 ;
+
+		psf_binheader_readf (psf, "mE8", &marker, &chunk_size) ;
+		if (marker == 0)
+		{	sf_count_t pos = psf_ftell (psf) ;
+			psf_log_printf (psf, "Have 0 marker at position %D (0x%x).\n", pos, pos) ;
+			break ;
+			} ;
+		if (chunk_size < 0)
+		{	psf_log_printf (psf, "%M : %D *** Should be >= 0 ***\n", marker, chunk_size) ;
+			break ;
+			} ;
+		if (chunk_size > psf->filelength)
+			break ;
+
+		psf_store_read_chunk_u32 (&psf->rchunks, marker, psf_ftell (psf), chunk_size) ;
+
+		switch (marker)
+		{	case peak_MARKER :
+				psf_log_printf (psf, "%M : %D\n", marker, chunk_size) ;
+				if (chunk_size != CAF_PEAK_CHUNK_SIZE (psf->sf.channels))
+				{	psf_binheader_readf (psf, "j", (size_t) chunk_size) ;
+					psf_log_printf (psf, "*** File PEAK chunk %D should be %d.\n", chunk_size, CAF_PEAK_CHUNK_SIZE (psf->sf.channels)) ;
+					return SFE_CAF_BAD_PEAK ;
+					} ;
+
+				if (psf->peak_info)
+				{	psf_log_printf (psf, "*** Found existing peak info, using last one.\n") ;
+					free (psf->peak_info) ;
+					psf->peak_info = NULL ;
+					} ;
+				if ((psf->peak_info = peak_info_calloc (psf->sf.channels)) == NULL)
+					return SFE_MALLOC_FAILED ;
+
+				/* read in rest of PEAK chunk. */
+				psf_binheader_readf (psf, "E4", & (psf->peak_info->edit_number)) ;
+				psf_log_printf (psf, "  edit count : %d\n", psf->peak_info->edit_number) ;
+
+				psf_log_printf (psf, "     Ch   Position       Value\n") ;
+				for (k = 0 ; k < psf->sf.channels ; k++)
+				{	sf_count_t position ;
+					float value ;
+
+					psf_binheader_readf (psf, "Ef8", &value, &position) ;
+					psf->peak_info->peaks [k].value = value ;
+					psf->peak_info->peaks [k].position = position ;
+
+					snprintf (ubuf.cbuf, sizeof (ubuf.cbuf), "    %2d   %-12" PRId64 "   %g\n", k, position, value) ;
+					psf_log_printf (psf, ubuf.cbuf) ;
+					} ;
+
+				psf->peak_info->peak_loc = SF_PEAK_START ;
+				break ;
+
+			case chan_MARKER :
+				if (chunk_size < 12)
+				{	psf_log_printf (psf, "%M : %D (should be >= 12)\n", marker, chunk_size) ;
+					psf_binheader_readf (psf, "j", (size_t) chunk_size) ;
+					break ;
+					}
+
+				psf_log_printf (psf, "%M : %D\n", marker, chunk_size) ;
+
+				if ((error = caf_read_chanmap (psf, chunk_size)))
+					return error ;
+				break ;
+
+			case free_MARKER :
+				psf_log_printf (psf, "%M : %D\n", marker, chunk_size) ;
+				psf_binheader_readf (psf, "j", (size_t) chunk_size) ;
+				break ;
+
+			case data_MARKER :
+				psf_binheader_readf (psf, "E4", &k) ;
+				if (chunk_size == -1)
+				{	psf_log_printf (psf, "%M : -1\n") ;
+					chunk_size = psf->filelength - psf->header.indx ;
+					}
+				else if (psf->filelength > 0 && chunk_size > psf->filelength - psf->header.indx + 10)
+				{	psf_log_printf (psf, "%M : %D (should be %D)\n", marker, chunk_size, psf->filelength - psf->header.indx - 8) ;
+					psf->datalength = psf->filelength - psf->header.indx - 8 ;
+					}
+				else
+				{	psf_log_printf (psf, "%M : %D\n", marker, chunk_size) ;
+					/* Subtract the 4 bytes of the 'edit' field above. */
+					psf->datalength = chunk_size - 4 ;
+					} ;
+
+				psf_log_printf (psf, "  edit : %u\n", k) ;
+
+				psf->dataoffset = psf->header.indx ;
+				if (psf->datalength + psf->dataoffset < psf->filelength)
+					psf->dataend = psf->datalength + psf->dataoffset ;
+
+				psf_binheader_readf (psf, "j", (size_t) psf->datalength) ;
+				have_data = 1 ;
+				break ;
+
+			case kuki_MARKER :
+				psf_log_printf (psf, "%M : %D\n", marker, chunk_size) ;
+				pcaf->alac.kuki_offset = psf_ftell (psf) - 12 ;
+				psf_binheader_readf (psf, "j", (size_t) chunk_size) ;
+				break ;
+
+			case pakt_MARKER :
+				if (chunk_size < 24)
+				{	psf_log_printf (psf, "%M : %D (should be > 24)\n", marker, chunk_size) ;
+					return SFE_MALFORMED_FILE ;
+					}
+				else if (chunk_size > psf->filelength - psf->header.indx)
+				{	psf_log_printf (psf, "%M : %D (should be < %D)\n", marker, chunk_size, psf->filelength - psf->header.indx) ;
+					return SFE_MALFORMED_FILE ;
+					}
+				else
+					psf_log_printf (psf, "%M : %D\n", marker, chunk_size) ;
+
+				psf_binheader_readf (psf, "E8844", &pcaf->alac.packets, &pcaf->alac.valid_frames,
+									&pcaf->alac.priming_frames, &pcaf->alac.remainder_frames) ;
+
+				psf_log_printf (psf,
+						"  Packets          : %D\n"
+						"  Valid frames     : %D\n"
+						"  Priming frames   : %d\n"
+						"  Remainder frames : %d\n",
+						pcaf->alac.packets, pcaf->alac.valid_frames, pcaf->alac.priming_frames,
+						pcaf->alac.remainder_frames
+						) ;
+
+				if (pcaf->alac.packets == 0 && pcaf->alac.valid_frames == 0
+							&& pcaf->alac.priming_frames == 0 && pcaf->alac.remainder_frames == 0)
+					psf_log_printf (psf, "*** 'pakt' chunk header is all zero.\n") ;
+
+				pcaf->alac.pakt_offset = psf_ftell (psf) - 12 ;
+				psf_binheader_readf (psf, "j", (size_t) chunk_size - 24) ;
+				break ;
+
+			case info_MARKER :
+				if (chunk_size < 4)
+				{	psf_log_printf (psf, "%M : %D (should be > 4)\n", marker, chunk_size) ;
+					return SFE_MALFORMED_FILE ;
+					}
+				else if (chunk_size > psf->filelength - psf->header.indx)
+				{	psf_log_printf (psf, "%M : %D (should be < %D)\n", marker, chunk_size, psf->filelength - psf->header.indx) ;
+					return SFE_MALFORMED_FILE ;
+					} ;
+				psf_log_printf (psf, "%M : %D\n", marker, chunk_size) ;
+				if (chunk_size > 4)
+					caf_read_strings (psf, chunk_size - 4) ;
+				break ;
+
+			default :
+				psf_log_printf (psf, "%M : %D (skipped)\n", marker, chunk_size) ;
+				psf_binheader_readf (psf, "j", (size_t) chunk_size) ;
+				break ;
+			} ;
+
+		if (marker != data_MARKER && chunk_size >= 0xffffff00)
+			break ;
+
+		if (! psf->sf.seekable && have_data)
+			break ;
+
+		if (psf_ftell (psf) >= psf->filelength - SIGNED_SIZEOF (chunk_size))
+		{	psf_log_printf (psf, "End\n") ;
+			break ;
+			} ;
+		} ;
+
+	if (have_data == 0)
+	{	psf_log_printf (psf, "**** Error, could not find 'data' chunk.\n") ;
+		return SFE_MALFORMED_FILE ;
+		} ;
+
+	psf->endian = (desc.fmt_flags & 2) ? SF_ENDIAN_LITTLE : SF_ENDIAN_BIG ;
+
+	psf_fseek (psf, psf->dataoffset, SEEK_SET) ;
+
+	if ((psf->sf.format = decode_desc_chunk (psf, &desc)) == 0)
+		return SFE_UNSUPPORTED_ENCODING ;
+
+	if (psf->bytewidth > 0)
+		psf->sf.frames = psf->datalength / psf->bytewidth ;
+
+	return 0 ;
+} /* caf_read_header */
+
+/*------------------------------------------------------------------------------
+*/
+
+static int
+caf_write_header (SF_PRIVATE *psf, int calc_length)
+{	BUF_UNION	ubuf ;
+	CAF_PRIVATE	*pcaf ;
+	DESC_CHUNK desc ;
+	sf_count_t current ;
+	uint32_t uk ;
+	int subformat, append_free_block = SF_TRUE ;
+
+	if ((pcaf = psf->container_data) == NULL)
+		return SFE_INTERNAL ;
+
+	memset (&desc, 0, sizeof (desc)) ;
+
+	current = psf_ftell (psf) ;
+
+	if (calc_length)
+	{	psf->filelength = psf_get_filelen (psf) ;
+
+		psf->datalength = psf->filelength - psf->dataoffset ;
+
+		if (psf->dataend)
+			psf->datalength -= psf->filelength - psf->dataend ;
+
+		if (psf->bytewidth > 0)
+			psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ;
+		} ;
+
+	/* Reset the current header length to zero. */
+	psf->header.ptr [0] = 0 ;
+	psf->header.indx = 0 ;
+	psf_fseek (psf, 0, SEEK_SET) ;
+
+	/* 'caff' marker, version and flags. */
+	psf_binheader_writef (psf, "Em22", BHWm (caff_MARKER), BHW2 (1), BHW2 (0)) ;
+
+	/* 'desc' marker and chunk size. */
+	psf_binheader_writef (psf, "Em8", BHWm (desc_MARKER), BHW8 ((sf_count_t) (sizeof (DESC_CHUNK)))) ;
+
+ 	double64_be_write (1.0 * psf->sf.samplerate, ubuf.ucbuf) ;
+	psf_binheader_writef (psf, "b", BHWv (ubuf.ucbuf), BHWz (8)) ;
+
+	subformat = SF_CODEC (psf->sf.format) ;
+
+	psf->endian = SF_ENDIAN (psf->sf.format) ;
+
+	if (CPU_IS_BIG_ENDIAN && (psf->endian == 0 || psf->endian == SF_ENDIAN_CPU))
+		psf->endian = SF_ENDIAN_BIG ;
+	else if (CPU_IS_LITTLE_ENDIAN && (psf->endian == SF_ENDIAN_LITTLE || psf->endian == SF_ENDIAN_CPU))
+		psf->endian = SF_ENDIAN_LITTLE ;
+
+	if (psf->endian == SF_ENDIAN_LITTLE)
+		desc.fmt_flags = 2 ;
+	else
+		psf->endian = SF_ENDIAN_BIG ;
+
+	/* initial section (same for all, it appears) */
+	switch (subformat)
+	{	case SF_FORMAT_PCM_S8 :
+			desc.fmt_id = lpcm_MARKER ;
+			psf->bytewidth = 1 ;
+			desc.pkt_bytes = psf->bytewidth * psf->sf.channels ;
+			desc.frames_per_packet = 1 ;
+			desc.channels_per_frame = psf->sf.channels ;
+			desc.bits_per_chan = 8 ;
+			break ;
+
+		case SF_FORMAT_PCM_16 :
+			desc.fmt_id = lpcm_MARKER ;
+			psf->bytewidth = 2 ;
+			desc.pkt_bytes = psf->bytewidth * psf->sf.channels ;
+			desc.frames_per_packet = 1 ;
+			desc.channels_per_frame = psf->sf.channels ;
+			desc.bits_per_chan = 16 ;
+			break ;
+
+		case SF_FORMAT_PCM_24 :
+			psf->bytewidth = 3 ;
+			desc.pkt_bytes = psf->bytewidth * psf->sf.channels ;
+			desc.frames_per_packet = 1 ;
+			desc.channels_per_frame = psf->sf.channels ;
+			desc.bits_per_chan = 24 ;
+			desc.fmt_id = lpcm_MARKER ;
+			break ;
+
+		case SF_FORMAT_PCM_32 :
+			desc.fmt_id = lpcm_MARKER ;
+			psf->bytewidth = 4 ;
+			desc.pkt_bytes = psf->bytewidth * psf->sf.channels ;
+			desc.frames_per_packet = 1 ;
+			desc.channels_per_frame = psf->sf.channels ;
+			desc.bits_per_chan = 32 ;
+			break ;
+
+		case SF_FORMAT_FLOAT :
+			desc.fmt_id = lpcm_MARKER ;
+			desc.fmt_flags |= 1 ;
+			psf->bytewidth = 4 ;
+			desc.pkt_bytes = psf->bytewidth * psf->sf.channels ;
+			desc.frames_per_packet = 1 ;
+			desc.channels_per_frame = psf->sf.channels ;
+			desc.bits_per_chan = 32 ;
+			break ;
+
+		case SF_FORMAT_DOUBLE :
+			desc.fmt_id = lpcm_MARKER ;
+			desc.fmt_flags |= 1 ;
+			psf->bytewidth = 8 ;
+			desc.pkt_bytes = psf->bytewidth * psf->sf.channels ;
+			desc.frames_per_packet = 1 ;
+			desc.channels_per_frame = psf->sf.channels ;
+			desc.bits_per_chan = 64 ;
+			break ;
+
+		case SF_FORMAT_ALAW :
+			desc.fmt_id = alaw_MARKER ;
+			psf->bytewidth = 1 ;
+			desc.pkt_bytes = psf->bytewidth * psf->sf.channels ;
+			desc.frames_per_packet = 1 ;
+			desc.channels_per_frame = psf->sf.channels ;
+			desc.bits_per_chan = 8 ;
+			break ;
+
+		case SF_FORMAT_ULAW :
+			desc.fmt_id = ulaw_MARKER ;
+			psf->bytewidth = 1 ;
+			desc.pkt_bytes = psf->bytewidth * psf->sf.channels ;
+			desc.frames_per_packet = 1 ;
+			desc.channels_per_frame = psf->sf.channels ;
+			desc.bits_per_chan = 8 ;
+			break ;
+
+		case SF_FORMAT_ALAC_16 :
+		case SF_FORMAT_ALAC_20 :
+		case SF_FORMAT_ALAC_24 :
+		case SF_FORMAT_ALAC_32 :
+			desc.fmt_id = alac_MARKER ;
+			desc.pkt_bytes = psf->bytewidth * psf->sf.channels ;
+			desc.channels_per_frame = psf->sf.channels ;
+			alac_get_desc_chunk_items (subformat, &desc.fmt_flags, &desc.frames_per_packet) ;
+			append_free_block = SF_FALSE ;
+			break ;
+
+		default :
+			return SFE_UNIMPLEMENTED ;
+		} ;
+
+	psf_binheader_writef (psf, "mE44444", BHWm (desc.fmt_id), BHW4 (desc.fmt_flags), BHW4 (desc.pkt_bytes), BHW4 (desc.frames_per_packet), BHW4 (desc.channels_per_frame), BHW4 (desc.bits_per_chan)) ;
+
+	caf_write_strings (psf, SF_STR_LOCATE_START) ;
+
+	if (psf->peak_info != NULL)
+	{	int k ;
+		psf_binheader_writef (psf, "Em84", BHWm (peak_MARKER), BHW8 ((sf_count_t) CAF_PEAK_CHUNK_SIZE (psf->sf.channels)), BHW4 (psf->peak_info->edit_number)) ;
+		for (k = 0 ; k < psf->sf.channels ; k++)
+			psf_binheader_writef (psf, "Ef8", BHWf ((float) psf->peak_info->peaks [k].value), BHW8 (psf->peak_info->peaks [k].position)) ;
+		} ;
+
+	if (psf->channel_map && pcaf->chanmap_tag)
+		psf_binheader_writef (psf, "Em8444", BHWm (chan_MARKER), BHW8 ((sf_count_t) 12), BHW4 (pcaf->chanmap_tag), BHW4 (0), BHW4 (0)) ;
+
+	/* Write custom headers. */
+	for (uk = 0 ; uk < psf->wchunks.used ; uk++)
+		psf_binheader_writef (psf, "m44b", BHWm ((int) psf->wchunks.chunks [uk].mark32), BHW4 (0), BHW4 (psf->wchunks.chunks [uk].len), BHWv (psf->wchunks.chunks [uk].data), BHWz (psf->wchunks.chunks [uk].len)) ;
+
+	if (append_free_block)
+	{	/* Add free chunk so that the actual audio data starts at a multiple 0x1000. */
+		sf_count_t free_len = 0x1000 - psf->header.indx - 16 - 12 ;
+		while (free_len < 0)
+			free_len += 0x1000 ;
+		psf_binheader_writef (psf, "Em8z", BHWm (free_MARKER), BHW8 (free_len), BHWz (free_len)) ;
+		} ;
+
+	psf_binheader_writef (psf, "Em84", BHWm (data_MARKER), BHW8 (psf->datalength + 4), BHW4 (0)) ;
+
+	psf_fwrite (psf->header.ptr, psf->header.indx, 1, psf) ;
+	if (psf->error)
+		return psf->error ;
+
+	psf->dataoffset = psf->header.indx ;
+	if (current < psf->dataoffset)
+		psf_fseek (psf, psf->dataoffset, SEEK_SET) ;
+	else if (current > 0)
+		psf_fseek (psf, current, SEEK_SET) ;
+
+	return psf->error ;
+} /* caf_write_header */
+
+static int
+caf_write_tailer (SF_PRIVATE *psf)
+{
+	/* Reset the current header buffer length to zero. */
+	psf->header.ptr [0] = 0 ;
+	psf->header.indx = 0 ;
+
+	if (psf->bytewidth > 0 && psf->sf.seekable == SF_TRUE)
+	{	psf->datalength = psf->sf.frames * psf->bytewidth * psf->sf.channels ;
+		psf->dataend = psf->dataoffset + psf->datalength ;
+		} ;
+
+	if (psf->dataend > 0)
+		psf_fseek (psf, psf->dataend, SEEK_SET) ;
+	else
+		psf->dataend = psf_fseek (psf, 0, SEEK_END) ;
+
+	if (psf->dataend & 1)
+		psf_binheader_writef (psf, "z", BHWz (1)) ;
+
+	if (psf->strings.flags & SF_STR_LOCATE_END)
+		caf_write_strings (psf, SF_STR_LOCATE_END) ;
+
+	/* Write the tailer. */
+	if (psf->header.indx > 0)
+		psf_fwrite (psf->header.ptr, psf->header.indx, 1, psf) ;
+
+	return 0 ;
+} /* caf_write_tailer */
+
+static int
+caf_read_chanmap (SF_PRIVATE * psf, sf_count_t chunk_size)
+{	const AIFF_CAF_CHANNEL_MAP * map_info ;
+	unsigned channel_bitmap, channel_decriptions, bytesread ;
+	int layout_tag ;
+
+	bytesread = psf_binheader_readf (psf, "E444", &layout_tag, &channel_bitmap, &channel_decriptions) ;
+
+	map_info = aiff_caf_of_channel_layout_tag (layout_tag) ;
+
+	psf_log_printf (psf, "  Tag    : %x\n", layout_tag) ;
+	if (map_info)
+		psf_log_printf (psf, "  Layout : %s\n", map_info->name) ;
+
+	if (bytesread < chunk_size)
+		psf_binheader_readf (psf, "j", chunk_size - bytesread) ;
+
+	if (map_info && map_info->channel_map != NULL)
+	{	size_t chanmap_size = SF_MIN (psf->sf.channels, layout_tag & 0xff) * sizeof (psf->channel_map [0]) ;
+
+		free (psf->channel_map) ;
+
+		if ((psf->channel_map = malloc (chanmap_size)) == NULL)
+			return SFE_MALLOC_FAILED ;
+
+		memcpy (psf->channel_map, map_info->channel_map, chanmap_size) ;
+		} ;
+
+	return 0 ;
+} /* caf_read_chanmap */
+
+
+static uint32_t
+string_hash32 (const char * str)
+{	uint32_t hash = 0x87654321 ;
+
+	while (str [0])
+	{	hash = hash * 333 + str [0] ;
+		str ++ ;
+		} ;
+
+	return hash ;
+} /* string_hash32 */
+
+static int
+caf_read_strings (SF_PRIVATE * psf, sf_count_t chunk_size)
+{	char *buf ;
+	char *key, *value ;
+	uint32_t count, hash ;
+
+	if ((buf = malloc (chunk_size + 1)) == NULL)
+		return (psf->error = SFE_MALLOC_FAILED) ;
+
+	psf_binheader_readf (psf, "E4b", &count, buf, (size_t) chunk_size) ;
+	psf_log_printf (psf, " count: %u\n", count) ;
+
+	/* Force terminate `buf` to make sure. */
+	buf [chunk_size] = 0 ;
+
+	for (key = buf ; key < buf + chunk_size ; )
+	{	value = key + strlen (key) + 1 ;
+		if (value > buf + chunk_size)
+			break ;
+		psf_log_printf (psf, "   %-12s : %s\n", key, value) ;
+
+		hash = string_hash32 (key) ;
+		switch (hash)
+		{	case 0xC4861943 : /* 'title' */
+				psf_store_string (psf, SF_STR_TITLE, value) ;
+				break ;
+			case 0xAD47A394 : /* 'software' */
+				psf_store_string (psf, SF_STR_SOFTWARE, value) ;
+				break ;
+			case 0x5D178E2A : /* 'copyright' */
+				psf_store_string (psf, SF_STR_COPYRIGHT, value) ;
+				break ;
+			case 0x60E4D0C8 : /* 'artist' */
+				psf_store_string (psf, SF_STR_ARTIST, value) ;
+				break ;
+			case 0x83B5D16A : /* 'genre' */
+				psf_store_string (psf, SF_STR_GENRE, value) ;
+				break ;
+			case 0x15E5FC88 : /* 'comment' */
+			case 0x7C297D5B : /* 'comments' */
+				psf_store_string (psf, SF_STR_COMMENT, value) ;
+				break ;
+			case 0x24A7C347 : /* 'tracknumber' */
+				psf_store_string (psf, SF_STR_TRACKNUMBER, value) ;
+				break ;
+			case 0x50A31EB7 : /* 'date' */
+				psf_store_string (psf, SF_STR_DATE, value) ;
+				break ;
+			case 0x6583545A : /* 'album' */
+				psf_store_string (psf, SF_STR_ALBUM, value) ;
+				break ;
+			case 0xE7C64B6C : /* 'license' */
+				psf_store_string (psf, SF_STR_LICENSE, value) ;
+				break ;
+			default :
+				psf_log_printf (psf, " Unhandled hash 0x%x : /* '%s' */\n", hash, key) ;
+				break ;
+			} ;
+
+		key = value + strlen (value) + 1 ;
+		} ;
+
+	free (buf) ;
+
+	return 0 ;
+} /* caf_read_strings */
+
+struct put_buffer
+{	uint32_t index ;
+	char s [16 * 1024] ;
+} ;
+
+static uint32_t
+put_key_value (struct put_buffer * buf, const char * key, const char * value)
+{	uint32_t written ;
+
+	if (buf->index + strlen (key) + strlen (value) + 2 > sizeof (buf->s))
+		return 0 ;
+
+	written = snprintf (buf->s + buf->index, sizeof (buf->s) - buf->index, "%s%c%s%c", key, 0, value, 0) ;
+
+	if (buf->index + written >= sizeof (buf->s))
+		return 0 ;
+
+	buf->index += written ;
+	return 1 ;
+} /* put_key_value */
+
+static void
+caf_write_strings (SF_PRIVATE * psf, int location)
+{	struct put_buffer buf ;
+ 	const char * cptr ;
+	uint32_t k, string_count = 0 ;
+
+	memset (&buf, 0, sizeof (buf)) ;
+
+	for (k = 0 ; k < SF_MAX_STRINGS ; k++)
+	{	if (psf->strings.data [k].type == 0)
+			break ;
+
+		if (psf->strings.data [k].flags != location)
+			continue ;
+
+		if ((cptr = psf_get_string (psf, psf->strings.data [k].type)) == NULL)
+			continue ;
+
+		switch (psf->strings.data [k].type)
+		{	case SF_STR_TITLE :
+				string_count += put_key_value (&buf, "title", cptr) ;
+				break ;
+			case SF_STR_COPYRIGHT :
+				string_count += put_key_value (&buf, "copyright", cptr) ;
+				break ;
+			case SF_STR_SOFTWARE :
+				string_count += put_key_value (&buf, "software", cptr) ;
+				break ;
+			case SF_STR_ARTIST :
+				string_count += put_key_value (&buf, "artist", cptr) ;
+				break ;
+			case SF_STR_COMMENT :
+				string_count += put_key_value (&buf, "comment", cptr) ;
+				break ;
+			case SF_STR_DATE :
+				string_count += put_key_value (&buf, "date", cptr) ;
+				break ;
+			case SF_STR_ALBUM :
+				string_count += put_key_value (&buf, "album", cptr) ;
+				break ;
+			case SF_STR_LICENSE :
+				string_count += put_key_value (&buf, "license", cptr) ;
+				break ;
+			case SF_STR_TRACKNUMBER :
+				string_count += put_key_value (&buf, "tracknumber", cptr) ;
+				break ;
+			case SF_STR_GENRE :
+				string_count += put_key_value (&buf, "genre", cptr) ;
+				break ;
+
+			default :
+				break ;
+			} ;
+		} ;
+
+	if (string_count == 0 || buf.index == 0)
+		return ;
+
+	psf_binheader_writef (psf, "Em84b", BHWm (info_MARKER), BHW8 (buf.index + 4), BHW4 (string_count), BHWv (buf.s), BHWz (buf.index)) ;
+} /* caf_write_strings */
+
+/*==============================================================================
+*/
+
+static int
+caf_set_chunk (SF_PRIVATE *psf, const SF_CHUNK_INFO * chunk_info)
+{	return psf_save_write_chunk (&psf->wchunks, chunk_info) ;
+} /* caf_set_chunk */
+
+static SF_CHUNK_ITERATOR *
+caf_next_chunk_iterator (SF_PRIVATE *psf, SF_CHUNK_ITERATOR * iterator)
+{	return psf_next_chunk_iterator (&psf->rchunks, iterator) ;
+} /* caf_next_chunk_iterator */
+
+static int
+caf_get_chunk_size (SF_PRIVATE *psf, const SF_CHUNK_ITERATOR * iterator, SF_CHUNK_INFO * chunk_info)
+{	int indx ;
+
+	if ((indx = psf_find_read_chunk_iterator (&psf->rchunks, iterator)) < 0)
+		return SFE_UNKNOWN_CHUNK ;
+
+	chunk_info->datalen = psf->rchunks.chunks [indx].len ;
+
+	return SFE_NO_ERROR ;
+} /* caf_get_chunk_size */
+
+static int
+caf_get_chunk_data (SF_PRIVATE *psf, const SF_CHUNK_ITERATOR * iterator, SF_CHUNK_INFO * chunk_info)
+{	int indx ;
+	sf_count_t pos ;
+
+	if ((indx = psf_find_read_chunk_iterator (&psf->rchunks, iterator)) < 0)
+		return SFE_UNKNOWN_CHUNK ;
+
+	if (chunk_info->data == NULL)
+		return SFE_BAD_CHUNK_DATA_PTR ;
+
+	chunk_info->id_size = psf->rchunks.chunks [indx].id_size ;
+	memcpy (chunk_info->id, psf->rchunks.chunks [indx].id, sizeof (chunk_info->id) / sizeof (*chunk_info->id)) ;
+
+	pos = psf_ftell (psf) ;
+	psf_fseek (psf, psf->rchunks.chunks [indx].offset, SEEK_SET) ;
+	psf_fread (chunk_info->data, SF_MIN (chunk_info->datalen, psf->rchunks.chunks [indx].len), 1, psf) ;
+	psf_fseek (psf, pos, SEEK_SET) ;
+
+	return SFE_NO_ERROR ;
+} /* caf_get_chunk_data */
diff --git a/src/cart.c b/src/cart.c
new file mode 100644
index 0000000..8b00eab
--- /dev/null
+++ b/src/cart.c
@@ -0,0 +1,101 @@
+/*
+** Copyright (C) 2012 Chris Roberts <c.roberts@csrfm.com>
+** Copyright (C) 2006-2013 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2006 Paul Davis <paul@linuxaudiosystems.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+#include "common.h"
+
+
+
+static inline size_t
+cart_min_size (const SF_CART_INFO* info)
+{	if (info == NULL)
+		return 0 ;
+
+	return offsetof (SF_CART_INFO, tag_text) + info->tag_text_size ;
+} /* cart_min_size */
+
+SF_CART_INFO_16K*
+cart_var_alloc (void)
+{	SF_CART_INFO_16K* thing ;
+	thing = malloc (sizeof (SF_CART_INFO_16K)) ;
+	return thing ;
+} /* cart_var_alloc */
+
+int
+cart_var_set (SF_PRIVATE *psf, const SF_CART_INFO * info, size_t datasize)
+{	size_t len ;
+
+	if (info == NULL)
+		return SF_FALSE ;
+
+	if (cart_min_size (info) > datasize)
+	{	psf->error = SFE_BAD_CART_INFO_SIZE ;
+		return SF_FALSE ;
+		} ;
+
+	if (datasize >= sizeof (SF_CART_INFO_16K))
+	{	psf->error = SFE_BAD_CART_INFO_TOO_BIG ;
+		return SF_FALSE ;
+		} ;
+
+	if (psf->cart_16k == NULL)
+	{	if ((psf->cart_16k = cart_var_alloc ()) == NULL)
+		{	psf->error = SFE_MALLOC_FAILED ;
+			return SF_FALSE ;
+			} ;
+		} ;
+
+	memcpy (psf->cart_16k, info, offsetof (SF_CART_INFO, tag_text)) ;
+	psf_strlcpy_crlf (psf->cart_16k->tag_text, info->tag_text, sizeof (psf->cart_16k->tag_text), datasize - offsetof (SF_CART_INFO, tag_text)) ;
+
+	len = strlen (psf->cart_16k->tag_text) ;
+
+	if (len > 0 && psf->cart_16k->tag_text [len - 1] != '\n')
+		psf_strlcat (psf->cart_16k->tag_text, sizeof (psf->cart_16k->tag_text), "\r\n") ;
+
+	/* Force tag_text_size to be even. */
+	len = strlen (psf->cart_16k->tag_text) ;
+	len += (len & 1) ? 1 : 2 ;
+
+	psf->cart_16k->tag_text_size = (uint32_t) len ;
+
+	return SF_TRUE ;
+} /* cart_var_set */
+
+
+int
+cart_var_get (SF_PRIVATE *psf, SF_CART_INFO * data, size_t datasize)
+{	size_t size ;
+	if (psf->cart_16k == NULL)
+		return SF_FALSE ;
+
+	size = SF_MIN (datasize, cart_min_size ((const SF_CART_INFO *) psf->cart_16k)) ;
+
+	memcpy (data, psf->cart_16k, size) ;
+
+	return SF_TRUE ;
+} /* cart_var_get */
+
+
diff --git a/src/chanmap.c b/src/chanmap.c
new file mode 100644
index 0000000..c06702c
--- /dev/null
+++ b/src/chanmap.c
@@ -0,0 +1,262 @@
+/*
+** Copyright (C) 2009-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*
+**	Mostly from "Apple Core Audio Format Specification 1.0":
+**
+**		http://developer.apple.com/documentation/MusicAudio/Reference/CAFSpec/CAFSpec.pdf
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "sndfile.h"
+#include "common.h"
+#include "chanmap.h"
+
+
+static const AIFF_CAF_CHANNEL_MAP zero_chan [] =
+{	{	(0 << 16) | 0, NULL, "Use channel descriptions." },
+	{	(1 << 16) | 0, NULL, "Use channel bitmap." }
+} ; /* zero_chan */
+
+
+static const int one_chan_mono [1] = {	SF_CHANNEL_MAP_MONO } ;
+
+static const AIFF_CAF_CHANNEL_MAP one_chan [] =
+{	{	(100 << 16) | 1, one_chan_mono, "mono" }
+} ; /* one_chan */
+
+
+static const int two_channel_stereo [2] = {	SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT } ;
+
+static const AIFF_CAF_CHANNEL_MAP two_chan [] =
+{	{	(101 << 16) | 2, two_channel_stereo, "stereo (L, R)" },
+	{	(102 << 16) | 2, two_channel_stereo, "stereo headphones (L, R)" },
+#if 0
+	{	(103 << 16) | 2, NULL, "matrix stereo (Lt, Rt)" },
+	{	(104 << 16) | 2, NULL, "2 channels (mid, side)" },
+	{	(105 << 16) | 2, NULL, "coincident mic pair" },
+	{	(106 << 16) | 2, NULL, "binaural stereo (L, R)"
+		}
+#endif
+} ; /* two_chan */
+
+
+static const int three_channel_mpeg_30a [3] =
+	{	SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT, SF_CHANNEL_MAP_CENTER } ;
+static const int three_channel_mpeg_30b [3] =
+	{	SF_CHANNEL_MAP_CENTER, SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT } ;
+static const int three_channel_itu_21 [3] =
+	{	SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT, SF_CHANNEL_MAP_REAR_CENTER } ;
+static const int three_channel_dvd_4 [3] =
+	{	SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT, SF_CHANNEL_MAP_LFE } ;
+
+static const AIFF_CAF_CHANNEL_MAP three_chan [] =
+{	{	(113 << 16) | 3, three_channel_mpeg_30a, "MPEG 3 0 A (L, R, C)" },
+	{	(114 << 16) | 3, three_channel_mpeg_30b, "MPEG 3 0 B (C, L, R)" },
+	{	(131 << 16) | 3, three_channel_itu_21, "ITU 2.1 (L, R, Cs)" },
+	{	(133 << 16) | 3, three_channel_dvd_4, "DVD 4 (L, R, LFE)" }
+} ; /* three_chan */
+
+
+static const int four_channel_ambisonc_b [4] =
+	{	SF_CHANNEL_MAP_AMBISONIC_B_W, SF_CHANNEL_MAP_AMBISONIC_B_X, SF_CHANNEL_MAP_AMBISONIC_B_Y, SF_CHANNEL_MAP_AMBISONIC_B_Z } ;
+static const int four_channel_quad [4] =
+	{	SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT, SF_CHANNEL_MAP_REAR_LEFT, SF_CHANNEL_MAP_REAR_RIGHT } ;
+static const int four_channel_mpeg_40a [4] =
+	{	SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT, SF_CHANNEL_MAP_CENTER, SF_CHANNEL_MAP_REAR_CENTER } ;
+static const int four_channel_mpeg_40b [4] =
+	{	SF_CHANNEL_MAP_CENTER, SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT, SF_CHANNEL_MAP_REAR_CENTER } ;
+static const int four_channel_itu_23 [4] =
+	{	SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT, SF_CHANNEL_MAP_REAR_LEFT, SF_CHANNEL_MAP_REAR_RIGHT } ;
+static const int four_channel_dvd_5 [4] =
+	{	SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT, SF_CHANNEL_MAP_LFE, SF_CHANNEL_MAP_REAR_CENTER } ;
+static const int four_channel_dvd_10 [4] =
+	{	SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT, SF_CHANNEL_MAP_CENTER, SF_CHANNEL_MAP_LFE } ;
+
+static const AIFF_CAF_CHANNEL_MAP four_chan [] =
+{	{	(107 << 16) | 4, four_channel_ambisonc_b, "ambisonic B (W,  X, Y, Z)" },
+	{	(108 << 16) | 4, four_channel_quad, "quad (Lfront, Rfront, Lrear, Rrear)" },
+	{	(115 << 16) | 4, four_channel_mpeg_40a, "MPEG 4.0 A (L, R, C, Cs)" },
+	{	(116 << 16) | 4, four_channel_mpeg_40b, "MPEG 4.0 B (C, L, R, Cs)" },
+	{	(132 << 16) | 4, four_channel_itu_23, "ITU 2.3 (L, R, Ls, Rs)" },
+	{	(134 << 16) | 4, four_channel_dvd_5, "DVD 5 (L, R, LFE, Cs)" },
+	{	(136 << 16) | 4, four_channel_dvd_10, "DVD 10 (L, R, C, LFE)" }
+} ; /* four_chan */
+
+
+static const int five_channel_pentagonal [5] =
+	{	SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT, SF_CHANNEL_MAP_REAR_LEFT, SF_CHANNEL_MAP_REAR_RIGHT, SF_CHANNEL_MAP_CENTER } ;
+static const int five_channel_mpeg_50_a [5] =
+	{	SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT, SF_CHANNEL_MAP_CENTER, SF_CHANNEL_MAP_REAR_LEFT, SF_CHANNEL_MAP_REAR_RIGHT } ;
+static const int five_channel_mpeg_50_b [5] =
+	{	SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT, SF_CHANNEL_MAP_REAR_LEFT, SF_CHANNEL_MAP_REAR_RIGHT, SF_CHANNEL_MAP_CENTER } ;
+static const int five_channel_mpeg_50_c [5] =
+	{	SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_CENTER, SF_CHANNEL_MAP_RIGHT, SF_CHANNEL_MAP_REAR_LEFT, SF_CHANNEL_MAP_REAR_RIGHT } ;
+static const int five_channel_mpeg_50_d [5] =
+	{	SF_CHANNEL_MAP_CENTER, SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT, SF_CHANNEL_MAP_REAR_LEFT, SF_CHANNEL_MAP_REAR_RIGHT } ;
+static const int five_channel_dvd_6 [5] =
+	{	SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT, SF_CHANNEL_MAP_LFE, SF_CHANNEL_MAP_REAR_LEFT, SF_CHANNEL_MAP_REAR_RIGHT } ;
+static const int five_channel_dvd_11 [5] =
+	{	SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT, SF_CHANNEL_MAP_CENTER, SF_CHANNEL_MAP_LFE, SF_CHANNEL_MAP_REAR_CENTER } ;
+static const int five_channel_dvd_18 [5] =
+	{	SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT, SF_CHANNEL_MAP_REAR_LEFT, SF_CHANNEL_MAP_REAR_RIGHT, SF_CHANNEL_MAP_LFE } ;
+
+static const AIFF_CAF_CHANNEL_MAP five_chan [] =
+{	{	(109 << 16) | 5, five_channel_pentagonal, "pentagonal (L,  R, Lrear, Rrear, C)" },
+	{	(117 << 16) | 5, five_channel_mpeg_50_a, "MPEG 5.0 A (L, R, C, Ls, Rs)" },
+	{	(118 << 16) | 5, five_channel_mpeg_50_b, "MPEG 5.0 B (L, R, Ls, Rs, C)" },
+	{	(119 << 16) | 5, five_channel_mpeg_50_c, "MPEG 5.0 C (L, C, R, Ls, Rs,)" },
+	{	(120 << 16) | 5, five_channel_mpeg_50_d, "MPEG 5.0 D (C, L, R, Ls, Rs)" },
+	{	(135 << 16) | 5, five_channel_dvd_6, "DVD 6 (L, R, LFE, Ls, Rs)" },
+	{	(137 << 16) | 5, five_channel_dvd_11, "DVD 11 (L, R, C, LFE, Cs)" },
+	{	(138 << 16) | 5, five_channel_dvd_18, "DVD 18 (L, R, Ls, Rs, LFE)" }
+} ; /* five_chan */
+
+
+static const int six_channel_mpeg_51_a [6] =
+	{	SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT, SF_CHANNEL_MAP_CENTER, SF_CHANNEL_MAP_LFE, SF_CHANNEL_MAP_REAR_LEFT, SF_CHANNEL_MAP_REAR_RIGHT } ;
+static const int six_channel_mpeg_51_b [6] =
+	{	SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT, SF_CHANNEL_MAP_REAR_LEFT, SF_CHANNEL_MAP_REAR_RIGHT, SF_CHANNEL_MAP_CENTER, SF_CHANNEL_MAP_LFE } ;
+static const int six_channel_mpeg_51_c [6] =
+	{	SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_CENTER, SF_CHANNEL_MAP_RIGHT, SF_CHANNEL_MAP_REAR_LEFT, SF_CHANNEL_MAP_REAR_RIGHT, SF_CHANNEL_MAP_LFE } ;
+static const int six_channel_mpeg_51_d [6] =
+	{	SF_CHANNEL_MAP_CENTER, SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT, SF_CHANNEL_MAP_REAR_LEFT, SF_CHANNEL_MAP_REAR_RIGHT, SF_CHANNEL_MAP_LFE } ;
+static const int six_channel_audio_unit_60 [6] =
+	{	SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT, SF_CHANNEL_MAP_REAR_LEFT, SF_CHANNEL_MAP_REAR_RIGHT, SF_CHANNEL_MAP_CENTER, SF_CHANNEL_MAP_REAR_CENTER } ;
+static const int six_channel_aac_60 [6] =
+	{	SF_CHANNEL_MAP_CENTER, SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT, SF_CHANNEL_MAP_REAR_LEFT, SF_CHANNEL_MAP_REAR_RIGHT, SF_CHANNEL_MAP_REAR_CENTER } ;
+
+static const AIFF_CAF_CHANNEL_MAP six_chan [] =
+{	{	(110 << 16) | 6, NULL, "hexagonal (L, R, Lr, Rr, C, Rear)" },
+	{	(121 << 16) | 6, six_channel_mpeg_51_a, "MPEG 5.1 A (L, R, C, LFE, Ls, Rs)" },
+	{	(122 << 16) | 6, six_channel_mpeg_51_b, "MPEG 5.1 B (L, R, Ls, Rs, C, LFE)" },
+	{	(123 << 16) | 6, six_channel_mpeg_51_c, "MPEG 5.1 C (L, C, R, Ls, Rs, LFE)" },
+	{	(124 << 16) | 6, six_channel_mpeg_51_d, "MPEG 5.1 D (C, L, R, Ls, Rs, LFE)" },
+	{	(139 << 16) | 6, six_channel_audio_unit_60, "AudioUnit 6.0 (L, R, Ls, Rs, C, Cs)" },
+	{	(141 << 16) | 6, six_channel_aac_60, "AAC 6.0 (C, L, R, Ls, Rs, Cs)" }
+} ; /* six_chan */
+
+
+static const int six_channel_mpeg_61a [7] =
+	{	SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT, SF_CHANNEL_MAP_CENTER, SF_CHANNEL_MAP_LFE, SF_CHANNEL_MAP_REAR_LEFT, SF_CHANNEL_MAP_REAR_RIGHT, SF_CHANNEL_MAP_REAR_CENTER } ;
+static const int six_channel_aac_61 [7] =
+	{	SF_CHANNEL_MAP_CENTER, SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT, SF_CHANNEL_MAP_REAR_LEFT, SF_CHANNEL_MAP_REAR_RIGHT, SF_CHANNEL_MAP_REAR_CENTER, SF_CHANNEL_MAP_LFE } ;
+
+static const AIFF_CAF_CHANNEL_MAP seven_chan [] =
+{	{	(125 << 16) | 7, six_channel_mpeg_61a, "MPEG 6.1 A (L, R, C, LFE, Ls, Rs, Cs)" },
+	{	(140 << 16) | 7, NULL, "AudioUnit 7.0 (L, R, Ls, Rs, C, Rls, Rrs)" },
+	{	(142 << 16) | 7, six_channel_aac_61, "AAC 6.1 (C, L, R, Ls, Rs, Cs, Lfe)" },
+	{	(143 << 16) | 7, NULL, "AAC 7.0 (C, L, R, Ls, Rs, Rls, Rrs,)" }
+} ; /* seven_chan */
+
+
+static const AIFF_CAF_CHANNEL_MAP eight_chan [] =
+{	{	(111 << 16) | 8, NULL,
+		// front left, front right, rear left, rear right,
+		// front center, rear center, side left, side right
+		"octagonal (Lf, Rf, Lr, Rr, Cf, Cr, Ls, Rs)"
+		},
+	{	(112 << 16) | 8, NULL,
+		// left, right, rear left, rear right
+		// top left, top right, top rear left, top rear right
+		"cube (L, R, Lrear, Rrear, Ltop, Rtop, Ltoprear, Rtoprear)"
+		},
+	{	(126 << 16) | 8, NULL, "MPEG 7.1 A (L, R, C, LFE, Ls, Rs, Lc, Rc)" },
+	{	(127 << 16) | 8, NULL, "MPEG 7.1 B (C, Lc, Rc, L, R, Ls, Rs, LFE)" },
+	{	(128 << 16) | 8, NULL, "MPEG 7.1 C (L, R, C, LFE, Ls, R, Rls, Rrs)" },
+	{	(129 << 16) | 8, NULL, "Emagic Default 7.1 (L, R, Ls, Rs, C, LFE, Lc, Rc)" },
+	{	(130 << 16) | 8, NULL,
+		// (ITU_5_1 plus a matrix encoded stereo mix)
+		"SMPTE DTV (L, R, C, LFE, Ls, Rs, Lt, Rt)"
+		},
+	{	(144 << 16) | 8, NULL, "AAC octagonal (C, L, R, Ls, Rs, Rls, Rrs, Cs)" }
+} ; /* eight_chan */
+
+
+
+#if 0
+
+TMH_10_2_std = (145 << 16) | 16,
+// L R C Vhc Lsd Rsd Ls Rs Vhl Vhr Lw Rw Csd Cs LFE1 LFE2
+
+TMH_10_2_full = (146 << 16) | 21,
+// TMH_10_2_std plus: Lc Rc HI VI Haptic
+
+#endif
+
+
+typedef struct
+{	const AIFF_CAF_CHANNEL_MAP * map ;
+	int len ;
+} MAP_MAP ;
+
+static const MAP_MAP map [] =
+{	{ zero_chan,	ARRAY_LEN (zero_chan) },
+	{ one_chan,		ARRAY_LEN (one_chan) },
+	{ two_chan,		ARRAY_LEN (two_chan) },
+	{ three_chan,	ARRAY_LEN (three_chan) },
+	{ four_chan,	ARRAY_LEN (four_chan) },
+	{ five_chan,	ARRAY_LEN (five_chan) },
+	{ six_chan,		ARRAY_LEN (six_chan) },
+	{ seven_chan,	ARRAY_LEN (seven_chan) },
+	{ eight_chan,	ARRAY_LEN (eight_chan) }
+} ; /* map */
+
+
+int
+aiff_caf_find_channel_layout_tag (const int *chan_map, int channels)
+{	const AIFF_CAF_CHANNEL_MAP * curr_map ;
+	unsigned k, len ;
+
+	if (channels < 1 || channels >= ARRAY_LEN (map))
+		return 0 ;
+
+	curr_map = map [channels].map ;
+	len = map [channels].len ;
+
+	for (k = 0 ; k < len ; k++)
+		if (curr_map [k].channel_map != NULL)
+			if (memcmp (chan_map, curr_map [k].channel_map, channels * sizeof (chan_map [0])) == 0)
+				return curr_map [k].channel_layout_tag ;
+
+	return 0 ;
+} /* aiff_caf_find_channel_layout_tag */
+
+const AIFF_CAF_CHANNEL_MAP *
+aiff_caf_of_channel_layout_tag (int tag)
+{	const AIFF_CAF_CHANNEL_MAP * curr_map ;
+	unsigned k, len ;
+	int channels = tag & 0xffff ;
+
+	if (channels < 0 || channels >= ARRAY_LEN (map))
+		return NULL ;
+
+	curr_map = map [channels].map ;
+	len = map [channels].len ;
+
+	for (k = 0 ; k < len ; k++)
+		if (curr_map [k].channel_layout_tag == tag)
+			return curr_map + k ;
+
+	return NULL ;
+} /* aiff_caf_of_channel_layout_tag */
diff --git a/src/chanmap.h b/src/chanmap.h
new file mode 100644
index 0000000..8af409d
--- /dev/null
+++ b/src/chanmap.h
@@ -0,0 +1,32 @@
+/*
+** Copyright (C) 2009-2011 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+typedef struct
+{	/* The tag in the AIFF or CAF file. */
+	int channel_layout_tag ;
+
+	/* The equivalent array of SF_CHANNEL_MAP_* entries. */
+	const int * channel_map ;
+
+	const char * name ;
+} AIFF_CAF_CHANNEL_MAP ;
+
+
+int aiff_caf_find_channel_layout_tag (const int *chan_map, int channels) ;
+
+const AIFF_CAF_CHANNEL_MAP * aiff_caf_of_channel_layout_tag (int tag) ;
diff --git a/src/chunk.c b/src/chunk.c
new file mode 100644
index 0000000..9b181fc
--- /dev/null
+++ b/src/chunk.c
@@ -0,0 +1,266 @@
+/*
+** Copyright (C) 2008-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2012 IOhannes m zmoelnig, IEM <zmoelnig@iem.at>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdlib.h>
+#include	<string.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+static int64_t
+hash_of_str (const char * str)
+{	int64_t marker = 0 ;
+	int k ;
+
+	for (k = 0 ; str [k] ; k++)
+		marker = marker * 0x7f + ((const uint8_t *) str) [k] ;
+
+	return marker ;
+} /* hash_of_str */
+
+SF_CHUNK_ITERATOR *
+psf_get_chunk_iterator (SF_PRIVATE * psf, const char * marker_str)
+{	const READ_CHUNKS * pchk = &psf->rchunks ;
+	int idx ;
+
+	if (marker_str)
+		idx = psf_find_read_chunk_str (pchk, marker_str) ;
+	else
+		idx = pchk->used > 0 ? 0 : -1 ;
+
+	if (idx < 0)
+		return NULL ;
+
+	if (psf->iterator == NULL)
+	{	psf->iterator = calloc (1, sizeof (SF_CHUNK_ITERATOR)) ;
+		if (psf->iterator == NULL)
+			return NULL ;
+		} ;
+
+	psf->iterator->sndfile = (SNDFILE *) psf ;
+
+	if (marker_str)
+	{	int64_t hash ;
+		size_t marker_len ;
+		union
+		{	uint32_t marker ;
+			char str [5] ;
+		} u ;
+
+		snprintf (u.str, sizeof (u.str), "%s", marker_str) ;
+
+		marker_len = strlen (marker_str) ;
+		if (marker_len > 64)
+			marker_len = 64 ;
+
+		hash = marker_len > 4 ? hash_of_str (marker_str) : u.marker ;
+
+		memcpy (psf->iterator->id, marker_str, marker_len) ;
+		psf->iterator->id_size = (unsigned) marker_len ;
+		psf->iterator->hash = hash ;
+		}
+
+	psf->iterator->current = idx ;
+
+	return psf->iterator ;
+} /* psf_get_chunk_iterator */
+
+SF_CHUNK_ITERATOR *
+psf_next_chunk_iterator (const READ_CHUNKS * pchk , SF_CHUNK_ITERATOR * iterator)
+{	uint64_t hash = iterator->hash ;
+	uint32_t k ;
+
+	iterator->current++ ;
+
+	if (hash)
+	{	for (k = iterator->current ; k < pchk->used ; k++)
+			if (pchk->chunks [k].hash == hash)
+			{	iterator->current = k ;
+				return iterator ;
+				}
+		}
+	else if (iterator->current < pchk->used)
+		return iterator ;
+
+	/* No match, clear iterator and return NULL */
+	memset (iterator, 0, sizeof (*iterator)) ;
+	return NULL ;
+} /* psf_next_chunk_iterator */
+
+static int
+psf_store_read_chunk (READ_CHUNKS * pchk, const READ_CHUNK * rchunk)
+{	if (pchk->count == 0)
+	{	pchk->used = 0 ;
+		pchk->count = 20 ;
+		pchk->chunks = calloc (pchk->count, sizeof (READ_CHUNK)) ;
+		if (!pchk->chunks)
+		{	return SFE_MALLOC_FAILED ;
+			} ;
+		}
+	else if (pchk->used > pchk->count)
+		return SFE_INTERNAL ;
+	else if (pchk->used == pchk->count)
+	{	READ_CHUNK * old_ptr = pchk->chunks ;
+		int new_count = 3 * (pchk->count + 1) / 2 ;
+
+		READ_CHUNK * new_chunks = realloc (old_ptr, new_count * sizeof (READ_CHUNK)) ;
+		if (new_chunks == NULL)
+		{
+			return SFE_MALLOC_FAILED ;
+			} else {
+			pchk->chunks = new_chunks;
+			} ;
+		pchk->count = new_count ;
+		} ;
+
+	pchk->chunks [pchk->used] = *rchunk ;
+
+	pchk->used ++ ;
+
+	return SFE_NO_ERROR ;
+} /* psf_store_read_chunk */
+
+int
+psf_store_read_chunk_u32 (READ_CHUNKS * pchk, uint32_t marker, sf_count_t offset, uint32_t len)
+{	READ_CHUNK rchunk ;
+
+	memset (&rchunk, 0, sizeof (rchunk)) ;
+
+	rchunk.hash = marker ;
+	rchunk.mark32 = marker ;
+	rchunk.offset = offset ;
+	rchunk.len = len ;
+
+	rchunk.id_size = 4 ;
+	memcpy (rchunk.id, &marker, rchunk.id_size) ;
+
+	return psf_store_read_chunk (pchk, &rchunk) ;
+} /* psf_store_read_chunk_u32 */
+
+int
+psf_find_read_chunk_str (const READ_CHUNKS * pchk, const char * marker_str)
+{	uint64_t hash ;
+	uint32_t k ;
+	union
+	{	uint32_t marker ;
+		char str [5] ;
+	} u ;
+
+	snprintf (u.str, sizeof (u.str), "%s", marker_str) ;
+
+	hash = strlen (marker_str) > 4 ? hash_of_str (marker_str) : u.marker ;
+
+	for (k = 0 ; k < pchk->used ; k++)
+		if (pchk->chunks [k].hash == hash)
+			return k ;
+
+	return -1 ;
+} /* psf_find_read_chunk_str */
+
+int
+psf_find_read_chunk_m32 (const READ_CHUNKS * pchk, uint32_t marker)
+{	uint32_t k ;
+
+	for (k = 0 ; k < pchk->used ; k++)
+		if (pchk->chunks [k].mark32 == marker)
+			return k ;
+
+	return -1 ;
+} /* psf_find_read_chunk_m32 */
+int
+psf_find_read_chunk_iterator (const READ_CHUNKS * pchk, const SF_CHUNK_ITERATOR * marker)
+{	if (marker->current < pchk->used)
+		return marker->current ;
+
+	return -1 ;
+} /* psf_find_read_chunk_iterator */
+
+int
+psf_store_read_chunk_str (READ_CHUNKS * pchk, const char * marker_str, sf_count_t offset, uint32_t len)
+{	READ_CHUNK rchunk ;
+	union
+	{	uint32_t marker ;
+		char str [5] ;
+	} u ;
+	size_t marker_len ;
+
+	memset (&rchunk, 0, sizeof (rchunk)) ;
+	snprintf (u.str, sizeof (u.str), "%s", marker_str) ;
+
+	marker_len = strlen (marker_str) ;
+
+	rchunk.hash = marker_len > 4 ? hash_of_str (marker_str) : u.marker ;
+	rchunk.mark32 = u.marker ;
+	rchunk.offset = offset ;
+	rchunk.len = len ;
+
+	rchunk.id_size = marker_len > 64 ? 64 : (unsigned) marker_len ;
+	memcpy (rchunk.id, marker_str, rchunk.id_size) ;
+
+	return psf_store_read_chunk (pchk, &rchunk) ;
+} /* psf_store_read_chunk_str */
+
+int
+psf_save_write_chunk (WRITE_CHUNKS * pchk, const SF_CHUNK_INFO * chunk_info)
+{	union
+	{	uint32_t marker ;
+		char str [5] ;
+		/* Update snprintf() format string below when changing this */
+	} u ;
+	uint32_t len ;
+
+	if (pchk->count == 0)
+	{	pchk->used = 0 ;
+		pchk->count = 20 ;
+		pchk->chunks = calloc (pchk->count, sizeof (WRITE_CHUNK)) ;
+		if (!pchk->chunks)
+		{	return SFE_MALLOC_FAILED ;
+			} ;
+		}
+	else if (pchk->used >= pchk->count)
+	{	WRITE_CHUNK * old_ptr = pchk->chunks ;
+		int new_count = 3 * (pchk->count + 1) / 2 ;
+
+		WRITE_CHUNK * new_chunks = realloc (old_ptr, new_count * sizeof (WRITE_CHUNK)) ;
+		if (new_chunks == NULL)
+		{
+			return SFE_MALLOC_FAILED ;
+			} else {
+			pchk->chunks = new_chunks;
+			} ;
+		} ;
+
+	len = chunk_info->datalen ;
+	while (len & 3) len ++ ;
+
+	snprintf (u.str, sizeof (u.str), "%.4s", chunk_info->id) ;
+
+	pchk->chunks [pchk->used].hash = strlen (chunk_info->id) > 4 ? hash_of_str (chunk_info->id) : u.marker ;
+	pchk->chunks [pchk->used].mark32 = u.marker ;
+	pchk->chunks [pchk->used].len = len ;
+	pchk->chunks [pchk->used].data = psf_memdup (chunk_info->data, chunk_info->datalen) ;
+
+	pchk->used ++ ;
+
+	return SFE_NO_ERROR ;
+} /* psf_save_write_chunk */
+
diff --git a/src/command.c b/src/command.c
new file mode 100644
index 0000000..d4837da
--- /dev/null
+++ b/src/command.c
@@ -0,0 +1,415 @@
+/*
+** Copyright (C) 2001-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<string.h>
+#include	<math.h>
+
+#include	"sndfile.h"
+#include	"common.h"
+
+static SF_FORMAT_INFO const simple_formats [] =
+{
+	{	SF_FORMAT_AIFF | SF_FORMAT_PCM_16,
+		"AIFF (Apple/SGI 16 bit PCM)", "aiff"
+		},
+
+	{	SF_FORMAT_AIFF | SF_FORMAT_FLOAT,
+		"AIFF (Apple/SGI 32 bit float)", "aifc"
+		},
+
+	{	SF_FORMAT_AIFF | SF_FORMAT_PCM_S8,
+		"AIFF (Apple/SGI 8 bit PCM)", "aiff"
+		},
+
+	{	SF_FORMAT_AU | SF_FORMAT_PCM_16,
+		"AU (Sun/Next 16 bit PCM)", "au"
+		},
+
+	{	SF_FORMAT_AU | SF_FORMAT_ULAW,
+		"AU (Sun/Next 8-bit u-law)", "au"
+		},
+
+	{	SF_FORMAT_CAF | SF_FORMAT_ALAC_16,
+		"CAF (Apple 16 bit ALAC)", "caf"
+		},
+
+	{	SF_FORMAT_CAF | SF_FORMAT_PCM_16,
+		"CAF (Apple 16 bit PCM)", "caf"
+		},
+
+#if HAVE_EXTERNAL_XIPH_LIBS
+	{	SF_FORMAT_FLAC | SF_FORMAT_PCM_16,
+		"FLAC 16 bit", "flac"
+		},
+#endif
+
+#if HAVE_MPEG
+	{	SF_FORMAT_MPEG | SF_FORMAT_MPEG_LAYER_III,
+		"MPEG Layer 3", "mp3"
+		},
+#endif
+
+	{	SF_FORMAT_RAW | SF_FORMAT_VOX_ADPCM,
+		"OKI Dialogic VOX ADPCM", "vox"
+		},
+
+#if HAVE_EXTERNAL_XIPH_LIBS
+	{	SF_FORMAT_OGG | SF_FORMAT_OPUS,
+		"Ogg Opus (Xiph Foundation)", "opus"
+		},
+
+	{	SF_FORMAT_OGG | SF_FORMAT_VORBIS,
+		"Ogg Vorbis (Xiph Foundation)", "ogg"
+		},
+#endif
+
+	{	SF_FORMAT_WAV | SF_FORMAT_PCM_16,
+		"WAV (Microsoft 16 bit PCM)", "wav"
+		},
+
+	{	SF_FORMAT_WAV | SF_FORMAT_FLOAT,
+		"WAV (Microsoft 32 bit float)", "wav"
+		},
+
+	{	SF_FORMAT_WAV | SF_FORMAT_IMA_ADPCM,
+		"WAV (Microsoft 4 bit IMA ADPCM)", "wav"
+		},
+
+	{	SF_FORMAT_WAV | SF_FORMAT_MS_ADPCM,
+		"WAV (Microsoft 4 bit MS ADPCM)", "wav"
+		},
+
+	{	SF_FORMAT_WAV | SF_FORMAT_PCM_U8,
+		"WAV (Microsoft 8 bit PCM)", "wav"
+		},
+
+} ; /* simple_formats */
+
+int
+psf_get_format_simple_count	(void)
+{	return (sizeof (simple_formats) / sizeof (SF_FORMAT_INFO)) ;
+} /* psf_get_format_simple_count */
+
+int
+psf_get_format_simple (SF_FORMAT_INFO *data)
+{	int indx ;
+
+	if (data->format < 0 || data->format >= (SIGNED_SIZEOF (simple_formats) / SIGNED_SIZEOF (SF_FORMAT_INFO)))
+		return SFE_BAD_COMMAND_PARAM ;
+
+	indx = data->format ;
+	memcpy (data, &(simple_formats [indx]), SIGNED_SIZEOF (SF_FORMAT_INFO)) ;
+
+	return 0 ;
+} /* psf_get_format_simple */
+
+/*============================================================================
+** Major format info.
+*/
+
+static SF_FORMAT_INFO const major_formats [] =
+{
+	{	SF_FORMAT_AIFF,		"AIFF (Apple/SGI)",						"aiff" 	},
+	{	SF_FORMAT_AU,		"AU (Sun/NeXT)", 						"au"	},
+	{	SF_FORMAT_AVR,		"AVR (Audio Visual Research)",			"avr"	},
+	{	SF_FORMAT_CAF,		"CAF (Apple Core Audio File)",			"caf"	},
+#if HAVE_EXTERNAL_XIPH_LIBS
+	{	SF_FORMAT_FLAC,		"FLAC (Free Lossless Audio Codec)",		"flac"	},
+#endif
+	{	SF_FORMAT_HTK,		"HTK (HMM Tool Kit)",					"htk"	},
+	{	SF_FORMAT_SVX,		"IFF (Amiga IFF/SVX8/SV16)",			"iff"	},
+	{	SF_FORMAT_MAT4,		"MAT4 (GNU Octave 2.0 / Matlab 4.2)",	"mat"	},
+	{	SF_FORMAT_MAT5,		"MAT5 (GNU Octave 2.1 / Matlab 5.0)",	"mat"	},
+	{	SF_FORMAT_MPC2K,	"MPC (Akai MPC 2k)",					"mpc"	},
+#if HAVE_MPEG
+	{	SF_FORMAT_MPEG,		"MPEG-1/2 Audio",						"m1a"	},
+#endif
+#if HAVE_EXTERNAL_XIPH_LIBS
+	{	SF_FORMAT_OGG,		"OGG (OGG Container format)",			"oga"	},
+#endif
+	{	SF_FORMAT_PAF,		"PAF (Ensoniq PARIS)", 					"paf"	},
+	{	SF_FORMAT_PVF,		"PVF (Portable Voice Format)",			"pvf"	},
+	{	SF_FORMAT_RAW,		"RAW (header-less)",					"raw"	},
+	{	SF_FORMAT_RF64,		"RF64 (RIFF 64)",						"rf64"	},
+	{	SF_FORMAT_SD2,		"SD2 (Sound Designer II)", 				"sd2"	},
+	{	SF_FORMAT_SDS,		"SDS (Midi Sample Dump Standard)", 		"sds"	},
+	{	SF_FORMAT_IRCAM,	"SF (Berkeley/IRCAM/CARL)",				"sf"	},
+	{	SF_FORMAT_VOC,		"VOC (Creative Labs)",					"voc"	},
+	{	SF_FORMAT_W64,		"W64 (SoundFoundry WAVE 64)",			"w64"	},
+	{	SF_FORMAT_WAV,		"WAV (Microsoft)",						"wav"	},
+	{	SF_FORMAT_NIST,		"WAV (NIST Sphere)",					"wav"	},
+	{	SF_FORMAT_WAVEX,	"WAVEX (Microsoft)",					"wav"	},
+	{	SF_FORMAT_WVE,		"WVE (Psion Series 3)",					"wve"	},
+	{	SF_FORMAT_XI,		"XI (FastTracker 2)",					"xi"	},
+
+} ; /* major_formats */
+
+int
+psf_get_format_major_count	(void)
+{	return (sizeof (major_formats) / sizeof (SF_FORMAT_INFO)) ;
+} /* psf_get_format_major_count */
+
+int
+psf_get_format_major (SF_FORMAT_INFO *data)
+{	int indx ;
+
+	if (data->format < 0 || data->format >= (SIGNED_SIZEOF (major_formats) / SIGNED_SIZEOF (SF_FORMAT_INFO)))
+		return SFE_BAD_COMMAND_PARAM ;
+
+	indx = data->format ;
+	memcpy (data, &(major_formats [indx]), SIGNED_SIZEOF (SF_FORMAT_INFO)) ;
+
+	return 0 ;
+} /* psf_get_format_major */
+
+/*============================================================================
+** Subtype format info.
+*/
+
+static SF_FORMAT_INFO subtype_formats [] =
+{
+	{	SF_FORMAT_PCM_S8,		"Signed 8 bit PCM",		NULL 	},
+	{	SF_FORMAT_PCM_16,		"Signed 16 bit PCM",	NULL 	},
+	{	SF_FORMAT_PCM_24,		"Signed 24 bit PCM",	NULL 	},
+	{	SF_FORMAT_PCM_32,		"Signed 32 bit PCM",	NULL 	},
+
+	{	SF_FORMAT_PCM_U8,		"Unsigned 8 bit PCM",	NULL 	},
+
+	{	SF_FORMAT_FLOAT,		"32 bit float",			NULL 	},
+	{	SF_FORMAT_DOUBLE,		"64 bit float",			NULL 	},
+
+	{	SF_FORMAT_ULAW,			"U-Law",				NULL 	},
+	{	SF_FORMAT_ALAW,			"A-Law",				NULL 	},
+	{	SF_FORMAT_IMA_ADPCM,	"IMA ADPCM",			NULL 	},
+	{	SF_FORMAT_MS_ADPCM,		"Microsoft ADPCM",		NULL 	},
+
+	{	SF_FORMAT_GSM610,		"GSM 6.10",				NULL 	},
+
+	{	SF_FORMAT_G721_32,		"32kbs G721 ADPCM",		NULL 	},
+	{	SF_FORMAT_G723_24,		"24kbs G723 ADPCM",		NULL 	},
+	{	SF_FORMAT_G723_40,		"40kbs G723 ADPCM",		NULL	},
+
+	{	SF_FORMAT_DWVW_12,		"12 bit DWVW",			NULL 	},
+	{	SF_FORMAT_DWVW_16,		"16 bit DWVW",			NULL 	},
+	{	SF_FORMAT_DWVW_24,		"24 bit DWVW",			NULL 	},
+	{	SF_FORMAT_VOX_ADPCM,	"VOX ADPCM",			"vox" 	},
+
+	{	SF_FORMAT_NMS_ADPCM_16,	"16kbs NMS ADPCM",		NULL	},
+	{	SF_FORMAT_NMS_ADPCM_24,	"24kbs NMS ADPCM",		NULL	},
+	{	SF_FORMAT_NMS_ADPCM_32,	"32kbs NMS ADPCM",		NULL	},
+
+	{	SF_FORMAT_DPCM_16,		"16 bit DPCM",			NULL 	},
+	{	SF_FORMAT_DPCM_8,		"8 bit DPCM",			NULL 	},
+
+#if HAVE_EXTERNAL_XIPH_LIBS
+	{	SF_FORMAT_VORBIS,		"Vorbis",				NULL 	},
+	{	SF_FORMAT_OPUS,			"Opus",					NULL 	},
+#endif
+
+#if HAVE_MPEG
+	{	SF_FORMAT_MPEG_LAYER_I,		"MPEG Layer I",		"mp1"	},
+	{	SF_FORMAT_MPEG_LAYER_II,	"MPEG Layer II",	"mp2"	},
+	{	SF_FORMAT_MPEG_LAYER_III,	"MPEG Layer III",	"mp3"	},
+#endif
+
+	{	SF_FORMAT_ALAC_16,		"16 bit ALAC",			NULL	},
+	{	SF_FORMAT_ALAC_20,		"20 bit ALAC",			NULL	},
+	{	SF_FORMAT_ALAC_24,		"24 bit ALAC",			NULL	},
+	{	SF_FORMAT_ALAC_32,		"32 bit ALAC",			NULL	},
+} ; /* subtype_formats */
+
+int
+psf_get_format_subtype_count	(void)
+{	return (sizeof (subtype_formats) / sizeof (SF_FORMAT_INFO)) ;
+} /* psf_get_format_subtype_count */
+
+int
+psf_get_format_subtype (SF_FORMAT_INFO *data)
+{	int indx ;
+
+	if (data->format < 0 || data->format >= (SIGNED_SIZEOF (subtype_formats) / SIGNED_SIZEOF (SF_FORMAT_INFO)))
+	{	data->format = 0 ;
+		return SFE_BAD_COMMAND_PARAM ;
+		} ;
+
+	indx = data->format ;
+	memcpy (data, &(subtype_formats [indx]), sizeof (SF_FORMAT_INFO)) ;
+
+	return 0 ;
+} /* psf_get_format_subtype */
+
+/*==============================================================================
+*/
+
+int
+psf_get_format_info (SF_FORMAT_INFO *data)
+{	int k, format ;
+
+	if (SF_CONTAINER (data->format))
+	{	format = SF_CONTAINER (data->format) ;
+
+		for (k = 0 ; k < (SIGNED_SIZEOF (major_formats) / SIGNED_SIZEOF (SF_FORMAT_INFO)) ; k++)
+		{	if (format == major_formats [k].format)
+			{	memcpy (data, &(major_formats [k]), sizeof (SF_FORMAT_INFO)) ;
+				return 0 ;
+				} ;
+			} ;
+		}
+	else if (SF_CODEC (data->format))
+	{	format = SF_CODEC (data->format) ;
+
+		for (k = 0 ; k < (SIGNED_SIZEOF (subtype_formats) / SIGNED_SIZEOF (SF_FORMAT_INFO)) ; k++)
+		{	if (format == subtype_formats [k].format)
+			{	memcpy (data, &(subtype_formats [k]), sizeof (SF_FORMAT_INFO)) ;
+				return 0 ;
+				} ;
+			} ;
+		} ;
+
+	memset (data, 0, sizeof (SF_FORMAT_INFO)) ;
+
+	return SFE_BAD_COMMAND_PARAM ;
+} /* psf_get_format_info */
+
+/*==============================================================================
+*/
+
+double
+psf_calc_signal_max (SF_PRIVATE *psf, int normalize)
+{	BUF_UNION	ubuf ;
+	sf_count_t	position ;
+	double 		max_val, temp, *data ;
+	int			k, len, readcount, save_state ;
+
+	/* If the file is not seekable, there is nothing we can do. */
+	if (! psf->sf.seekable)
+	{	psf->error = SFE_NOT_SEEKABLE ;
+		return	0.0 ;
+		} ;
+
+	if (! psf->read_double)
+	{	psf->error = SFE_UNIMPLEMENTED ;
+		return	0.0 ;
+		} ;
+
+	save_state = sf_command ((SNDFILE*) psf, SFC_GET_NORM_DOUBLE, NULL, 0) ;
+	sf_command ((SNDFILE*) psf, SFC_SET_NORM_DOUBLE, NULL, normalize) ;
+
+	/* Brute force. Read the whole file and find the biggest sample. */
+	/* Get current position in file */
+	position = sf_seek ((SNDFILE*) psf, 0, SEEK_CUR) ;
+	/* Go to start of file. */
+	sf_seek ((SNDFILE*) psf, 0, SEEK_SET) ;
+
+	data = ubuf.dbuf ;
+	/* Make sure len is an integer multiple of the channel count. */
+	len = ARRAY_LEN (ubuf.dbuf) - (ARRAY_LEN (ubuf.dbuf) % psf->sf.channels) ;
+
+	for (readcount = 1, max_val = 0.0 ; readcount > 0 ; /* nothing */)
+	{	readcount = (int) sf_read_double ((SNDFILE*) psf, data, len) ;
+		for (k = 0 ; k < readcount ; k++)
+		{	temp = fabs (data [k]) ;
+			max_val = temp > max_val ? temp : max_val ;
+			} ;
+		} ;
+
+	/* Return to SNDFILE to original state. */
+	sf_seek ((SNDFILE*) psf, position, SEEK_SET) ;
+	sf_command ((SNDFILE*) psf, SFC_SET_NORM_DOUBLE, NULL, save_state) ;
+
+	return	max_val ;
+} /* psf_calc_signal_max */
+
+int
+psf_calc_max_all_channels (SF_PRIVATE *psf, double *peaks, int normalize)
+{	BUF_UNION	ubuf ;
+	sf_count_t	position ;
+	double 		temp, *data ;
+	int			k, len, readcount, save_state ;
+	int			chan ;
+
+	/* If the file is not seekable, there is nothing we can do. */
+	if (! psf->sf.seekable)
+		return (psf->error = SFE_NOT_SEEKABLE) ;
+
+	if (! psf->read_double)
+		return (psf->error = SFE_UNIMPLEMENTED) ;
+
+	save_state = sf_command ((SNDFILE*) psf, SFC_GET_NORM_DOUBLE, NULL, 0) ;
+	sf_command ((SNDFILE*) psf, SFC_SET_NORM_DOUBLE, NULL, normalize) ;
+
+	memset (peaks, 0, sizeof (double) * psf->sf.channels) ;
+
+	/* Brute force. Read the whole file and find the biggest sample for each channel. */
+	position = sf_seek ((SNDFILE*) psf, 0, SEEK_CUR) ; /* Get current position in file */
+	sf_seek ((SNDFILE*) psf, 0, SEEK_SET) ;			/* Go to start of file. */
+
+	len = ARRAY_LEN (ubuf.dbuf) - (ARRAY_LEN (ubuf.dbuf) % psf->sf.channels) ;
+
+	data = ubuf.dbuf ;
+
+	chan = 0 ;
+	readcount = len ;
+	while (readcount > 0)
+	{	readcount = (int) sf_read_double ((SNDFILE*) psf, data, len) ;
+		for (k = 0 ; k < readcount ; k++)
+		{	temp = fabs (data [k]) ;
+			peaks [chan] = temp > peaks [chan] ? temp : peaks [chan] ;
+			chan = (chan + 1) % psf->sf.channels ;
+			} ;
+		} ;
+
+	sf_seek ((SNDFILE*) psf, position, SEEK_SET) ;		/* Return to original position. */
+
+	sf_command ((SNDFILE*) psf, SFC_SET_NORM_DOUBLE, NULL, save_state) ;
+
+	return	0 ;
+} /* psf_calc_max_all_channels */
+
+int
+psf_get_signal_max (SF_PRIVATE *psf, double *peak)
+{	int k ;
+
+	if (psf->peak_info == NULL)
+		return SF_FALSE ;
+
+	peak [0] = psf->peak_info->peaks [0].value ;
+
+	for (k = 1 ; k < psf->sf.channels ; k++)
+		peak [0] = SF_MAX (peak [0], psf->peak_info->peaks [k].value) ;
+
+	return SF_TRUE ;
+} /* psf_get_signal_max */
+
+int
+psf_get_max_all_channels (SF_PRIVATE *psf, double *peaks)
+{	int k ;
+
+	if (psf->peak_info == NULL)
+		return SF_FALSE ;
+
+	for (k = 0 ; k < psf->sf.channels ; k++)
+		peaks [k] = psf->peak_info->peaks [k].value ;
+
+	return SF_TRUE ;
+} /* psf_get_max_all_channels */
+
+
diff --git a/src/common.c b/src/common.c
new file mode 100644
index 0000000..1c3d951
--- /dev/null
+++ b/src/common.c
@@ -0,0 +1,1845 @@
+/*
+** Copyright (C) 1999-2019 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <config.h>
+
+#include <stdarg.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#else
+#include "sf_unistd.h"
+#endif
+#include <ctype.h>
+#include <math.h>
+#include <time.h>
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#include "sndfile.h"
+#include "sfendian.h"
+#include "common.h"
+
+#define	INITIAL_HEADER_SIZE	256
+
+/* Allocate and initialize the SF_PRIVATE struct. */
+SF_PRIVATE *
+psf_allocate (void)
+{	SF_PRIVATE * psf ;
+
+	if ((psf = calloc (1, sizeof (SF_PRIVATE))) == NULL)
+		return	NULL ;
+
+	if ((psf->header.ptr = calloc (1, INITIAL_HEADER_SIZE)) == NULL)
+	{	free (psf) ;
+		return	NULL ;
+		} ;
+	psf->header.len = INITIAL_HEADER_SIZE ;
+
+	return psf ;
+} /* psf_allocate */
+
+static int
+psf_bump_header_allocation (SF_PRIVATE * psf, sf_count_t needed)
+{
+	sf_count_t newlen, smallest = INITIAL_HEADER_SIZE ;
+	void * ptr ;
+
+	newlen = (needed > psf->header.len) ? 2 * SF_MAX (needed, smallest) : 2 * psf->header.len ;
+
+	if (newlen > 100 * 1024)
+	{	psf_log_printf (psf, "Request for header allocation of %D denied.\n", newlen) ;
+		return 1 ;
+		}
+
+	if ((ptr = realloc (psf->header.ptr, newlen)) == NULL)
+	{	psf_log_printf (psf, "realloc (%p, %D) failed\n", psf->header.ptr, newlen) ;
+		psf->error = SFE_MALLOC_FAILED ;
+		return 1 ;
+		} ;
+
+	/* Always zero-out new header memory to avoid un-initializer memory accesses. */
+	if (newlen > psf->header.len)
+		memset ((char *) ptr + psf->header.len, 0, newlen - psf->header.len) ;
+
+	psf->header.ptr = ptr ;
+	psf->header.len = newlen ;
+	return 0 ;
+} /* psf_bump_header_allocation */
+
+/*-----------------------------------------------------------------------------------------------
+** psf_log_printf allows libsndfile internal functions to print to an internal parselog which
+** can later be displayed.
+** The format specifiers are as for printf but without the field width and other modifiers.
+** Printing is performed to the parselog char array of the SF_PRIVATE struct.
+** Printing is done in such a way as to guarantee that the log never overflows the end of the
+** parselog array.
+*/
+
+static inline void
+log_putchar (SF_PRIVATE *psf, char ch)
+{	if (psf->parselog.indx < SIGNED_SIZEOF (psf->parselog.buf) - 1)
+	{	psf->parselog.buf [psf->parselog.indx++] = ch ;
+		psf->parselog.buf [psf->parselog.indx] = 0 ;
+		} ;
+	return ;
+} /* log_putchar */
+
+void
+psf_log_printf (SF_PRIVATE *psf, const char *format, ...)
+{	va_list		ap ;
+	uint32_t	u, tens ;
+	int			d, shift, width, width_specifier, left_align, slen, precision ;
+	char		c, *strptr, istr [5], lead_char, sign_char ;
+
+	va_start (ap, format) ;
+
+	while ((c = *format++))
+	{	if (c != '%')
+		{	log_putchar (psf, c) ;
+			continue ;
+			} ;
+
+		if (format [0] == '%') /* Handle %% */
+		{ 	log_putchar (psf, '%') ;
+			format ++ ;
+			continue ;
+			} ;
+
+		sign_char = 0 ;
+		left_align = SF_FALSE ;
+		while (1)
+		{	switch (format [0])
+			{	case ' ' :
+				case '+' :
+					sign_char = format [0] ;
+					format ++ ;
+					continue ;
+
+				case '-' :
+					left_align = SF_TRUE ;
+					format ++ ;
+					continue ;
+
+				default : break ;
+				} ;
+
+			break ;
+			} ;
+
+		if (format [0] == 0)
+			break ;
+
+		lead_char = ' ' ;
+		if (format [0] == '0')
+			lead_char = '0' ;
+
+		width_specifier = 0 ;
+		while ((c = *format++) && isdigit (c))
+			width_specifier = width_specifier * 10 + (c - '0') ;
+
+		precision = 0 ;
+		if (c == '.')
+		{	while ((c = *format++) && isdigit (c))
+				precision = precision * 10 + (c - '0') ;
+			} ;
+
+		switch (c)
+		{	case 0 : /* NULL character. */
+					va_end (ap) ;
+					return ;
+
+			case 's': /* string */
+					strptr = va_arg (ap, char *) ;
+					if (strptr == NULL)
+						break ;
+					if (precision > 0)
+						slen = strnlen (strptr, precision) ;
+					else
+						slen = strlen (strptr) ;
+					width_specifier = width_specifier >= slen ? width_specifier - slen : 0 ;
+					if (left_align == SF_FALSE)
+						while (width_specifier -- > 0)
+							log_putchar (psf, ' ') ;
+					while (slen--)
+						log_putchar (psf, *strptr++) ;
+					while (width_specifier -- > 0)
+						log_putchar (psf, ' ') ;
+					break ;
+
+			case 'd': /* int */
+					d = va_arg (ap, int) ;
+
+					if (d < 0)
+					{	sign_char = '-' ;
+						if (lead_char != '0' && left_align == SF_FALSE)
+							width_specifier -- ;
+
+						u = - ((unsigned) d) ;
+						}
+					else
+					{	u = (unsigned) d ;
+						}
+
+					tens = 1 ;
+					width = 1 ;
+					while (u / tens >= 10)
+					{	tens *= 10 ;
+						width ++ ;
+						} ;
+
+					width_specifier -= width ;
+
+					if (sign_char == ' ')
+					{	log_putchar (psf, ' ') ;
+						width_specifier -- ;
+						} ;
+
+					if (left_align == SF_FALSE && lead_char != '0')
+					{	if (sign_char == '+')
+							width_specifier -- ;
+
+						while (width_specifier -- > 0)
+							log_putchar (psf, lead_char) ;
+						} ;
+
+					if (sign_char == '+' || sign_char == '-')
+					{	log_putchar (psf, sign_char) ;
+						width_specifier -- ;
+						} ;
+
+					if (left_align == SF_FALSE)
+						while (width_specifier -- > 0)
+							log_putchar (psf, lead_char) ;
+
+					while (tens > 0)
+					{	log_putchar (psf, '0' + u / tens) ;
+						u %= tens ;
+						tens /= 10 ;
+						} ;
+
+					while (width_specifier -- > 0)
+						log_putchar (psf, lead_char) ;
+					break ;
+
+			case 'D': /* sf_count_t */
+					{	sf_count_t	D ;
+						uint64_t	U, Tens ;
+
+						D = va_arg (ap, sf_count_t) ;
+
+						if (D == 0)
+						{	while (-- width_specifier > 0)
+								log_putchar (psf, lead_char) ;
+							log_putchar (psf, '0') ;
+							break ;
+							}
+						else
+						{	if (D < 0)
+							{	log_putchar (psf, '-') ;
+								U = - ((uint64_t) D) ;
+								}
+							else
+							{	U = (uint64_t) D ;
+								}
+							}
+
+						Tens = 1 ;
+						width = 1 ;
+						while (U / Tens >= 10)
+						{	Tens *= 10 ;
+							width ++ ;
+							} ;
+
+						while (width_specifier > width)
+						{	log_putchar (psf, lead_char) ;
+							width_specifier-- ;
+							} ;
+
+						while (Tens > 0)
+						{	log_putchar (psf, '0' + U / Tens) ;
+							U %= Tens ;
+							Tens /= 10 ;
+							} ;
+						} ;
+					break ;
+
+			case 'u': /* unsigned int */
+					u = va_arg (ap, unsigned int) ;
+
+					tens = 1 ;
+					width = 1 ;
+					while (u / tens >= 10)
+					{	tens *= 10 ;
+						width ++ ;
+						} ;
+
+					width_specifier -= width ;
+
+					if (sign_char == ' ')
+					{	log_putchar (psf, ' ') ;
+						width_specifier -- ;
+						} ;
+
+					if (left_align == SF_FALSE && lead_char != '0')
+					{	if (sign_char == '+')
+							width_specifier -- ;
+
+						while (width_specifier -- > 0)
+							log_putchar (psf, lead_char) ;
+						} ;
+
+					if (sign_char == '+' || sign_char == '-')
+					{	log_putchar (psf, sign_char) ;
+						width_specifier -- ;
+						} ;
+
+					if (left_align == SF_FALSE)
+						while (width_specifier -- > 0)
+							log_putchar (psf, lead_char) ;
+
+					while (tens > 0)
+					{	log_putchar (psf, '0' + u / tens) ;
+						u %= tens ;
+						tens /= 10 ;
+						} ;
+
+					while (width_specifier -- > 0)
+						log_putchar (psf, lead_char) ;
+					break ;
+
+			case 'c': /* char */
+					c = va_arg (ap, int) & 0xFF ;
+					log_putchar (psf, c) ;
+					break ;
+
+			case 'x': /* hex */
+			case 'X': /* hex */
+					d = va_arg (ap, int) ;
+
+					if (d == 0)
+					{	while (--width_specifier > 0)
+							log_putchar (psf, lead_char) ;
+						log_putchar (psf, '0') ;
+						break ;
+						} ;
+					shift = 28 ;
+					width = (width_specifier < 8) ? 8 : width_specifier ;
+					while (! ((((uint32_t) 0xF) << shift) & d))
+					{	shift -= 4 ;
+						width -- ;
+						} ;
+
+					while (width > 0 && width_specifier > width)
+					{	log_putchar (psf, lead_char) ;
+						width_specifier-- ;
+						} ;
+
+					while (shift >= 0)
+					{	c = (d >> shift) & 0xF ;
+						log_putchar (psf, (c > 9) ? c + 'A' - 10 : c + '0') ;
+						shift -= 4 ;
+						} ;
+					break ;
+
+			case 'M': /* int2str */
+					d = va_arg (ap, int) ;
+					if (CPU_IS_LITTLE_ENDIAN)
+					{	istr [0] = d & 0xFF ;
+						istr [1] = (d >> 8) & 0xFF ;
+						istr [2] = (d >> 16) & 0xFF ;
+						istr [3] = (d >> 24) & 0xFF ;
+						}
+					else
+					{	istr [3] = d & 0xFF ;
+						istr [2] = (d >> 8) & 0xFF ;
+						istr [1] = (d >> 16) & 0xFF ;
+						istr [0] = (d >> 24) & 0xFF ;
+						} ;
+					istr [4] = 0 ;
+					strptr = istr ;
+					while (*strptr)
+					{	c = *strptr++ ;
+						log_putchar (psf, psf_isprint (c) ? c : '.') ;
+						} ;
+					break ;
+
+			default :
+					log_putchar (psf, '*') ;
+					log_putchar (psf, c) ;
+					log_putchar (psf, '*') ;
+					break ;
+			} /* switch */
+		} /* while */
+
+	va_end (ap) ;
+	return ;
+} /* psf_log_printf */
+
+/*-----------------------------------------------------------------------------------------------
+**  ASCII header printf functions.
+**  Some formats (ie NIST) use ascii text in their headers.
+**  Format specifiers are the same as the standard printf specifiers (uses vsnprintf).
+**  If this generates a compile error on any system, the author should be notified
+**  so an alternative vsnprintf can be provided.
+*/
+
+void
+psf_asciiheader_printf (SF_PRIVATE *psf, const char *format, ...)
+{	va_list	argptr ;
+	int		maxlen ;
+	char	*start ;
+
+	if (! format)
+		return ;
+
+	maxlen = strlen ((char*) psf->header.ptr) ;
+	start	= ((char*) psf->header.ptr) + maxlen ;
+	maxlen	= psf->header.len - maxlen ;
+
+	va_start (argptr, format) ;
+	vsnprintf (start, maxlen, format, argptr) ;
+	va_end (argptr) ;
+
+	/* Make sure the string is properly terminated. */
+	start [maxlen - 1] = 0 ;
+
+	psf->header.indx = strlen ((char*) psf->header.ptr) ;
+
+	return ;
+} /* psf_asciiheader_printf */
+
+/*-----------------------------------------------------------------------------------------------
+**  Binary header writing functions. Returns number of bytes written.
+**
+**  Format specifiers for psf_binheader_writef are as follows
+**		m	- marker - four bytes - no endian manipulation
+**
+**		e   - all following numerical values will be little endian
+**		E   - all following numerical values will be big endian
+**
+**		t   - all following O types will be truncated to 4 bytes
+**		T   - switch off truncation of all following O types
+**
+**		1	- single byte value
+**		2	- two byte value
+**		3	- three byte value
+**		4	- four byte value
+**		8	- eight byte value (sometimes written as 4 bytes)
+**
+**		s   - string preceded by a four byte length
+**		S   - string including null terminator
+**      p   - a Pascal string
+**
+**		f	- floating point data
+**		d	- double precision floating point data
+**		h	- 16 binary bytes value
+**
+**		b	- binary data (see below)
+**		z   - zero bytes (ses below)
+**		j	- jump forwards or backwards
+**
+**	To write a word followed by an int (both little endian) use:
+**		psf_binheader_writef ("e24", wordval, longval) ;
+**
+**	To write binary data use:
+**		psf_binheader_writef ("b", &bindata, sizeof (bindata)) ;
+**
+**	To write N zero bytes use:
+**			NOTE: due to platform issues (ie x86-64) you should cast the
+**			argument to size_t or ensure the variable type is size_t.
+**		psf_binheader_writef ("z", N) ;
+*/
+
+/* These macros may seem a bit messy but do prevent problems with processors which
+** seg. fault when asked to write an int or short to a non-int/short aligned address.
+*/
+
+static inline void
+header_put_byte (SF_PRIVATE *psf, char x)
+{	psf->header.ptr [psf->header.indx++] = x ;
+} /* header_put_byte */
+
+#if (CPU_IS_BIG_ENDIAN == 1)
+static inline void
+header_put_marker (SF_PRIVATE *psf, int x)
+{	psf->header.ptr [psf->header.indx++] = (x >> 24) ;
+	psf->header.ptr [psf->header.indx++] = (x >> 16) ;
+	psf->header.ptr [psf->header.indx++] = (x >> 8) ;
+	psf->header.ptr [psf->header.indx++] = x ;
+} /* header_put_marker */
+
+#elif (CPU_IS_LITTLE_ENDIAN == 1)
+static inline void
+header_put_marker (SF_PRIVATE *psf, int x)
+{	psf->header.ptr [psf->header.indx++] = x ;
+	psf->header.ptr [psf->header.indx++] = (x >> 8) ;
+	psf->header.ptr [psf->header.indx++] = (x >> 16) ;
+	psf->header.ptr [psf->header.indx++] = (x >> 24) ;
+} /* header_put_marker */
+
+#else
+#	error "Cannot determine endian-ness of processor."
+#endif
+
+
+static inline void
+header_put_be_short (SF_PRIVATE *psf, int x)
+{	psf->header.ptr [psf->header.indx++] = (x >> 8) ;
+	psf->header.ptr [psf->header.indx++] = x ;
+} /* header_put_be_short */
+
+static inline void
+header_put_le_short (SF_PRIVATE *psf, int x)
+{	psf->header.ptr [psf->header.indx++] = x ;
+	psf->header.ptr [psf->header.indx++] = (x >> 8) ;
+} /* header_put_le_short */
+
+static inline void
+header_put_be_3byte (SF_PRIVATE *psf, int x)
+{	psf->header.ptr [psf->header.indx++] = (x >> 16) ;
+	psf->header.ptr [psf->header.indx++] = (x >> 8) ;
+	psf->header.ptr [psf->header.indx++] = x ;
+} /* header_put_be_3byte */
+
+static inline void
+header_put_le_3byte (SF_PRIVATE *psf, int x)
+{	psf->header.ptr [psf->header.indx++] = x ;
+	psf->header.ptr [psf->header.indx++] = (x >> 8) ;
+	psf->header.ptr [psf->header.indx++] = (x >> 16) ;
+} /* header_put_le_3byte */
+
+static inline void
+header_put_be_int (SF_PRIVATE *psf, int x)
+{	psf->header.ptr [psf->header.indx++] = (x >> 24) ;
+	psf->header.ptr [psf->header.indx++] = (x >> 16) ;
+	psf->header.ptr [psf->header.indx++] = (x >> 8) ;
+	psf->header.ptr [psf->header.indx++] = x ;
+} /* header_put_be_int */
+
+static inline void
+header_put_le_int (SF_PRIVATE *psf, int x)
+{	psf->header.ptr [psf->header.indx++] = x ;
+	psf->header.ptr [psf->header.indx++] = (x >> 8) ;
+	psf->header.ptr [psf->header.indx++] = (x >> 16) ;
+	psf->header.ptr [psf->header.indx++] = (x >> 24) ;
+} /* header_put_le_int */
+
+static inline void
+header_put_be_8byte (SF_PRIVATE *psf, sf_count_t x)
+{	psf->header.ptr [psf->header.indx++] = (x >> 56) ;
+	psf->header.ptr [psf->header.indx++] = (unsigned char) (x >> 48) ;
+	psf->header.ptr [psf->header.indx++] = (unsigned char) (x >> 40) ;
+	psf->header.ptr [psf->header.indx++] = (unsigned char) (x >> 32) ;
+	psf->header.ptr [psf->header.indx++] = (unsigned char) (x >> 24) ;
+	psf->header.ptr [psf->header.indx++] = (unsigned char) (x >> 16) ;
+	psf->header.ptr [psf->header.indx++] = (unsigned char) (x >> 8) ;
+	psf->header.ptr [psf->header.indx++] = (unsigned char) x ;
+} /* header_put_be_8byte */
+
+static inline void
+header_put_le_8byte (SF_PRIVATE *psf, sf_count_t x)
+{	psf->header.ptr [psf->header.indx++] = (unsigned char) x ;
+	psf->header.ptr [psf->header.indx++] = (unsigned char) (x >> 8) ;
+	psf->header.ptr [psf->header.indx++] = (unsigned char) (x >> 16) ;
+	psf->header.ptr [psf->header.indx++] = (unsigned char) (x >> 24) ;
+	psf->header.ptr [psf->header.indx++] = (unsigned char) (x >> 32) ;
+	psf->header.ptr [psf->header.indx++] = (unsigned char) (x >> 40) ;
+	psf->header.ptr [psf->header.indx++] = (unsigned char) (x >> 48) ;
+	psf->header.ptr [psf->header.indx++] = (x >> 56) ;
+} /* header_put_le_8byte */
+
+int
+psf_binheader_writef (SF_PRIVATE *psf, const char *format, ...)
+{	va_list	argptr ;
+	sf_count_t 		countdata ;
+	unsigned long 	longdata ;
+	unsigned int 	data ;
+	float			floatdata ;
+	double			doubledata ;
+	void			*bindata ;
+	size_t			size ;
+	char			c, *strptr ;
+	int				count = 0, trunc_8to4 = SF_FALSE ;
+
+	if (! format)
+		return psf_ftell (psf) ;
+
+	va_start (argptr, format) ;
+
+	while ((c = *format++))
+	{
+		if (psf->header.indx + 16 >= psf->header.len && psf_bump_header_allocation (psf, 16))
+			break ;
+
+		switch (c)
+		{	case ' ' : /* Do nothing. Just used to space out format string. */
+					break ;
+
+			case 'e' : /* All conversions are now from LE to host. */
+					psf->rwf_endian = SF_ENDIAN_LITTLE ;
+					break ;
+
+			case 'E' : /* All conversions are now from BE to host. */
+					psf->rwf_endian = SF_ENDIAN_BIG ;
+					break ;
+
+			case 't' : /* All 8 byte values now get written as 4 bytes. */
+					trunc_8to4 = SF_TRUE ;
+					break ;
+
+			case 'T' : /* All 8 byte values now get written as 8 bytes. */
+					trunc_8to4 = SF_FALSE ;
+					break ;
+
+			case 'm' :
+					data = va_arg (argptr, unsigned int) ;
+					header_put_marker (psf, data) ;
+					count += 4 ;
+					break ;
+
+			case '1' :
+					data = va_arg (argptr, unsigned int) ;
+					header_put_byte (psf, data) ;
+					count += 1 ;
+					break ;
+
+			case '2' :
+					data = va_arg (argptr, unsigned int) ;
+					if (psf->rwf_endian == SF_ENDIAN_BIG)
+					{	header_put_be_short (psf, data) ;
+						}
+					else
+					{	header_put_le_short (psf, data) ;
+						} ;
+					count += 2 ;
+					break ;
+
+			case '3' : /* tribyte */
+					data = va_arg (argptr, unsigned int) ;
+					if (psf->rwf_endian == SF_ENDIAN_BIG)
+					{	header_put_be_3byte (psf, data) ;
+						}
+					else
+					{	header_put_le_3byte (psf, data) ;
+						} ;
+					count += 3 ;
+					break ;
+
+			case '4' :
+					data = va_arg (argptr, unsigned int) ;
+					if (psf->rwf_endian == SF_ENDIAN_BIG)
+					{	header_put_be_int (psf, data) ;
+						}
+					else
+					{	header_put_le_int (psf, data) ;
+						} ;
+					count += 4 ;
+					break ;
+
+			case '8' :
+					countdata = va_arg (argptr, sf_count_t) ;
+					if (psf->rwf_endian == SF_ENDIAN_BIG && trunc_8to4 == SF_FALSE)
+					{	header_put_be_8byte (psf, countdata) ;
+						count += 8 ;
+						}
+					else if (psf->rwf_endian == SF_ENDIAN_LITTLE && trunc_8to4 == SF_FALSE)
+					{	header_put_le_8byte (psf, countdata) ;
+						count += 8 ;
+						}
+					else if (psf->rwf_endian == SF_ENDIAN_BIG && trunc_8to4 == SF_TRUE)
+					{	longdata = countdata & 0xFFFFFFFF ;
+						header_put_be_int (psf, longdata) ;
+						count += 4 ;
+						}
+					else if (psf->rwf_endian == SF_ENDIAN_LITTLE && trunc_8to4 == SF_TRUE)
+					{	longdata = countdata & 0xFFFFFFFF ;
+						header_put_le_int (psf, longdata) ;
+						count += 4 ;
+						}
+					break ;
+
+			case 'f' :
+					/* Floats are passed as doubles. Is this always true? */
+					floatdata = (float) va_arg (argptr, double) ;
+					if (psf->rwf_endian == SF_ENDIAN_BIG)
+						float32_be_write (floatdata, psf->header.ptr + psf->header.indx) ;
+					else
+						float32_le_write (floatdata, psf->header.ptr + psf->header.indx) ;
+					psf->header.indx += 4 ;
+					count += 4 ;
+					break ;
+
+			case 'd' :
+					doubledata = va_arg (argptr, double) ;
+					if (psf->rwf_endian == SF_ENDIAN_BIG)
+						double64_be_write (doubledata, psf->header.ptr + psf->header.indx) ;
+					else
+						double64_le_write (doubledata, psf->header.ptr + psf->header.indx) ;
+					psf->header.indx += 8 ;
+					count += 8 ;
+					break ;
+
+			case 's' :
+					/* Write a C string (guaranteed to have a zero terminator). */
+					strptr = va_arg (argptr, char *) ;
+					size = strlen (strptr) + 1 ;
+
+					if (psf->header.indx + 4 + (sf_count_t) size + (sf_count_t) (size & 1) > psf->header.len && psf_bump_header_allocation (psf, 4 + size + (size & 1)))
+						break ;
+
+					if (psf->rwf_endian == SF_ENDIAN_BIG)
+						header_put_be_int (psf, size + (size & 1)) ;
+					else
+						header_put_le_int (psf, size + (size & 1)) ;
+					memcpy (&(psf->header.ptr [psf->header.indx]), strptr, size) ;
+					size += (size & 1) ;
+					psf->header.indx += size ;
+					psf->header.ptr [psf->header.indx - 1] = 0 ;
+					count += 4 + size ;
+					break ;
+
+			case 'S' :
+					/*
+					**	Write an AIFF style string (no zero terminator but possibly
+					**	an extra pad byte if the string length is odd).
+					*/
+					strptr = va_arg (argptr, char *) ;
+					size = strlen (strptr) ;
+					if (psf->header.indx + 4 + (sf_count_t) size + (sf_count_t) (size & 1) > psf->header.len && psf_bump_header_allocation (psf, 4 + size + (size & 1)))
+						break ;
+					if (psf->rwf_endian == SF_ENDIAN_BIG)
+						header_put_be_int (psf, size) ;
+					else
+						header_put_le_int (psf, size) ;
+					memcpy (&(psf->header.ptr [psf->header.indx]), strptr, size + (size & 1)) ;
+					size += (size & 1) ;
+					psf->header.indx += size ;
+					count += 4 + size ;
+					break ;
+
+			case 'p' :
+					/* Write a PASCAL string (as used by AIFF files).
+					*/
+					strptr = va_arg (argptr, char *) ;
+					size = strlen (strptr) ;
+					size = (size & 1) ? size : size + 1 ;
+					size = (size > 254) ? 254 : size ;
+
+					if (psf->header.indx + 1 + (sf_count_t) size > psf->header.len && psf_bump_header_allocation (psf, 1 + size))
+						break ;
+
+					header_put_byte (psf, size) ;
+					memcpy (&(psf->header.ptr [psf->header.indx]), strptr, size) ;
+					psf->header.indx += size ;
+					count += 1 + size ;
+					break ;
+
+			case 'b' :
+					bindata	= va_arg (argptr, void *) ;
+					size	= va_arg (argptr, size_t) ;
+
+					if (psf->header.indx + (sf_count_t) size > psf->header.len && psf_bump_header_allocation (psf, size))
+						break ;
+
+					memcpy (&(psf->header.ptr [psf->header.indx]), bindata, size) ;
+					psf->header.indx += size ;
+					count += size ;
+					break ;
+
+			case 'z' :
+					size = va_arg (argptr, size_t) ;
+
+					if (psf->header.indx + (sf_count_t) size > psf->header.len && psf_bump_header_allocation (psf, size))
+						break ;
+
+					count += size ;
+					while (size)
+					{	psf->header.ptr [psf->header.indx] = 0 ;
+						psf->header.indx ++ ;
+						size -- ;
+						} ;
+					break ;
+
+			case 'h' :
+					bindata = va_arg (argptr, void *) ;
+					memcpy (&(psf->header.ptr [psf->header.indx]), bindata, 16) ;
+					psf->header.indx += 16 ;
+					count += 16 ;
+					break ;
+
+			case 'j' :	/* Jump forwards/backwards by specified amount. */
+					size = va_arg (argptr, size_t) ;
+
+					if (psf->header.indx + (sf_count_t) size > psf->header.len && psf_bump_header_allocation (psf, size))
+						break ;
+
+					psf->header.indx += size ;
+					count += size ;
+					break ;
+
+			case 'o' :	/* Jump to specified offset. */
+					size = va_arg (argptr, size_t) ;
+
+					if ((sf_count_t) size >= psf->header.len && psf_bump_header_allocation (psf, size))
+						break ;
+
+					psf->header.indx = size ;
+					break ;
+
+			default :
+				psf_log_printf (psf, "*** Invalid format specifier `%c'\n", c) ;
+				psf->error = SFE_INTERNAL ;
+				break ;
+			} ;
+		} ;
+
+	va_end (argptr) ;
+	return count ;
+} /* psf_binheader_writef */
+
+/*-----------------------------------------------------------------------------------------------
+**  Binary header reading functions. Returns number of bytes read.
+**
+**	Format specifiers are the same as for header write function above with the following
+**	additions:
+**
+**		p   - jump a given number of position from start of file.
+**
+**	If format is NULL, psf_binheader_readf returns the current offset.
+*/
+
+#if (CPU_IS_BIG_ENDIAN == 1)
+#define	GET_MARKER(ptr)	(	(((uint32_t) (ptr) [0]) << 24)	| ((ptr) [1] << 16) |	\
+							((ptr) [2] << 8)	| ((ptr) [3]))
+
+#elif (CPU_IS_LITTLE_ENDIAN == 1)
+#define	GET_MARKER(ptr)	(	((ptr) [0])			| ((ptr) [1] << 8) |	\
+							((ptr) [2] << 16)	| (((uint32_t) (ptr) [3]) << 24))
+
+#else
+#	error "Cannot determine endian-ness of processor."
+#endif
+
+#define	GET_LE_SHORT(ptr)	(((ptr) [1] << 8) | ((ptr) [0]))
+#define	GET_BE_SHORT(ptr)	(((ptr) [0] << 8) | ((ptr) [1]))
+
+#define	GET_LE_3BYTE(ptr)	(	((ptr) [2] << 16) | ((ptr) [1] << 8) | ((ptr) [0]))
+#define	GET_BE_3BYTE(ptr)	(	((ptr) [0] << 16) | ((ptr) [1] << 8) | ((ptr) [2]))
+
+#define	GET_LE_INT(ptr)		(	((ptr) [3] << 24)	| ((ptr) [2] << 16) |	\
+								((ptr) [1] << 8)	| ((ptr) [0]))
+
+#define	GET_BE_INT(ptr)		(	((ptr) [0] << 24)	| ((ptr) [1] << 16) |	\
+								((ptr) [2] << 8)	| ((ptr) [3]))
+
+#define	GET_LE_8BYTE(ptr)	(	(((sf_count_t) (ptr) [7]) << 56)	| (((sf_count_t) (ptr) [6]) << 48) |	\
+								(((sf_count_t) (ptr) [5]) << 40)	| (((sf_count_t) (ptr) [4]) << 32) |	\
+								(((sf_count_t) (ptr) [3]) << 24)	| (((sf_count_t) (ptr) [2]) << 16) |	\
+								(((sf_count_t) (ptr) [1]) << 8)		| ((ptr) [0]))
+
+#define	GET_BE_8BYTE(ptr)	(	(((sf_count_t) (ptr) [0]) << 56)	| (((sf_count_t) (ptr) [1]) << 48) |	\
+								(((sf_count_t) (ptr) [2]) << 40)	| (((sf_count_t) (ptr) [3]) << 32) |	\
+								(((sf_count_t) (ptr) [4]) << 24)	| (((sf_count_t) (ptr) [5]) << 16) |	\
+								(((sf_count_t) (ptr) [6]) << 8)		| ((ptr) [7]))
+
+
+
+static int
+header_read (SF_PRIVATE *psf, void *ptr, int bytes)
+{	int count = 0 ;
+
+	if (psf->header.indx + bytes >= psf->header.len && psf_bump_header_allocation (psf, bytes))
+		return count ;
+
+	if (psf->header.indx + bytes > psf->header.end)
+	{	count = psf_fread (psf->header.ptr + psf->header.end, 1, bytes - (psf->header.end - psf->header.indx), psf) ;
+		if (count != bytes - (int) (psf->header.end - psf->header.indx))
+		{	psf_log_printf (psf, "Error : psf_fread returned short count.\n") ;
+			return count ;
+			} ;
+		psf->header.end += count ;
+		} ;
+
+	memcpy (ptr, psf->header.ptr + psf->header.indx, bytes) ;
+	psf->header.indx += bytes ;
+
+	return bytes ;
+} /* header_read */
+
+static void
+header_seek (SF_PRIVATE *psf, sf_count_t position, int whence)
+{
+	switch (whence)
+	{	case SEEK_SET :
+			if (psf->header.indx + position >= psf->header.len)
+				psf_bump_header_allocation (psf, position) ;
+			if (position > psf->header.len)
+			{	/* Too much header to cache so just seek instead. */
+				psf->header.indx = psf->header.end = 0 ;
+				psf_fseek (psf, position, whence) ;
+				return ;
+				} ;
+			if (position > psf->header.end)
+				psf->header.end += psf_fread (psf->header.ptr + psf->header.end, 1, position - psf->header.end, psf) ;
+			psf->header.indx = position ;
+			break ;
+
+		case SEEK_CUR :
+			if (psf->header.indx + position >= psf->header.len)
+				psf_bump_header_allocation (psf, position) ;
+
+			if (psf->header.indx + position < 0)
+				break ;
+
+			if (psf->header.indx >= psf->header.len)
+			{	psf_fseek (psf, position, whence) ;
+				return ;
+				} ;
+
+			if (psf->header.indx + position <= psf->header.end)
+			{	psf->header.indx += position ;
+				break ;
+				} ;
+
+			if (psf->header.indx + position > psf->header.len)
+			{	/* Need to jump this without caching it. */
+				position -= (psf->header.end - psf->header.indx) ;
+				psf->header.indx = psf->header.end ;
+				if (psf->is_pipe)
+				{
+					/* seeking is not supported on pipe input, so we read instead */
+					size_t skip = position ;
+					while (skip)
+					{	char junk [16 * 1024] ;
+						size_t to_skip = SF_MIN (skip, sizeof (junk)) ;
+						psf_fread (junk, 1, to_skip, psf) ;
+						skip -= to_skip ;
+						}
+					}
+				else
+				{	psf_fseek (psf, position, SEEK_CUR) ;
+					}
+				break ;
+				} ;
+
+			psf->header.end += psf_fread (psf->header.ptr + psf->header.end, 1, position - (psf->header.end - psf->header.indx), psf) ;
+			psf->header.indx = psf->header.end ;
+			break ;
+
+		case SEEK_END :
+		default :
+			psf_log_printf (psf, "Bad whence param in header_seek().\n") ;
+			break ;
+		} ;
+
+	return ;
+} /* header_seek */
+
+static int
+header_gets (SF_PRIVATE *psf, char *ptr, int bufsize)
+{	int		k ;
+
+	if (psf->header.indx + bufsize >= psf->header.len && psf_bump_header_allocation (psf, bufsize))
+		return 0 ;
+
+	for (k = 0 ; k < bufsize - 1 ; k++)
+	{	if (psf->header.indx < psf->header.end)
+		{	ptr [k] = psf->header.ptr [psf->header.indx] ;
+			psf->header.indx ++ ;
+			}
+		else
+		{	psf->header.end += psf_fread (psf->header.ptr + psf->header.end, 1, 1, psf) ;
+			ptr [k] = psf->header.ptr [psf->header.indx] ;
+			psf->header.indx = psf->header.end ;
+			} ;
+
+		if (ptr [k] == '\n')
+			break ;
+		} ;
+
+	ptr [k] = 0 ;
+
+	return k ;
+} /* header_gets */
+
+int
+psf_binheader_readf (SF_PRIVATE *psf, char const *format, ...)
+{	va_list			argptr ;
+	sf_count_t		*countptr, countdata ;
+	unsigned char	*ucptr, sixteen_bytes [16] = { 0 } ;
+	unsigned int 	*intptr, intdata ;
+	unsigned short	*shortptr ;
+	char			*charptr ;
+	float			*floatptr ;
+	double			*doubleptr ;
+	char			c ;
+	int				byte_count = 0, count = 0 ;
+
+	if (! format)
+		return psf_ftell (psf) ;
+
+	va_start (argptr, format) ;
+
+	while ((c = *format++))
+	{
+		if (psf->header.indx + 16 >= psf->header.len && psf_bump_header_allocation (psf, 16))
+			break ;
+
+		switch (c)
+		{	case 'e' : /* All conversions are now from LE to host. */
+					psf->rwf_endian = SF_ENDIAN_LITTLE ;
+					break ;
+
+			case 'E' : /* All conversions are now from BE to host. */
+					psf->rwf_endian = SF_ENDIAN_BIG ;
+					break ;
+
+			case 'm' : /* 4 byte marker value eg 'RIFF' */
+					intptr = va_arg (argptr, unsigned int*) ;
+					*intptr = 0 ;
+					ucptr = (unsigned char*) intptr ;
+					byte_count += header_read (psf, ucptr, sizeof (int)) ;
+					*intptr = GET_MARKER (ucptr) ;
+					break ;
+
+			case 'h' :
+					intptr = va_arg (argptr, unsigned int*) ;
+					*intptr = 0 ;
+					ucptr = (unsigned char*) intptr ;
+					byte_count += header_read (psf, sixteen_bytes, sizeof (sixteen_bytes)) ;
+					{	int k ;
+						intdata = 0 ;
+						for (k = 0 ; k < 16 ; k++)
+							intdata ^= sixteen_bytes [k] << k ;
+						}
+					*intptr = intdata ;
+					break ;
+
+			case '1' :
+					charptr = va_arg (argptr, char*) ;
+					*charptr = 0 ;
+					byte_count += header_read (psf, charptr, sizeof (char)) ;
+					break ;
+
+			case '2' : /* 2 byte value with the current endian-ness */
+					shortptr = va_arg (argptr, unsigned short*) ;
+					*shortptr = 0 ;
+					ucptr = (unsigned char*) shortptr ;
+					byte_count += header_read (psf, ucptr, sizeof (short)) ;
+					if (psf->rwf_endian == SF_ENDIAN_BIG)
+						*shortptr = GET_BE_SHORT (ucptr) ;
+					else
+						*shortptr = GET_LE_SHORT (ucptr) ;
+					break ;
+
+			case '3' : /* 3 byte value with the current endian-ness */
+					intptr = va_arg (argptr, unsigned int*) ;
+					*intptr = 0 ;
+					byte_count += header_read (psf, sixteen_bytes, 3) ;
+					if (psf->rwf_endian == SF_ENDIAN_BIG)
+						*intptr = GET_BE_3BYTE (sixteen_bytes) ;
+					else
+						*intptr = GET_LE_3BYTE (sixteen_bytes) ;
+					break ;
+
+			case '4' : /* 4 byte value with the current endian-ness */
+					intptr = va_arg (argptr, unsigned int*) ;
+					*intptr = 0 ;
+					ucptr = (unsigned char*) intptr ;
+					byte_count += header_read (psf, ucptr, sizeof (int)) ;
+					if (psf->rwf_endian == SF_ENDIAN_BIG)
+						*intptr = psf_get_be32 (ucptr, 0) ;
+					else
+						*intptr = psf_get_le32 (ucptr, 0) ;
+					break ;
+
+			case '8' : /* 8 byte value with the current endian-ness */
+					countptr = va_arg (argptr, sf_count_t *) ;
+					*countptr = 0 ;
+					byte_count += header_read (psf, sixteen_bytes, 8) ;
+					if (psf->rwf_endian == SF_ENDIAN_BIG)
+						countdata = psf_get_be64 (sixteen_bytes, 0) ;
+					else
+						countdata = psf_get_le64 (sixteen_bytes, 0) ;
+					*countptr = countdata ;
+					break ;
+
+			case 'f' : /* Float conversion */
+					floatptr = va_arg (argptr, float *) ;
+					*floatptr = 0.0 ;
+					byte_count += header_read (psf, floatptr, sizeof (float)) ;
+					if (psf->rwf_endian == SF_ENDIAN_BIG)
+						*floatptr = float32_be_read ((unsigned char*) floatptr) ;
+					else
+						*floatptr = float32_le_read ((unsigned char*) floatptr) ;
+					break ;
+
+			case 'd' : /* double conversion */
+					doubleptr = va_arg (argptr, double *) ;
+					*doubleptr = 0.0 ;
+					byte_count += header_read (psf, doubleptr, sizeof (double)) ;
+					if (psf->rwf_endian == SF_ENDIAN_BIG)
+						*doubleptr = double64_be_read ((unsigned char*) doubleptr) ;
+					else
+						*doubleptr = double64_le_read ((unsigned char*) doubleptr) ;
+					break ;
+
+			case 's' :
+					psf_log_printf (psf, "Format conversion 's' not implemented yet.\n") ;
+					/*
+					strptr = va_arg (argptr, char *) ;
+					size   = strlen (strptr) + 1 ;
+					size  += (size & 1) ;
+					longdata = H2LE_32 (size) ;
+					get_int (psf, longdata) ;
+					memcpy (&(psf->header.ptr [psf->header.indx]), strptr, size) ;
+					psf->header.indx += size ;
+					*/
+					break ;
+
+			case 'b' : /* Raw bytes */
+					charptr = va_arg (argptr, char*) ;
+					count = va_arg (argptr, size_t) ;
+					memset (charptr, 0, count) ;
+					byte_count += header_read (psf, charptr, count) ;
+					break ;
+
+			case 'G' :
+					charptr = va_arg (argptr, char*) ;
+					count = va_arg (argptr, size_t) ;
+					memset (charptr, 0, count) ;
+
+					if (psf->header.indx + count >= psf->header.len && psf_bump_header_allocation (psf, count))
+						break ;
+
+					byte_count += header_gets (psf, charptr, count) ;
+					break ;
+
+			case 'z' :
+					psf_log_printf (psf, "Format conversion 'z' not implemented yet.\n") ;
+					/*
+					size    = va_arg (argptr, size_t) ;
+					while (size)
+					{	psf->header.ptr [psf->header.indx] = 0 ;
+						psf->header.indx ++ ;
+						size -- ;
+						} ;
+					*/
+					break ;
+
+			case 'p' :	/* Seek to position from start. */
+					count = va_arg (argptr, size_t) ;
+					header_seek (psf, count, SEEK_SET) ;
+					byte_count = count ;
+					break ;
+
+			case 'j' :	/* Seek to position from current position. */
+					count = va_arg (argptr, size_t) ;
+					header_seek (psf, count, SEEK_CUR) ;
+					byte_count += count ;
+					break ;
+
+			case '!' : /* Clear buffer, forcing re-read. */
+					psf->header.end = psf->header.indx = 0 ;
+					break ;
+
+			default :
+				psf_log_printf (psf, "*** Invalid format specifier `%c'\n", c) ;
+				psf->error = SFE_INTERNAL ;
+				break ;
+			} ;
+		} ;
+
+	va_end (argptr) ;
+
+	return byte_count ;
+} /* psf_binheader_readf */
+
+/*-----------------------------------------------------------------------------------------------
+*/
+
+sf_count_t
+psf_default_seek (SF_PRIVATE *psf, int UNUSED (mode), sf_count_t samples_from_start)
+{	sf_count_t position, retval ;
+
+	if (! (psf->blockwidth && psf->dataoffset >= 0))
+	{	psf->error = SFE_BAD_SEEK ;
+		return	PSF_SEEK_ERROR ;
+		} ;
+
+	if (! psf->sf.seekable)
+	{	psf->error = SFE_NOT_SEEKABLE ;
+		return	PSF_SEEK_ERROR ;
+		} ;
+
+	position = psf->dataoffset + psf->blockwidth * samples_from_start ;
+
+	if ((retval = psf_fseek (psf, position, SEEK_SET)) != position)
+	{	psf->error = SFE_SEEK_FAILED ;
+		return PSF_SEEK_ERROR ;
+		} ;
+
+	return samples_from_start ;
+} /* psf_default_seek */
+
+/*-----------------------------------------------------------------------------------------------
+*/
+
+void
+psf_hexdump (const void *ptr, int len)
+{	const char *data ;
+	char	ascii [17] ;
+	int		k, m ;
+
+	if ((data = ptr) == NULL)
+		return ;
+	if (len <= 0)
+		return ;
+
+	puts ("") ;
+	for (k = 0 ; k < len ; k += 16)
+	{	memset (ascii, ' ', sizeof (ascii)) ;
+
+		printf ("%08X: ", k) ;
+		for (m = 0 ; m < 16 && k + m < len ; m++)
+		{	printf (m == 8 ? " %02X " : "%02X ", data [k + m] & 0xFF) ;
+			ascii [m] = psf_isprint (data [k + m]) ? data [k + m] : '.' ;
+			} ;
+
+		if (m <= 8) printf (" ") ;
+		for ( ; m < 16 ; m++) printf ("   ") ;
+
+		ascii [16] = 0 ;
+		printf (" %s\n", ascii) ;
+		} ;
+
+	puts ("") ;
+} /* psf_hexdump */
+
+void
+psf_log_SF_INFO (SF_PRIVATE *psf)
+{	psf_log_printf (psf, "---------------------------------\n") ;
+
+	psf_log_printf (psf, " Sample rate :   %d\n", psf->sf.samplerate) ;
+	if (psf->sf.frames == SF_COUNT_MAX)
+		psf_log_printf (psf, " Frames      :   unknown\n") ;
+	else
+		psf_log_printf (psf, " Frames      :   %D\n", psf->sf.frames) ;
+	psf_log_printf (psf, " Channels    :   %d\n", psf->sf.channels) ;
+
+	psf_log_printf (psf, " Format      :   0x%X\n", psf->sf.format) ;
+	psf_log_printf (psf, " Sections    :   %d\n", psf->sf.sections) ;
+	psf_log_printf (psf, " Seekable    :   %s\n", psf->sf.seekable ? "TRUE" : "FALSE") ;
+
+	psf_log_printf (psf, "---------------------------------\n") ;
+} /* psf_dump_SFINFO */
+
+/*========================================================================================
+*/
+
+int
+psf_isprint (int ch)
+{	return (ch >= ' ' && ch <= '~') ;
+} /* psf_isprint */
+
+void
+psf_strlcat (char *dest, size_t n, const char *src)
+{	strncat (dest, src, n - strlen (dest) - 1) ;
+	dest [n - 1] = 0 ;
+} /* psf_strlcat */
+
+void
+psf_strlcpy (char *dest, size_t n, const char *src)
+{	strncpy (dest, src, n - 1) ;
+	dest [n - 1] = 0 ;
+} /* psf_strlcpy */
+
+/*========================================================================================
+*/
+
+void *
+psf_memdup (const void *src, size_t n)
+{	if (src == NULL)
+		return NULL ;
+
+	void * mem = calloc (1, n & 3 ? n + 4 - (n & 3) : n) ;
+	if (mem != NULL)
+		memcpy (mem, src, n) ;
+	return mem ;
+} /* psf_memdup */
+
+void*
+psf_memset (void *s, int c, sf_count_t len)
+{	char	*ptr ;
+	int 	setcount ;
+
+	ptr = (char *) s ;
+
+	while (len > 0)
+	{	setcount = (len > 0x10000000) ? 0x10000000 : (int) len ;
+
+		memset (ptr, c, setcount) ;
+
+		ptr += setcount ;
+		len -= setcount ;
+		} ;
+
+	return s ;
+} /* psf_memset */
+
+
+/*
+** Clang refuses to do sizeof (SF_CUES_VAR (cue_count)) so we have to manually
+** bodgy something up instead.
+*/
+
+#ifdef _MSC_VER
+typedef SF_CUES_VAR (0) SF_CUES_0 ;
+#else
+typedef SF_CUES_VAR () SF_CUES_0 ;
+#endif
+
+/* calculate size of SF_CUES struct given number of cues */
+#define SF_CUES_VAR_SIZE(count)	(sizeof (SF_CUES_0) + count * sizeof (SF_CUE_POINT))
+
+/* calculate number of cues in SF_CUES struct given data size */
+#define SF_CUES_COUNT(datasize) (((datasize) - sizeof (uint32_t)) / sizeof (SF_CUE_POINT))
+
+SF_CUES *
+psf_cues_alloc (uint32_t cue_count)
+{	SF_CUES *pcues = calloc (1, SF_CUES_VAR_SIZE (cue_count)) ;
+	if (pcues)
+	{	pcues->cue_count = cue_count ;
+		} ;
+	return pcues ;
+} /* psf_cues_alloc */
+
+SF_CUES *
+psf_cues_dup (const void * ptr, size_t datasize)
+{	const SF_CUES *pcues = ptr ;
+	SF_CUES *pnew = NULL ;
+
+	if (pcues->cue_count <= SF_CUES_COUNT (datasize))
+	{	/* check that passed-in datasize is consistent with cue_count in passed-in SF_CUES struct */
+		pnew = psf_cues_alloc (pcues->cue_count) ;
+		memcpy (pnew, pcues, SF_CUES_VAR_SIZE (pcues->cue_count)) ;
+	}
+
+	return pnew ;
+} /* psf_cues_dup */
+
+void
+psf_get_cues (SF_PRIVATE * psf, void * data, size_t datasize)
+{
+	if (psf->cues)
+	{	uint32_t cue_count = SF_CUES_COUNT (datasize) ;
+
+		cue_count = SF_MIN (cue_count, psf->cues->cue_count) ;
+		memcpy (data, psf->cues, SF_CUES_VAR_SIZE (cue_count)) ;
+		((SF_CUES*) data)->cue_count = cue_count ;
+		} ;
+
+	return ;
+} /* psf_get_cues */
+
+
+SF_INSTRUMENT *
+psf_instrument_alloc (void)
+{	SF_INSTRUMENT *instr ;
+
+	instr = calloc (1, sizeof (SF_INSTRUMENT)) ;
+
+	if (instr == NULL)
+		return NULL ;
+
+	/* Set non-zero default values. */
+	instr->basenote = -1 ;
+	instr->velocity_lo = -1 ;
+	instr->velocity_hi = -1 ;
+	instr->key_lo = -1 ;
+	instr->key_hi = -1 ;
+
+	return instr ;
+} /* psf_instrument_alloc */
+
+void
+psf_sanitize_string (char * cptr, int len)
+{
+	do
+	{
+		len -- ;
+		cptr [len] = psf_isprint (cptr [len]) ? cptr [len] : '.' ;
+	}
+	while (len > 0) ;
+} /* psf_sanitize_string */
+
+void
+psf_get_date_str (char *str, int maxlen)
+{	time_t		current ;
+	struct tm	timedata, *tmptr ;
+
+	time (&current) ;
+
+#if defined (HAVE_GMTIME_R)
+	/* If the re-entrant version is available, use it. */
+	tmptr = gmtime_r (&current, &timedata) ;
+#elif defined (HAVE_GMTIME)
+	/* Otherwise use the standard one and copy the data to local storage. */
+	tmptr = gmtime (&current) ;
+	memcpy (&timedata, tmptr, sizeof (timedata)) ;
+#else
+	tmptr = NULL ;
+#endif
+
+	if (tmptr)
+		snprintf (str, maxlen, "%4d-%02d-%02d %02d:%02d:%02d UTC",
+			1900 + timedata.tm_year, timedata.tm_mon, timedata.tm_mday,
+			timedata.tm_hour, timedata.tm_min, timedata.tm_sec) ;
+	else
+		snprintf (str, maxlen, "Unknown date") ;
+
+	return ;
+} /* psf_get_date_str */
+
+int
+subformat_to_bytewidth (int format)
+{
+	switch (format)
+	{	case SF_FORMAT_PCM_U8 :
+		case SF_FORMAT_PCM_S8 :
+				return 1 ;
+		case SF_FORMAT_PCM_16 :
+				return 2 ;
+		case SF_FORMAT_PCM_24 :
+				return 3 ;
+		case SF_FORMAT_PCM_32 :
+		case SF_FORMAT_FLOAT :
+				return 4 ;
+		case SF_FORMAT_DOUBLE :
+				return 8 ;
+		} ;
+
+	return 0 ;
+} /* subformat_to_bytewidth */
+
+int
+s_bitwidth_to_subformat (int bits)
+{	static int array [] =
+	{	SF_FORMAT_PCM_S8, SF_FORMAT_PCM_16, SF_FORMAT_PCM_24, SF_FORMAT_PCM_32
+		} ;
+
+	if (bits < 8 || bits > 32)
+		return 0 ;
+
+	return array [((bits + 7) / 8) - 1] ;
+} /* bitwidth_to_subformat */
+
+int
+u_bitwidth_to_subformat (int bits)
+{	static int array [] =
+	{	SF_FORMAT_PCM_U8, SF_FORMAT_PCM_16, SF_FORMAT_PCM_24, SF_FORMAT_PCM_32
+		} ;
+
+	if (bits < 8 || bits > 32)
+		return 0 ;
+
+	return array [((bits + 7) / 8) - 1] ;
+} /* bitwidth_to_subformat */
+
+/*
+**	psf_rand_int32 : Not crypto quality, but more than adequate for things
+**	like stream serial numbers in Ogg files or the unique_id field of the
+**	SF_PRIVATE struct.
+*/
+
+int32_t
+psf_rand_int32 (void)
+{	static uint64_t value = 0 ;
+	int k, count ;
+
+	if (value == 0)
+	{
+#if HAVE_GETTIMEOFDAY
+		struct timeval tv ;
+		gettimeofday (&tv, NULL) ;
+		value = tv.tv_sec + tv.tv_usec ;
+#else
+		value = time (NULL) ;
+#endif
+		} ;
+
+	count = 4 + (value & 7) ;
+	for (k = 0 ; k < count ; k++)
+		value = (11117 * value + 211231) & 0x7fffffff ;
+
+	return (int32_t) value ;
+} /* psf_rand_int32 */
+
+void
+append_snprintf (char * dest, size_t maxlen, const char * fmt, ...)
+{	size_t len = strlen (dest) ;
+
+	if (len < maxlen)
+	{	va_list ap ;
+
+		va_start (ap, fmt) ;
+		vsnprintf (dest + len, maxlen - len, fmt, ap) ;
+		va_end (ap) ;
+		} ;
+
+	return ;
+} /* append_snprintf */
+
+
+void
+psf_strlcpy_crlf (char *dest, const char *src, size_t destmax, size_t srcmax)
+{	/* Must be minus 2 so it can still expand a single trailing '\n' or '\r'. */
+	char * destend = dest + destmax - 2 ;
+	const char * srcend = src + srcmax ;
+
+	while (dest < destend && src < srcend)
+	{	if ((src [0] == '\r' && src [1] == '\n') || (src [0] == '\n' && src [1] == '\r'))
+		{	*dest++ = '\r' ;
+			*dest++ = '\n' ;
+			src += 2 ;
+			continue ;
+			} ;
+
+		if (src [0] == '\r')
+		{	*dest++ = '\r' ;
+			*dest++ = '\n' ;
+			src += 1 ;
+			continue ;
+			} ;
+
+		if (src [0] == '\n')
+		{	*dest++ = '\r' ;
+			*dest++ = '\n' ;
+			src += 1 ;
+			continue ;
+			} ;
+
+		*dest++ = *src++ ;
+		} ;
+
+	/* Make sure dest is terminated. */
+	*dest = 0 ;
+} /* psf_strlcpy_crlf */
+
+sf_count_t
+psf_decode_frame_count (SF_PRIVATE *psf)
+{	sf_count_t count, readlen, total = 0 ;
+	BUF_UNION	ubuf ;
+
+	/* If we're reading from a pipe or the file is too long, just return SF_COUNT_MAX. */
+	if (psf_is_pipe (psf) || psf->datalength > 0x1000000)
+		return SF_COUNT_MAX ;
+
+	psf_fseek (psf, psf->dataoffset, SEEK_SET) ;
+
+	readlen = ARRAY_LEN (ubuf.ibuf) / psf->sf.channels ;
+	readlen *= psf->sf.channels ;
+
+	while ((count = psf->read_int (psf, ubuf.ibuf, readlen)) > 0)
+		total += count ;
+
+	psf_fseek (psf, psf->dataoffset, SEEK_SET) ;
+
+	return total / psf->sf.channels ;
+} /* psf_decode_frame_count */
+
+/*==============================================================================
+*/
+
+#define CASE_NAME(x)		case x : return #x ; break ;
+
+const char *
+str_of_major_format (int format)
+{	switch (SF_CONTAINER (format))
+	{	CASE_NAME (SF_FORMAT_WAV) ;
+		CASE_NAME (SF_FORMAT_AIFF) ;
+		CASE_NAME (SF_FORMAT_AU) ;
+		CASE_NAME (SF_FORMAT_RAW) ;
+		CASE_NAME (SF_FORMAT_PAF) ;
+		CASE_NAME (SF_FORMAT_SVX) ;
+		CASE_NAME (SF_FORMAT_NIST) ;
+		CASE_NAME (SF_FORMAT_VOC) ;
+		CASE_NAME (SF_FORMAT_IRCAM) ;
+		CASE_NAME (SF_FORMAT_W64) ;
+		CASE_NAME (SF_FORMAT_MAT4) ;
+		CASE_NAME (SF_FORMAT_MAT5) ;
+		CASE_NAME (SF_FORMAT_PVF) ;
+		CASE_NAME (SF_FORMAT_XI) ;
+		CASE_NAME (SF_FORMAT_HTK) ;
+		CASE_NAME (SF_FORMAT_SDS) ;
+		CASE_NAME (SF_FORMAT_AVR) ;
+		CASE_NAME (SF_FORMAT_WAVEX) ;
+		CASE_NAME (SF_FORMAT_SD2) ;
+		CASE_NAME (SF_FORMAT_FLAC) ;
+		CASE_NAME (SF_FORMAT_CAF) ;
+		CASE_NAME (SF_FORMAT_WVE) ;
+		CASE_NAME (SF_FORMAT_OGG) ;
+		CASE_NAME (SF_FORMAT_MPEG) ;
+		default :
+			break ;
+		} ;
+
+	return "BAD_MAJOR_FORMAT" ;
+} /* str_of_major_format */
+
+const char *
+str_of_minor_format (int format)
+{	switch (SF_CODEC (format))
+	{	CASE_NAME (SF_FORMAT_PCM_S8) ;
+		CASE_NAME (SF_FORMAT_PCM_16) ;
+		CASE_NAME (SF_FORMAT_PCM_24) ;
+		CASE_NAME (SF_FORMAT_PCM_32) ;
+		CASE_NAME (SF_FORMAT_PCM_U8) ;
+		CASE_NAME (SF_FORMAT_FLOAT) ;
+		CASE_NAME (SF_FORMAT_DOUBLE) ;
+		CASE_NAME (SF_FORMAT_ULAW) ;
+		CASE_NAME (SF_FORMAT_ALAW) ;
+		CASE_NAME (SF_FORMAT_IMA_ADPCM) ;
+		CASE_NAME (SF_FORMAT_MS_ADPCM) ;
+		CASE_NAME (SF_FORMAT_GSM610) ;
+		CASE_NAME (SF_FORMAT_VOX_ADPCM) ;
+		CASE_NAME (SF_FORMAT_NMS_ADPCM_16) ;
+		CASE_NAME (SF_FORMAT_NMS_ADPCM_24) ;
+		CASE_NAME (SF_FORMAT_NMS_ADPCM_32) ;
+		CASE_NAME (SF_FORMAT_G721_32) ;
+		CASE_NAME (SF_FORMAT_G723_24) ;
+		CASE_NAME (SF_FORMAT_G723_40) ;
+		CASE_NAME (SF_FORMAT_DWVW_12) ;
+		CASE_NAME (SF_FORMAT_DWVW_16) ;
+		CASE_NAME (SF_FORMAT_DWVW_24) ;
+		CASE_NAME (SF_FORMAT_DWVW_N) ;
+		CASE_NAME (SF_FORMAT_DPCM_8) ;
+		CASE_NAME (SF_FORMAT_DPCM_16) ;
+		CASE_NAME (SF_FORMAT_VORBIS) ;
+		CASE_NAME (SF_FORMAT_MPEG_LAYER_I) ;
+		CASE_NAME (SF_FORMAT_MPEG_LAYER_II) ;
+		CASE_NAME (SF_FORMAT_MPEG_LAYER_III) ;
+		default :
+			break ;
+		} ;
+
+	return "BAD_MINOR_FORMAT" ;
+} /* str_of_minor_format */
+
+const char *
+str_of_open_mode (int mode)
+{	switch (mode)
+	{	CASE_NAME (SFM_READ) ;
+		CASE_NAME (SFM_WRITE) ;
+		CASE_NAME (SFM_RDWR) ;
+
+		default :
+			break ;
+		} ;
+
+	return "BAD_MODE" ;
+} /* str_of_open_mode */
+
+const char *
+str_of_endianness (int end)
+{	switch (end)
+	{	CASE_NAME (SF_ENDIAN_BIG) ;
+		CASE_NAME (SF_ENDIAN_LITTLE) ;
+		CASE_NAME (SF_ENDIAN_CPU) ;
+		default :
+			break ;
+		} ;
+
+	/* Zero length string for SF_ENDIAN_FILE. */
+	return "" ;
+} /* str_of_endianness */
+
+/*==============================================================================
+*/
+
+void
+psf_f2s_array (const float *src, short *dest, int count, int normalize)
+{	float 			normfact ;
+
+	normfact = normalize ? (1.0 * 0x7FFF) : 1.0 ;
+	for (int i = 0 ; i < count ; i++)
+		dest [i] = psf_lrintf (src [i] * normfact) ;
+
+	return ;
+} /* psf_f2s_array */
+
+void
+psf_f2s_clip_array (const float *src, short *dest, int count, int normalize)
+{	float			normfact, scaled_value ;
+
+	normfact = normalize ? (1.0 * 0x8000) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	scaled_value = src [i] * normfact ;
+		if (scaled_value >= (1.0 * 0x7FFF))
+		{	dest [i] = 0x7FFF ;
+			continue ;
+			} ;
+		if (scaled_value <= (-8.0 * 0x1000))
+		{	dest [i] = -0x7FFF - 1 ;
+			continue ;
+			} ;
+
+		dest [i] = psf_lrintf (scaled_value) ;
+		} ;
+
+	return ;
+} /* psf_f2s_clip_array */
+
+void
+psf_d2s_array (const double *src, short *dest, int count, int normalize)
+{	double 			normfact ;
+
+	normfact = normalize ? (1.0 * 0x7FFF) : 1.0 ;
+	for (int i = 0 ; i < count ; i++)
+		dest [i] = psf_lrint (src [i] * normfact) ;
+
+	return ;
+} /* psf_f2s_array */
+
+void
+psf_d2s_clip_array (const double *src, short *dest, int count, int normalize)
+{	double			normfact, scaled_value ;
+
+	normfact = normalize ? (1.0 * 0x8000) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	scaled_value = src [i] * normfact ;
+		if (scaled_value >= (1.0 * 0x7FFF))
+		{	dest [i] = 0x7FFF ;
+			continue ;
+			} ;
+		if (scaled_value <= (-8.0 * 0x1000))
+		{	dest [i] = -0x7FFF - 1 ;
+			continue ;
+			} ;
+
+		dest [i] = psf_lrint (scaled_value) ;
+		} ;
+
+	return ;
+} /* psf_d2s_clip_array */
+
+
+void
+psf_f2i_array (const float *src, int *dest, int count, int normalize)
+{	float 			normfact ;
+
+	normfact = normalize ? (1.0 * 0x7FFFFFFF) : 1.0 ;
+	for (int i = 0 ; i < count ; i++)
+		dest [i] = psf_lrintf (src [i] * normfact) ;
+
+	return ;
+} /* psf_f2i_array */
+
+void
+psf_f2i_clip_array (const float *src, int *dest, int count, int normalize)
+{	float			normfact, scaled_value ;
+
+	normfact = normalize ? (8.0 * 0x10000000) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	scaled_value = src [i] * normfact ;
+#if CPU_CLIPS_POSITIVE == 0
+		if (scaled_value >= (1.0 * 0x7FFFFFFF))
+		{	dest [i] = 0x7FFFFFFF ;
+			continue ;
+			} ;
+#endif
+#if CPU_CLIPS_NEGATIVE == 0
+		if (scaled_value <= (-8.0 * 0x10000000))
+		{	dest [i] = 0x80000000 ;
+			continue ;
+			} ;
+#endif
+
+		dest [i] = psf_lrintf (scaled_value) ;
+		} ;
+
+	return ;
+} /* psf_f2i_clip_array */
+
+void
+psf_d2i_array (const double *src, int *dest, int count, int normalize)
+{	double 			normfact ;
+
+	normfact = normalize ? (1.0 * 0x7FFFFFFF) : 1.0 ;
+	for (int i = 0 ; i < count ; i++)
+		dest [i] = psf_lrint (src [i] * normfact) ;
+
+	return ;
+} /* psf_f2i_array */
+
+void
+psf_d2i_clip_array (const double *src, int *dest, int count, int normalize)
+{	double			normfact, scaled_value ;
+
+	normfact = normalize ? (8.0 * 0x10000000) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	scaled_value = src [i] * normfact ;
+#if CPU_CLIPS_POSITIVE == 0
+		if (scaled_value >= (1.0 * 0x7FFFFFFF))
+		{	dest [i] = 0x7FFFFFFF ;
+			continue ;
+			} ;
+#endif
+#if CPU_CLIPS_NEGATIVE == 0
+		if (scaled_value <= (-8.0 * 0x10000000))
+		{	dest [i] = 0x80000000 ;
+			continue ;
+			} ;
+#endif
+
+		dest [i] = psf_lrint (scaled_value) ;
+		} ;
+
+	return ;
+} /* psf_d2i_clip_array */
+
+FILE *
+psf_open_tmpfile (char * fname, size_t fnamelen)
+{	const char * tmpdir ;
+	FILE * file ;
+
+	if (OS_IS_WIN32)
+		tmpdir = getenv ("TEMP") ;
+	else
+	{	tmpdir = getenv ("TMPDIR") ;
+		tmpdir = tmpdir == NULL ? "/tmp" : tmpdir ;
+		} ;
+
+	if (tmpdir && access (tmpdir, R_OK | W_OK | X_OK) == 0)
+	{	snprintf (fname, fnamelen, "%s/%x%x-alac.tmp", tmpdir, psf_rand_int32 (), psf_rand_int32 ()) ;
+		if ((file = fopen (fname, "wb+")) != NULL)
+			return file ;
+		} ;
+
+	snprintf (fname, fnamelen, "%x%x-alac.tmp", psf_rand_int32 (), psf_rand_int32 ()) ;
+	if ((file = fopen (fname, "wb+")) != NULL)
+		return file ;
+
+	memset (fname, 0, fnamelen) ;
+	return NULL ;
+} /* psf_open_tmpfile */
diff --git a/src/common.h b/src/common.h
new file mode 100644
index 0000000..d92eabd
--- /dev/null
+++ b/src/common.h
@@ -0,0 +1,1096 @@
+/*
+** Copyright (C) 1999-2018 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef SNDFILE_COMMON_H
+#define SNDFILE_COMMON_H
+
+#include "sfconfig.h"
+
+#include <stdlib.h>
+#include <string.h>
+#if HAVE_STDBOOL_H
+#include <stdbool.h>
+#endif
+
+#if HAVE_INTTYPES_H
+#include <inttypes.h>
+#elif HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifndef SNDFILE_H
+#include "sndfile.h"
+#endif
+
+#include <math.h>
+
+#ifdef USE_SSE2
+#	ifdef _MSC_VER
+#		include <intrin.h>
+#	else
+#		include <immintrin.h>
+#	endif
+#endif
+
+#ifdef __cplusplus
+#error "This code is not designed to be compiled with a C++ compiler."
+#endif
+
+
+
+/*
+** Inspiration : http://sourcefrog.net/weblog/software/languages/C/unused.html
+*/
+#ifdef UNUSED
+#elif defined (__GNUC__)
+#	define UNUSED(x) UNUSED_ ## x __attribute__ ((unused))
+#elif defined (__LCLINT__)
+#	define UNUSED(x) /*@unused@*/ x
+#else
+#	define UNUSED(x) x
+#endif
+
+#ifdef __GNUC__
+#	define WARN_UNUSED	__attribute__ ((warn_unused_result))
+#else
+#	define WARN_UNUSED
+#endif
+
+#define	SF_BUFFER_LEN			(8192)
+#define	SF_FILENAME_LEN			(1024)
+#define SF_SYSERR_LEN			(256)
+#define SF_MAX_STRINGS			(32)
+#define	SF_PARSELOG_LEN			(2048)
+
+#define	PSF_SEEK_ERROR			((sf_count_t) -1)
+
+#define	BITWIDTH2BYTES(x)	(((x) + 7) / 8)
+
+/*	For some reason sizeof returns an unsigned  value which causes
+**	a warning when that value is added or subtracted from a signed
+**	value. Use SIGNED_SIZEOF instead.
+*/
+#define		SIGNED_SIZEOF(x)	((int) sizeof (x))
+
+#define		ARRAY_LEN(x)	((int) (sizeof (x) / sizeof ((x) [0])))
+
+#define		SF_MAX(a, b)	((a) > (b) ? (a) : (b))
+#define		SF_MIN(a, b)	((a) < (b) ? (a) : (b))
+
+
+#define		COMPILE_TIME_ASSERT(e)	(sizeof (struct { int : - !! (e) ; }))
+
+
+#define		SF_MAX_CHANNELS		1024
+
+/*
+*	Macros for spliting the format file of SF_INFO into container type,
+**	codec type and endian-ness.
+*/
+#define SF_CONTAINER(x)		((x) & SF_FORMAT_TYPEMASK)
+#define SF_CODEC(x)			((x) & SF_FORMAT_SUBMASK)
+#define SF_ENDIAN(x)		((x) & SF_FORMAT_ENDMASK)
+
+/*
+**	Binheader cast macros.
+*/
+
+#define BHW1(x) ((uint8_t) (x))
+#define BHW2(x) ((uint16_t) (x))
+#define BHW3(x) ((uint32_t) (x))
+#define BHW4(x) ((uint32_t) (x))
+#define BHW8(x) ((uint64_t) (x))
+
+#define BHWm(x) ((uint32_t) (x))
+#define BHWS(x) ((char *) (x))
+
+#define BHWf(x) ((double) (x))
+#define BHWd(x) ((double) (x))
+
+#define BHWh(x) ((void *) (x))
+#define BHWj(x) ((size_t) (x))
+#define BHWp(x) ((char *) (x))
+#define BHWo(x) ((size_t) (x))
+#define BHWs(x) ((char *) (x))
+#define BHWv(x) ((const void *) (x))
+#define BHWz(x) ((size_t) (x))
+
+/*------------------------------------------------------------------------------
+*/
+
+enum
+{	/* PEAK chunk location. */
+	SF_PEAK_START		= 42,
+	SF_PEAK_END			= 43,
+
+	/* PEAK chunk location. */
+	SF_SCALE_MAX		= 52,
+	SF_SCALE_MIN		= 53,
+
+	/* str_flags values. */
+	SF_STR_ALLOW_START	= 0x0100,
+	SF_STR_ALLOW_END	= 0x0200,
+
+	/* Location of strings. */
+	SF_STR_LOCATE_START	= 0x0400,
+	SF_STR_LOCATE_END	= 0x0800,
+
+	SFD_TYPEMASK		= 0x0FFFFFFF
+} ;
+
+#define		SFM_MASK 	(SFM_READ | SFM_WRITE | SFM_RDWR)
+#define		SFM_UNMASK 	(~SFM_MASK)
+
+/*---------------------------------------------------------------------------------------
+** Formats that may be supported at some time in the future.
+** When support is finalised, these values move to src/sndfile.h.
+*/
+
+enum
+{	/* Work in progress. */
+	SF_FORMAT_SPEEX			= 0x5000000,
+	SF_FORMAT_OGGFLAC		= 0x5000001,
+
+	/* Formats supported read only. */
+	SF_FORMAT_TXW			= 0x4030000,		/* Yamaha TX16 sampler file */
+	SF_FORMAT_DWD			= 0x4040000,		/* DiamondWare Digirized */
+
+	/* Following are detected but not supported. */
+	SF_FORMAT_REX			= 0x40A0000,		/* Propellorheads Rex/Rcy */
+	SF_FORMAT_REX2			= 0x40D0000,		/* Propellorheads Rex2 */
+	SF_FORMAT_KRZ			= 0x40E0000,		/* Kurzweil sampler file */
+	SF_FORMAT_WMA			= 0x4100000,		/* Windows Media Audio. */
+	SF_FORMAT_SHN			= 0x4110000,		/* Shorten. */
+
+	/* Unsupported encodings. */
+	SF_FORMAT_SVX_FIB		= 0x1020, 		/* SVX Fibonacci Delta encoding. */
+	SF_FORMAT_SVX_EXP		= 0x1021, 		/* SVX Exponential Delta encoding. */
+
+	SF_FORMAT_PCM_N			= 0x1030
+} ;
+
+/*---------------------------------------------------------------------------------------
+*/
+
+typedef struct
+{	unsigned	kuki_offset ;
+	unsigned	pakt_offset ;
+
+	unsigned	bits_per_sample ;
+	unsigned	frames_per_packet ;
+
+	int64_t 	packets ;
+	int64_t 	valid_frames ;
+	int32_t 	priming_frames ;
+	int32_t 	remainder_frames ;
+} ALAC_DECODER_INFO ;
+
+/*---------------------------------------------------------------------------------------
+**	PEAK_CHUNK - This chunk type is common to both AIFF and WAVE files although their
+**	endian encodings are different.
+*/
+
+typedef struct
+{	double		value ;		/* signed value of peak */
+	sf_count_t	position ;	/* the sample frame for the peak */
+} PEAK_POS ;
+
+typedef struct
+{	/* libsndfile internal : write a PEAK chunk at the start or end of the file? */
+	int				peak_loc ;
+
+	/* WAV/AIFF */
+	unsigned int	version ;	/* version of the PEAK chunk */
+	unsigned int	timestamp ;	/* secs since 1/1/1970  */
+
+	/* CAF */
+	unsigned int	edit_number ;
+
+	/* the per channel peak info */
+	PEAK_POS		peaks [] ;
+} PEAK_INFO ;
+
+static inline PEAK_INFO *
+peak_info_calloc (int channels)
+{	return calloc (1, sizeof (PEAK_INFO) + channels * sizeof (PEAK_POS)) ;
+} /* peak_info_calloc */
+
+typedef struct
+{	int		type ;
+	int		flags ;
+	size_t 	offset ;
+} STR_DATA ;
+
+typedef struct
+{	uint64_t	hash ;
+	char		id [64] ;
+	unsigned	id_size ;
+	uint32_t	mark32 ;
+	sf_count_t	offset ;
+	uint32_t	len ;
+} READ_CHUNK ;
+
+typedef struct
+{	uint64_t	hash ;
+	uint32_t	mark32 ;
+	uint32_t	len ;
+	void		*data ;
+} WRITE_CHUNK ;
+
+typedef struct
+{	uint32_t	count ;
+	uint32_t	used ;
+	READ_CHUNK	*chunks ;
+} READ_CHUNKS ;
+typedef struct
+{	uint32_t	count ;
+	uint32_t	used ;
+	WRITE_CHUNK	*chunks ;
+} WRITE_CHUNKS ;
+
+struct SF_CHUNK_ITERATOR
+{	uint32_t	current ;
+	int64_t		hash ;
+	char		id [64] ;
+	unsigned	id_size ;
+	SNDFILE		*sndfile ;
+} ;
+
+static inline size_t
+make_size_t (int x)
+{	return (size_t) x ;
+} /* make_size_t */
+
+typedef SF_BROADCAST_INFO_VAR (16 * 1024) SF_BROADCAST_INFO_16K ;
+
+typedef SF_CART_INFO_VAR (16 * 1024) SF_CART_INFO_16K ;
+
+typedef struct
+{	sf_count_t	offset ;
+	sf_count_t	len ;
+	unsigned	minor_version ;
+} ID3V2_HEADER_INFO ;
+
+#if SIZEOF_WCHAR_T == 2
+typedef wchar_t	sfwchar_t ;
+#else
+typedef int16_t sfwchar_t ;
+#endif
+
+
+void *psf_memdup (const void *src, size_t n) ;
+
+/*
+**	This version of isprint specifically ignores any locale info. Its used for
+**	determining which characters can be printed in things like hexdumps.
+*/
+int psf_isprint (int ch) ;
+
+/*=======================================================================================
+**	SF_PRIVATE stuct - a pointer to this struct is passed back to the caller of the
+**	sf_open_XXXX functions. The caller however has no knowledge of the struct's
+**	contents.
+*/
+
+typedef struct
+{
+	char	path [SF_FILENAME_LEN] ;
+	char	dir [SF_FILENAME_LEN] ;
+	char	name [SF_FILENAME_LEN / 4] ;
+
+#if USE_WINDOWS_API
+	/*
+	**	These fields can only be used in src/file_io.c.
+	**	They are basically the same as a windows file HANDLE.
+	*/
+	void 	*handle, *hsaved ;
+#else
+	/* These fields can only be used in src/file_io.c. */
+	int 	filedes, savedes ;
+#endif
+
+	int		do_not_close_descriptor ;
+	int		mode ;			/* Open mode : SFM_READ, SFM_WRITE or SFM_RDWR. */
+} PSF_FILE ;
+
+
+
+typedef union
+{	double			dbuf	[SF_BUFFER_LEN / sizeof (double)] ;
+#if (defined (SIZEOF_INT64_T) && (SIZEOF_INT64_T == 8))
+	int64_t			lbuf	[SF_BUFFER_LEN / sizeof (int64_t)] ;
+#else
+	long			lbuf	[SF_BUFFER_LEN / sizeof (double)] ;
+#endif
+	float			fbuf	[SF_BUFFER_LEN / sizeof (float)] ;
+	int				ibuf	[SF_BUFFER_LEN / sizeof (int)] ;
+	short			sbuf	[SF_BUFFER_LEN / sizeof (short)] ;
+	char			cbuf	[SF_BUFFER_LEN / sizeof (char)] ;
+	signed char		scbuf	[SF_BUFFER_LEN / sizeof (signed char)] ;
+	unsigned char	ucbuf	[SF_BUFFER_LEN / sizeof (signed char)] ;
+} BUF_UNION ;
+
+
+
+typedef struct sf_private_tag
+{
+	PSF_FILE		file, rsrc ;
+
+	char			syserr		[SF_SYSERR_LEN] ;
+
+	/* parselog and indx should only be changed within the logging functions
+	** of common.c
+	*/
+	struct
+	{	char			buf	[SF_PARSELOG_LEN] ;
+		int				indx ;
+	} parselog ;
+
+
+	struct
+	{	unsigned char	* ptr ;
+		sf_count_t		indx, end, len ;
+	} header ;
+
+	int				rwf_endian ;	/* Header endian-ness flag. */
+
+	/* Storage and housekeeping data for adding/reading strings from
+	** sound files.
+	*/
+	struct
+	{	STR_DATA	data [SF_MAX_STRINGS] ;
+		char		*storage ;
+		size_t		storage_len ;
+		size_t		storage_used ;
+		uint32_t	flags ;
+	} strings ;
+
+	/* Guard value. If this changes the buffers above have overflowed. */
+	int				Magick ;
+
+	unsigned		unique_id ;
+
+	int				error ;
+
+	int				endian ;		/* File endianness : SF_ENDIAN_LITTLE or SF_ENDIAN_BIG. */
+	int				data_endswap ;	/* Need to endswap data? */
+
+	/*
+	** Maximum float value for calculating the multiplier for
+	** float/double to short/int conversions.
+	*/
+	int				float_int_mult ;
+	float			float_max ;
+
+	int				scale_int_float ;
+
+	/* Vairables for handling pipes. */
+	int				is_pipe ;		/* True if file is a pipe. */
+	sf_count_t		pipeoffset ;	/* Number of bytes read from a pipe. */
+
+	/* True if clipping must be performed on float->int conversions. */
+	int				add_clipping ;
+
+	SF_INFO			sf ;
+
+	int				have_written ;	/* Has a single write been done to the file? */
+	PEAK_INFO		*peak_info ;
+
+	/* Cue Marker Info */
+	SF_CUES		*cues ;
+
+	/* Loop Info */
+	SF_LOOP_INFO	*loop_info ;
+	SF_INSTRUMENT	*instrument ;
+
+	/* Broadcast (EBU) Info */
+	SF_BROADCAST_INFO_16K *broadcast_16k ;
+
+	/* Cart (AES46) Info */
+	SF_CART_INFO_16K *cart_16k ;
+
+	/* Channel map data (if present) : an array of ints. */
+	int				*channel_map ;
+
+	sf_count_t		filelength ;	/* Overall length of (embedded) file. */
+	sf_count_t		fileoffset ;	/* Offset in number of bytes from beginning of file. */
+
+	sf_count_t		rsrclength ;	/* Length of the resource fork (if it exists). */
+
+	sf_count_t		dataoffset ;	/* Offset in number of bytes from beginning of file. */
+	sf_count_t		datalength ;	/* Length in bytes of the audio data. */
+	sf_count_t		dataend ;		/* Offset to file tailer. */
+
+	int				blockwidth ;	/* Size in bytes of one set of interleaved samples. */
+	int				bytewidth ;		/* Size in bytes of one sample (one channel). */
+
+	void			*dither ;
+	void			*interleave ;
+
+	int				last_op ;		/* Last operation; either SFM_READ or SFM_WRITE */
+	sf_count_t		read_current ;
+	sf_count_t		write_current ;
+
+	void			*container_data ;	/*	This is a pointer to dynamically allocated file
+										**	container format specific data.
+										*/
+
+	void			*codec_data ;		/*	This is a pointer to dynamically allocated file
+										**	codec format specific data.
+										*/
+
+	SF_DITHER_INFO	write_dither ;
+	SF_DITHER_INFO	read_dither ;
+
+	int				norm_double ;
+	int				norm_float ;
+
+	int				auto_header ;
+
+	int				ieee_replace ;
+
+	/* A set of file specific function pointers */
+	sf_count_t		(*read_short)	(struct sf_private_tag*, short *ptr, sf_count_t len) ;
+	sf_count_t		(*read_int)		(struct sf_private_tag*, int *ptr, sf_count_t len) ;
+	sf_count_t		(*read_float)	(struct sf_private_tag*, float *ptr, sf_count_t len) ;
+	sf_count_t		(*read_double)	(struct sf_private_tag*, double *ptr, sf_count_t len) ;
+
+	sf_count_t		(*write_short)	(struct sf_private_tag*, const short *ptr, sf_count_t len) ;
+	sf_count_t		(*write_int)	(struct sf_private_tag*, const int *ptr, sf_count_t len) ;
+	sf_count_t		(*write_float)	(struct sf_private_tag*, const float *ptr, sf_count_t len) ;
+	sf_count_t		(*write_double)	(struct sf_private_tag*, const double *ptr, sf_count_t len) ;
+
+	sf_count_t		(*seek) 		(struct sf_private_tag*, int mode, sf_count_t samples_from_start) ;
+	int				(*write_header)	(struct sf_private_tag*, int calc_length) ;
+	int				(*command)		(struct sf_private_tag*, int command, void *data, int datasize) ;
+	int				(*byterate)		(struct sf_private_tag*) ;
+
+	/*
+	**	Separate close functions for the codec and the container.
+	**	The codec close function is always called first.
+	*/
+	int				(*codec_close)		(struct sf_private_tag*) ;
+	int				(*container_close)	(struct sf_private_tag*) ;
+
+	char			*format_desc ;
+
+	/* Virtual I/O functions. */
+	int					virtual_io ;
+	SF_VIRTUAL_IO		vio ;
+	void				*vio_user_data ;
+
+	/* Chunk get/set. */
+	SF_CHUNK_ITERATOR	*iterator ;
+
+	READ_CHUNKS			rchunks ;
+	WRITE_CHUNKS		wchunks ;
+
+	int					(*set_chunk)		(struct sf_private_tag*, const SF_CHUNK_INFO * chunk_info) ;
+	SF_CHUNK_ITERATOR *	(*next_chunk_iterator)	(struct sf_private_tag*, SF_CHUNK_ITERATOR * iterator) ;
+	int					(*get_chunk_size)	(struct sf_private_tag*, const SF_CHUNK_ITERATOR * iterator, SF_CHUNK_INFO * chunk_info) ;
+	int					(*get_chunk_data)	(struct sf_private_tag*, const SF_CHUNK_ITERATOR * iterator, SF_CHUNK_INFO * chunk_info) ;
+
+	int cpu_flags ;
+
+	ID3V2_HEADER_INFO	id3_header ;
+} SF_PRIVATE ;
+
+
+
+enum
+{	SFE_NO_ERROR				= SF_ERR_NO_ERROR,
+	SFE_BAD_OPEN_FORMAT			= SF_ERR_UNRECOGNISED_FORMAT,
+	SFE_SYSTEM					= SF_ERR_SYSTEM,
+	SFE_MALFORMED_FILE			= SF_ERR_MALFORMED_FILE,
+	SFE_UNSUPPORTED_ENCODING	= SF_ERR_UNSUPPORTED_ENCODING,
+
+	SFE_ZERO_MAJOR_FORMAT,
+	SFE_ZERO_MINOR_FORMAT,
+	SFE_BAD_FILE,
+	SFE_BAD_FILE_READ,
+	SFE_OPEN_FAILED,
+	SFE_BAD_SNDFILE_PTR,
+	SFE_BAD_SF_INFO_PTR,
+	SFE_BAD_SF_INCOMPLETE,
+	SFE_BAD_FILE_PTR,
+	SFE_BAD_INT_PTR,
+	SFE_BAD_STAT_SIZE,
+	SFE_NO_TEMP_DIR,
+	SFE_MALLOC_FAILED,
+	SFE_UNIMPLEMENTED,
+	SFE_BAD_READ_ALIGN,
+	SFE_BAD_WRITE_ALIGN,
+	SFE_NOT_READMODE,
+	SFE_NOT_WRITEMODE,
+	SFE_BAD_MODE_RW,
+	SFE_BAD_SF_INFO,
+	SFE_BAD_OFFSET,
+	SFE_NO_EMBED_SUPPORT,
+	SFE_NO_EMBEDDED_RDWR,
+	SFE_NO_PIPE_WRITE,
+
+	SFE_INTERNAL,
+	SFE_BAD_COMMAND_PARAM,
+	SFE_BAD_ENDIAN,
+	SFE_CHANNEL_COUNT_ZERO,
+	SFE_CHANNEL_COUNT,
+	SFE_CHANNEL_COUNT_BAD,
+
+	SFE_BAD_VIRTUAL_IO,
+
+	SFE_INTERLEAVE_MODE,
+	SFE_INTERLEAVE_SEEK,
+	SFE_INTERLEAVE_READ,
+
+	SFE_BAD_SEEK,
+	SFE_NOT_SEEKABLE,
+	SFE_AMBIGUOUS_SEEK,
+	SFE_WRONG_SEEK,
+	SFE_SEEK_FAILED,
+
+	SFE_BAD_OPEN_MODE,
+	SFE_OPEN_PIPE_RDWR,
+	SFE_RDWR_POSITION,
+	SFE_RDWR_BAD_HEADER,
+	SFE_CMD_HAS_DATA,
+	SFE_BAD_BROADCAST_INFO_SIZE,
+	SFE_BAD_BROADCAST_INFO_TOO_BIG,
+	SFE_BAD_CART_INFO_SIZE,
+	SFE_BAD_CART_INFO_TOO_BIG,
+
+	SFE_STR_NO_SUPPORT,
+	SFE_STR_NOT_WRITE,
+	SFE_STR_MAX_DATA,
+	SFE_STR_MAX_COUNT,
+	SFE_STR_BAD_TYPE,
+	SFE_STR_NO_ADD_END,
+	SFE_STR_BAD_STRING,
+	SFE_STR_WEIRD,
+
+	SFE_WAV_NO_RIFF,
+	SFE_WAV_NO_WAVE,
+	SFE_WAV_NO_FMT,
+	SFE_WAV_BAD_FMT,
+	SFE_WAV_FMT_SHORT,
+	SFE_WAV_BAD_FACT,
+	SFE_WAV_BAD_PEAK,
+	SFE_WAV_PEAK_B4_FMT,
+	SFE_WAV_BAD_FORMAT,
+	SFE_WAV_BAD_BLOCKALIGN,
+	SFE_WAV_NO_DATA,
+	SFE_WAV_BAD_LIST,
+	SFE_WAV_ADPCM_NOT4BIT,
+	SFE_WAV_ADPCM_CHANNELS,
+	SFE_WAV_ADPCM_SAMPLES,
+	SFE_WAV_GSM610_FORMAT,
+	SFE_WAV_UNKNOWN_CHUNK,
+	SFE_WAV_WVPK_DATA,
+	SFE_WAV_NMS_FORMAT,
+
+	SFE_AIFF_NO_FORM,
+	SFE_AIFF_AIFF_NO_FORM,
+	SFE_AIFF_COMM_NO_FORM,
+	SFE_AIFF_SSND_NO_COMM,
+	SFE_AIFF_UNKNOWN_CHUNK,
+	SFE_AIFF_COMM_CHUNK_SIZE,
+	SFE_AIFF_BAD_COMM_CHUNK,
+	SFE_AIFF_PEAK_B4_COMM,
+	SFE_AIFF_BAD_PEAK,
+	SFE_AIFF_NO_SSND,
+	SFE_AIFF_NO_DATA,
+	SFE_AIFF_RW_SSND_NOT_LAST,
+
+	SFE_AU_UNKNOWN_FORMAT,
+	SFE_AU_NO_DOTSND,
+	SFE_AU_EMBED_BAD_LEN,
+
+	SFE_RAW_READ_BAD_SPEC,
+	SFE_RAW_BAD_BITWIDTH,
+	SFE_RAW_BAD_FORMAT,
+
+	SFE_PAF_NO_MARKER,
+	SFE_PAF_VERSION,
+	SFE_PAF_UNKNOWN_FORMAT,
+	SFE_PAF_SHORT_HEADER,
+	SFE_PAF_BAD_CHANNELS,
+
+	SFE_SVX_NO_FORM,
+	SFE_SVX_NO_BODY,
+	SFE_SVX_NO_DATA,
+	SFE_SVX_BAD_COMP,
+	SFE_SVX_BAD_NAME_LENGTH,
+
+	SFE_NIST_BAD_HEADER,
+	SFE_NIST_CRLF_CONVERISON,
+	SFE_NIST_BAD_ENCODING,
+
+	SFE_VOC_NO_CREATIVE,
+	SFE_VOC_BAD_FORMAT,
+	SFE_VOC_BAD_VERSION,
+	SFE_VOC_BAD_MARKER,
+	SFE_VOC_BAD_SECTIONS,
+	SFE_VOC_MULTI_SAMPLERATE,
+	SFE_VOC_MULTI_SECTION,
+	SFE_VOC_MULTI_PARAM,
+	SFE_VOC_SECTION_COUNT,
+	SFE_VOC_NO_PIPE,
+
+	SFE_IRCAM_NO_MARKER,
+	SFE_IRCAM_BAD_CHANNELS,
+	SFE_IRCAM_UNKNOWN_FORMAT,
+
+	SFE_W64_64_BIT,
+	SFE_W64_NO_RIFF,
+	SFE_W64_NO_WAVE,
+	SFE_W64_NO_DATA,
+	SFE_W64_ADPCM_NOT4BIT,
+	SFE_W64_ADPCM_CHANNELS,
+	SFE_W64_GSM610_FORMAT,
+
+	SFE_MAT4_BAD_NAME,
+	SFE_MAT4_NO_SAMPLERATE,
+
+	SFE_MAT5_BAD_ENDIAN,
+	SFE_MAT5_NO_BLOCK,
+	SFE_MAT5_SAMPLE_RATE,
+
+	SFE_PVF_NO_PVF1,
+	SFE_PVF_BAD_HEADER,
+	SFE_PVF_BAD_BITWIDTH,
+
+	SFE_DWVW_BAD_BITWIDTH,
+	SFE_G72X_NOT_MONO,
+	SFE_NMS_ADPCM_NOT_MONO,
+
+	SFE_XI_BAD_HEADER,
+	SFE_XI_EXCESS_SAMPLES,
+	SFE_XI_NO_PIPE,
+
+	SFE_HTK_NO_PIPE,
+
+	SFE_SDS_NOT_SDS,
+	SFE_SDS_BAD_BIT_WIDTH,
+
+	SFE_SD2_FD_DISALLOWED,
+	SFE_SD2_BAD_DATA_OFFSET,
+	SFE_SD2_BAD_MAP_OFFSET,
+	SFE_SD2_BAD_DATA_LENGTH,
+	SFE_SD2_BAD_MAP_LENGTH,
+	SFE_SD2_BAD_RSRC,
+	SFE_SD2_BAD_SAMPLE_SIZE,
+
+	SFE_FLAC_BAD_HEADER,
+	SFE_FLAC_NEW_DECODER,
+	SFE_FLAC_INIT_DECODER,
+	SFE_FLAC_LOST_SYNC,
+	SFE_FLAC_BAD_SAMPLE_RATE,
+	SFE_FLAC_CHANNEL_COUNT_CHANGED,
+	SFE_FLAC_UNKOWN_ERROR,
+
+	SFE_WVE_NOT_WVE,
+	SFE_WVE_NO_PIPE,
+
+	SFE_VORBIS_ENCODER_BUG,
+
+	SFE_RF64_NOT_RF64,
+	SFE_RF64_PEAK_B4_FMT,
+	SFE_RF64_NO_DATA,
+
+	SFE_BAD_CHUNK_PTR,
+	SFE_UNKNOWN_CHUNK,
+	SFE_BAD_CHUNK_FORMAT,
+	SFE_BAD_CHUNK_MARKER,
+	SFE_BAD_CHUNK_DATA_PTR,
+	SFE_ALAC_FAIL_TMPFILE,
+	SFE_FILENAME_TOO_LONG,
+	SFE_NEGATIVE_RW_LEN,
+
+	SFE_OPUS_BAD_SAMPLERATE,
+
+	SFE_CAF_NOT_CAF,
+	SFE_CAF_NO_DESC,
+	SFE_CAF_BAD_PEAK,
+
+	SFE_AVR_NOT_AVR,
+	SFE_AVR_BAD_REZ_SIGN,
+
+	SFE_MPC_NO_MARKER,
+
+	SFE_MPEG_BAD_SAMPLERATE,
+
+	SFE_MAX_ERROR			/* This must be last in list. */
+} ;
+
+/* Allocate and initialize the SF_PRIVATE struct. */
+SF_PRIVATE * psf_allocate (void) ;
+
+int subformat_to_bytewidth (int format) ;
+int s_bitwidth_to_subformat (int bits) ;
+int u_bitwidth_to_subformat (int bits) ;
+
+/*  Functions for reading and writing floats and doubles on processors
+**	with non-IEEE floats/doubles.
+*/
+float	float32_be_read		(const unsigned char *cptr) ;
+float	float32_le_read		(const unsigned char *cptr) ;
+void	float32_be_write	(float in, unsigned char *out) ;
+void	float32_le_write	(float in, unsigned char *out) ;
+
+double	double64_be_read	(const unsigned char *cptr) ;
+double	double64_le_read	(const unsigned char *cptr) ;
+void	double64_be_write	(double in, unsigned char *out) ;
+void	double64_le_write	(double in, unsigned char *out) ;
+
+/* Functions for writing to the internal logging buffer. */
+
+void	psf_log_printf		(SF_PRIVATE *psf, const char *format, ...) ;
+void	psf_log_SF_INFO 	(SF_PRIVATE *psf) ;
+
+int32_t	psf_rand_int32 (void) ;
+
+void append_snprintf (char * dest, size_t maxlen, const char * fmt, ...) ;
+void psf_strlcpy_crlf (char *dest, const char *src, size_t destmax, size_t srcmax) ;
+
+sf_count_t psf_decode_frame_count (SF_PRIVATE *psf) ;
+
+/* Functions used when writing file headers. */
+
+int		psf_binheader_writef	(SF_PRIVATE *psf, const char *format, ...) ;
+void	psf_asciiheader_printf	(SF_PRIVATE *psf, const char *format, ...) ;
+
+/* Functions used when reading file headers. */
+
+int		psf_binheader_readf	(SF_PRIVATE *psf, char const *format, ...) ;
+
+/* Functions used in the write function for updating the peak chunk. */
+
+void	peak_update_short	(SF_PRIVATE *psf, short *ptr, size_t items) ;
+void	peak_update_int		(SF_PRIVATE *psf, int *ptr, size_t items) ;
+void	peak_update_double	(SF_PRIVATE *psf, double *ptr, size_t items) ;
+
+/* Functions defined in command.c. */
+
+int		psf_get_format_simple_count	(void) ;
+int		psf_get_format_simple		(SF_FORMAT_INFO *data) ;
+
+int		psf_get_format_info			(SF_FORMAT_INFO *data) ;
+
+int		psf_get_format_major_count	(void) ;
+int		psf_get_format_major		(SF_FORMAT_INFO *data) ;
+
+int		psf_get_format_subtype_count	(void) ;
+int		psf_get_format_subtype		(SF_FORMAT_INFO *data) ;
+
+void	psf_generate_format_desc (SF_PRIVATE *psf) ;
+
+double	psf_calc_signal_max			(SF_PRIVATE *psf, int normalize) ;
+int		psf_calc_max_all_channels	(SF_PRIVATE *psf, double *peaks, int normalize) ;
+
+int		psf_get_signal_max			(SF_PRIVATE *psf, double *peak) ;
+int		psf_get_max_all_channels	(SF_PRIVATE *psf, double *peaks) ;
+
+/* Functions in strings.c. */
+
+const char* psf_get_string (SF_PRIVATE *psf, int str_type) ;
+int psf_set_string (SF_PRIVATE *psf, int str_type, const char *str) ;
+int psf_store_string (SF_PRIVATE *psf, int str_type, const char *str) ;
+int psf_location_string_count (const SF_PRIVATE * psf, int location) ;
+
+/* Default seek function. Use for PCM and float encoded data. */
+sf_count_t	psf_default_seek (SF_PRIVATE *psf, int mode, sf_count_t samples_from_start) ;
+
+int macos_guess_file_type (SF_PRIVATE *psf, const char *filename) ;
+
+/*------------------------------------------------------------------------------------
+**	File I/O functions which will allow access to large files (> 2 Gig) on
+**	some 32 bit OSes. Implementation in file_io.c.
+*/
+
+int psf_fopen (SF_PRIVATE *psf) ;
+int psf_set_stdio (SF_PRIVATE *psf) ;
+int psf_file_valid (SF_PRIVATE *psf) ;
+void psf_set_file (SF_PRIVATE *psf, int fd) ;
+void psf_init_files (SF_PRIVATE *psf) ;
+void psf_use_rsrc (SF_PRIVATE *psf, int on_off) ;
+
+SNDFILE * psf_open_file (SF_PRIVATE *psf, SF_INFO *sfinfo) ;
+
+sf_count_t psf_fseek (SF_PRIVATE *psf, sf_count_t offset, int whence) ;
+sf_count_t psf_fread (void *ptr, sf_count_t bytes, sf_count_t count, SF_PRIVATE *psf) ;
+sf_count_t psf_fwrite (const void *ptr, sf_count_t bytes, sf_count_t count, SF_PRIVATE *psf) ;
+sf_count_t psf_fgets (char *buffer, sf_count_t bufsize, SF_PRIVATE *psf) ;
+sf_count_t psf_ftell (SF_PRIVATE *psf) ;
+sf_count_t psf_get_filelen (SF_PRIVATE *psf) ;
+
+void psf_fsync (SF_PRIVATE *psf) ;
+
+int psf_is_pipe (SF_PRIVATE *psf) ;
+
+int psf_ftruncate (SF_PRIVATE *psf, sf_count_t len) ;
+int psf_fclose (SF_PRIVATE *psf) ;
+
+/* Open and close the resource fork of a file. */
+int psf_open_rsrc (SF_PRIVATE *psf) ;
+int psf_close_rsrc (SF_PRIVATE *psf) ;
+
+int	psf_copy_filename (SF_PRIVATE *psf, const char *path) ;
+
+/*
+void psf_fclearerr (SF_PRIVATE *psf) ;
+int psf_ferror (SF_PRIVATE *psf) ;
+*/
+
+/*------------------------------------------------------------------------------------
+** Functions for reading and writing different file formats.
+*/
+
+int		aiff_open	(SF_PRIVATE *psf) ;
+int		au_open		(SF_PRIVATE *psf) ;
+int		avr_open	(SF_PRIVATE *psf) ;
+int		htk_open	(SF_PRIVATE *psf) ;
+int		ircam_open	(SF_PRIVATE *psf) ;
+int		mat4_open	(SF_PRIVATE *psf) ;
+int		mat5_open	(SF_PRIVATE *psf) ;
+int		nist_open	(SF_PRIVATE *psf) ;
+int		paf_open	(SF_PRIVATE *psf) ;
+int		pvf_open	(SF_PRIVATE *psf) ;
+int		raw_open	(SF_PRIVATE *psf) ;
+int		sd2_open	(SF_PRIVATE *psf) ;
+int		sds_open	(SF_PRIVATE *psf) ;
+int		svx_open	(SF_PRIVATE *psf) ;
+int		voc_open	(SF_PRIVATE *psf) ;
+int		w64_open	(SF_PRIVATE *psf) ;
+int		wav_open	(SF_PRIVATE *psf) ;
+int		xi_open		(SF_PRIVATE *psf) ;
+int		flac_open	(SF_PRIVATE *psf) ;
+int		caf_open	(SF_PRIVATE *psf) ;
+int		mpc2k_open	(SF_PRIVATE *psf) ;
+int		rf64_open	(SF_PRIVATE *psf) ;
+
+int		ogg_vorbis_open	(SF_PRIVATE *psf) ;
+int		ogg_speex_open	(SF_PRIVATE *psf) ;
+int		ogg_pcm_open	(SF_PRIVATE *psf) ;
+int		ogg_opus_open	(SF_PRIVATE *psf) ;
+int		ogg_open	(SF_PRIVATE *psf) ;
+
+int		mpeg_open	(SF_PRIVATE *psf) ;
+
+/* In progress. Do not currently work. */
+
+int		rx2_open	(SF_PRIVATE *psf) ;
+int		txw_open	(SF_PRIVATE *psf) ;
+int		wve_open	(SF_PRIVATE *psf) ;
+int		dwd_open	(SF_PRIVATE *psf) ;
+
+/*------------------------------------------------------------------------------------
+**	Init functions for a number of common data encodings.
+*/
+
+int		pcm_init		(SF_PRIVATE *psf) ;
+int		ulaw_init		(SF_PRIVATE *psf) ;
+int		alaw_init		(SF_PRIVATE *psf) ;
+int		float32_init	(SF_PRIVATE *psf) ;
+int		double64_init	(SF_PRIVATE *psf) ;
+int		dwvw_init		(SF_PRIVATE *psf, int bitwidth) ;
+int		gsm610_init		(SF_PRIVATE *psf) ;
+int		nms_adpcm_init	(SF_PRIVATE *psf) ;
+int		vox_adpcm_init	(SF_PRIVATE *psf) ;
+int		flac_init		(SF_PRIVATE *psf) ;
+int		g72x_init 		(SF_PRIVATE * psf) ;
+int		alac_init		(SF_PRIVATE *psf, const ALAC_DECODER_INFO * info) ;
+int		mpeg_init		(SF_PRIVATE *psf, int bitrate_mode, int write_metadata) ;
+
+int 	dither_init		(SF_PRIVATE *psf, int mode) ;
+
+int		wavlike_ima_init (SF_PRIVATE *psf, int blockalign, int samplesperblock) ;
+int		wavlike_msadpcm_init (SF_PRIVATE *psf, int blockalign, int samplesperblock) ;
+
+int		aiff_ima_init (SF_PRIVATE *psf, int blockalign, int samplesperblock) ;
+
+int		interleave_init (SF_PRIVATE *psf) ;
+
+/*------------------------------------------------------------------------------------
+** Chunk logging functions.
+*/
+
+SF_CHUNK_ITERATOR * psf_get_chunk_iterator (SF_PRIVATE * psf, const char * marker_str) ;
+SF_CHUNK_ITERATOR * psf_next_chunk_iterator (const READ_CHUNKS * pchk , SF_CHUNK_ITERATOR *iterator) ;
+int		psf_store_read_chunk_u32 (READ_CHUNKS * pchk, uint32_t marker, sf_count_t offset, uint32_t len) ;
+int		psf_store_read_chunk_str (READ_CHUNKS * pchk, const char * marker, sf_count_t offset, uint32_t len) ;
+int		psf_save_write_chunk (WRITE_CHUNKS * pchk, const SF_CHUNK_INFO * chunk_info) ;
+int		psf_find_read_chunk_str (const READ_CHUNKS * pchk, const char * marker) ;
+int		psf_find_read_chunk_m32 (const READ_CHUNKS * pchk, uint32_t marker) ;
+int		psf_find_read_chunk_iterator (const READ_CHUNKS * pchk, const SF_CHUNK_ITERATOR * marker) ;
+
+int		psf_find_write_chunk (WRITE_CHUNKS * pchk, const char * marker) ;
+
+/*------------------------------------------------------------------------------------
+** Functions that work like OpenBSD's strlcpy/strlcat to replace strncpy/strncat.
+**
+** See : http://www.gratisoft.us/todd/papers/strlcpy.html
+**
+** These functions are available on *BSD, but are not avaialble everywhere so we
+** implement them here.
+**
+** The argument order has been changed to that of strncpy/strncat to cause
+** compiler errors if code is carelessly converted from one to the other.
+*/
+
+void psf_strlcat (char *dest, size_t n, const char *src) ;
+void psf_strlcpy (char *dest, size_t n, const char *src) ;
+
+/*------------------------------------------------------------------------------------
+** SIMD optimized math functions.
+*/
+
+static inline int psf_lrintf (float x)
+{
+	#ifdef USE_SSE2
+ 		return _mm_cvtss_si32 (_mm_load_ss (&x)) ;
+	#else
+		return lrintf (x) ;
+	#endif
+} /* psf_lrintf */
+
+static inline int psf_lrint (double x)
+{
+	#ifdef USE_SSE2
+ 		return _mm_cvtsd_si32 (_mm_load_sd (&x)) ;
+	#else
+		return lrint (x) ;
+	#endif
+} /* psf_lrintf */
+
+/*------------------------------------------------------------------------------------
+** Other helper functions.
+*/
+
+void	*psf_memset (void *s, int c, sf_count_t n) ;
+
+SF_CUES * psf_cues_dup (const void * ptr, size_t datasize) ;
+SF_CUES * psf_cues_alloc (uint32_t cue_count) ;
+void psf_get_cues (SF_PRIVATE * psf, void * data, size_t datasize) ;
+
+SF_INSTRUMENT * psf_instrument_alloc (void) ;
+
+void	psf_sanitize_string (char * cptr, int len) ;
+
+/* Generate the current date as a string. */
+void	psf_get_date_str (char *str, int maxlen) ;
+
+SF_BROADCAST_INFO_16K * broadcast_var_alloc (void) ;
+int		broadcast_var_set (SF_PRIVATE *psf, const SF_BROADCAST_INFO * data, size_t datasize) ;
+int		broadcast_var_get (SF_PRIVATE *psf, SF_BROADCAST_INFO * data, size_t datasize) ;
+
+
+SF_CART_INFO_16K * cart_var_alloc (void) ;
+int 		cart_var_set (SF_PRIVATE *psf, const SF_CART_INFO * date, size_t datasize) ;
+int		cart_var_get (SF_PRIVATE *psf, SF_CART_INFO * data, size_t datasize) ;
+
+typedef struct
+{	int channels ;
+	int endianness ;
+} AUDIO_DETECT ;
+
+int audio_detect (SF_PRIVATE * psf, AUDIO_DETECT *ad, const unsigned char * data, int datalen) ;
+int id3_skip (SF_PRIVATE * psf) ;
+const char *id3_lookup_v1_genre (int number) ;
+
+void	alac_get_desc_chunk_items (int subformat, uint32_t *fmt_flags, uint32_t *frames_per_packet) ;
+
+FILE *	psf_open_tmpfile (char * fname, size_t fnamelen) ;
+
+/*------------------------------------------------------------------------------------
+** Helper/debug functions.
+*/
+
+void	psf_hexdump (const void *ptr, int len) ;
+
+const char * str_of_major_format (int format) ;
+const char * str_of_minor_format (int format) ;
+const char * str_of_open_mode (int mode) ;
+const char * str_of_endianness (int end) ;
+
+/*------------------------------------------------------------------------------------
+** Extra commands for sf_command(). Not for public use yet.
+*/
+
+enum
+{	SFC_TEST_AIFF_ADD_INST_CHUNK	= 0x2000,
+	SFC_TEST_WAV_ADD_INFO_CHUNK		= 0x2010
+} ;
+
+/*
+** Maybe, one day, make these functions or something like them, public.
+**
+** Buffer to buffer dithering. Pointer in and out are allowed to point
+** to the same buffer for in-place dithering.
+*/
+
+#if 0
+int sf_dither_short		(const SF_DITHER_INFO *dither, const short *in, short *out, int count) ;
+int sf_dither_int		(const SF_DITHER_INFO *dither, const int *in, int *out, int count) ;
+int sf_dither_float		(const SF_DITHER_INFO *dither, const float *in, float *out, int count) ;
+int sf_dither_double	(const SF_DITHER_INFO *dither, const double *in, double *out, int count) ;
+#endif
+
+/*------------------------------------------------------------------------------------
+** Data conversion functions.
+*/
+
+void psf_f2s_array (const float *src, short *dest, int count, int normalize) ;
+void psf_f2s_clip_array (const float *src, short *dest, int count, int normalize) ;
+
+void psf_d2s_array (const double *src, short *dest, int count, int normalize) ;
+void psf_d2s_clip_array (const double *src, short *dest, int count, int normalize) ;
+
+void psf_f2i_array (const float *src, int *dest, int count, int normalize) ;
+void psf_f2i_clip_array (const float *src, int *dest, int count, int normalize) ;
+
+void psf_d2i_array (const double *src, int *dest, int count, int normalize) ;
+void psf_d2i_clip_array (const double *src, int *dest, int count, int normalize) ;
+
+
+/*------------------------------------------------------------------------------------
+** Left and right shift on int. According to the C standard, the left and right
+** shift operations applied to a negative integer results in undefined behavior.
+** These twp functions work around that.
+*/
+
+#if __GNUC__
+#define ALWAYS_INLINE		__attribute__ ((always_inline))
+#else
+#define ALWAYS_INLINE
+#endif
+
+static inline int32_t ALWAYS_INLINE
+arith_shift_left (int32_t x, int shift)
+{	return (int32_t) (((uint32_t) x) << shift) ;
+} /* arith_shift_left */
+
+static inline int32_t ALWAYS_INLINE
+arith_shift_right (int32_t x, int shift)
+{	if (x >= 0)
+		return x >> shift ;
+	return ~ ((~x) >> shift) ;
+} /* arith_shift_right */
+
+#endif /* SNDFILE_COMMON_H */
diff --git a/src/config.h.cmake b/src/config.h.cmake
new file mode 100644
index 0000000..e8d8145
--- /dev/null
+++ b/src/config.h.cmake
@@ -0,0 +1,315 @@
+/* Set to 1 if the compile is GNU GCC. */
+#cmakedefine01 COMPILER_IS_GCC
+
+/* Target processor clips on negative float to int conversion. */
+#cmakedefine01 CPU_CLIPS_NEGATIVE
+
+/* Target processor clips on positive float to int conversion. */
+#cmakedefine01 CPU_CLIPS_POSITIVE
+
+/* Target processor is big endian. */
+#cmakedefine01 CPU_IS_BIG_ENDIAN
+
+/* Target processor is little endian. */
+#cmakedefine01 CPU_IS_LITTLE_ENDIAN
+
+/* Set to 1 to enable experimental code. */
+#cmakedefine01 ENABLE_EXPERIMENTAL_CODE
+
+/* Define to 1 if you have the <alsa/asoundlib.h> header file. */
+#cmakedefine01 HAVE_ALSA_ASOUNDLIB_H
+
+/* Define to 1 if you have the <byteswap.h> header file. */
+#cmakedefine01 HAVE_BYTESWAP_H
+
+/* Define to 1 if you have the `calloc' function. */
+#cmakedefine01 HAVE_CALLOC
+
+/* Define to 1 if you have the `ceil' function. */
+#cmakedefine01 HAVE_CEIL
+
+/* Set to 1 if S_IRGRP is defined. */
+#cmakedefine01 HAVE_DECL_S_IRGRP
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#cmakedefine01 HAVE_DLFCN_H
+
+/* Define to 1 if you have the <direct.h> header file. */
+#cmakedefine HAVE_DIRECT_H
+
+/* Define to 1 if you have the <endian.h> header file. */
+#cmakedefine01 HAVE_ENDIAN_H
+
+/* Will be set to 1 if flac, ogg and vorbis are available. */
+#cmakedefine01 HAVE_EXTERNAL_XIPH_LIBS
+
+/* Will be set to 1 if lame and mpg123 are available. */
+#cmakedefine01 HAVE_MPEG
+
+/* Define to 1 if you have the `floor' function. */
+#cmakedefine01 HAVE_FLOOR
+
+/* Define to 1 if you have the `fmod' function. */
+#cmakedefine01 HAVE_FMOD
+
+/* Define to 1 if you have the `free' function. */
+#cmakedefine01 HAVE_FREE
+
+/* Define to 1 if you have the `fstat' function. */
+#cmakedefine01 HAVE_FSTAT
+
+/* Define to 1 if you have the `fstat64' function. */
+#cmakedefine01 HAVE_FSTAT64
+
+/* Define to 1 if you have the `fsync' function. */
+#cmakedefine01 HAVE_FSYNC
+
+/* Define to 1 if you have the `ftruncate' function. */
+#cmakedefine01 HAVE_FTRUNCATE
+
+/* Define to 1 if you have the `getpagesize' function. */
+#cmakedefine01 HAVE_GETPAGESIZE
+
+/* Define to 1 if you have the `gettimeofday' function. */
+#cmakedefine01 HAVE_GETTIMEOFDAY
+
+/* Define if you have the `gmtime' function. */
+#cmakedefine HAVE_GMTIME
+
+/* Define if you have the `gmtime_r' function. */
+#cmakedefine HAVE_GMTIME_R
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#cmakedefine01 HAVE_INTTYPES_H
+
+/* Define to 1 if you have the <io.h> header file. */
+#cmakedefine HAVE_IO_H
+
+/* Define to 1 if you have the `m' library (-lm). */
+#cmakedefine01 HAVE_LIBM
+
+/* Define to 1 if you have the <locale.h> header file. */
+#cmakedefine01 HAVE_LOCALE_H
+
+/* Define if you have the `localtime' function. */
+#cmakedefine HAVE_LOCALTIME
+
+/* Define if you have the `localtime_r' function. */
+#cmakedefine HAVE_LOCALTIME_R
+
+/* Define if you have C99's lrint function. */
+#cmakedefine01 HAVE_LRINT
+
+/* Define if you have C99's lrintf function. */
+#cmakedefine01 HAVE_LRINTF
+
+/* Define to 1 if you have the `lround' function. */
+#cmakedefine01 HAVE_LROUND
+
+/* Define to 1 if you have the `lseek' function. */
+#cmakedefine01 HAVE_LSEEK
+
+/* Define to 1 if you have the `lseek64' function. */
+#cmakedefine01 HAVE_LSEEK64
+
+/* Define to 1 if you have the `malloc' function. */
+#cmakedefine01 HAVE_MALLOC
+
+/* Define to 1 if you have the <memory.h> header file. */
+#cmakedefine01 HAVE_MEMORY_H
+
+/* Define to 1 if you have the `mmap' function. */
+#cmakedefine01 HAVE_MMAP
+
+/* Define to 1 if you have the `open' function. */
+#cmakedefine01 HAVE_OPEN
+
+/* Define to 1 if you have the `pipe' function. */
+#cmakedefine01 HAVE_PIPE
+
+/* Define to 1 if you have the `read' function. */
+#cmakedefine01 HAVE_READ
+
+/* Define to 1 if you have the `realloc' function. */
+#cmakedefine01 HAVE_REALLOC
+
+/* Define to 1 if you have the `setlocale' function. */
+#cmakedefine01 HAVE_SETLOCALE
+
+/* Set to 1 if <sndio.h> is available. */
+#cmakedefine01 HAVE_SNDIO_H
+
+/* Define to 1 if you have the `snprintf' function. */
+#cmakedefine01 HAVE_SNPRINTF
+
+/* Set to 1 if you have libsqlite3. */
+#cmakedefine01 HAVE_SQLITE3
+
+/* Define to 1 if the system has the type `ssize_t'. */
+#cmakedefine01 HAVE_SSIZE_T
+
+#if (HAVE_SSIZE_T == 0)
+#define ssize_t intptr_t
+#endif
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#cmakedefine01 HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#cmakedefine01 HAVE_STDLIB_H
+
+/* Define to 1 if you have the <strings.h> header file. */
+#cmakedefine01 HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#cmakedefine01 HAVE_STRING_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#cmakedefine01 HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#cmakedefine01 HAVE_SYS_TIME_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#cmakedefine01 HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */
+#cmakedefine01 HAVE_SYS_WAIT_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#cmakedefine01 HAVE_UNISTD_H
+
+/* Define to 1 if you have the <immintrin.h> header file. */
+#cmakedefine01 HAVE_IMMINTRIN_H
+
+/* Define to 1 if you have the <stdbool.h> header file. */
+#cmakedefine01 HAVE_STDBOOL_H
+
+/* Define to 1 if you have the `vsnprintf' function. */
+#cmakedefine01 HAVE_VSNPRINTF
+
+/* Define to 1 if you have the `waitpid' function. */
+#cmakedefine01 HAVE_WAITPID
+
+/* Define to 1 if you have the `write' function. */
+#cmakedefine01 HAVE_WRITE
+
+/* The darwin version, no-zero is valid */
+#cmakedefine01 OSX_DARWIN_VERSION
+
+/* Set to 1 if compiling for OpenBSD */
+#cmakedefine01 OS_IS_OPENBSD
+
+/* Set to 1 if compiling for Win32 */
+#cmakedefine01 OS_IS_WIN32
+
+/* Set to 1 if SSE2 is enabled */
+#cmakedefine USE_SSE2
+
+/* Name of package */
+#define PACKAGE "@PACKAGE_NAME@"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "@PACKAGE_BUGREPORT@"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "@PACKAGE_NAME@"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "@PACKAGE_NAME@ @CPACK_PACKAGE_VERSION_FULL@"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "@PACKAGE_NAME@"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL "@PACKAGE_URL@"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "@CPACK_PACKAGE_VERSION_FULL@"
+
+/* The size of `double', as computed by sizeof. */
+@SIZEOF_DOUBLE_CODE@
+
+/* The size of `float', as computed by sizeof. */
+@SIZEOF_FLOAT_CODE@
+
+/* The size of `int', as computed by sizeof. */
+@SIZEOF_INT_CODE@
+
+/* The size of `int64_t', as computed by sizeof. */
+@SIZEOF_INT64_T_CODE@
+
+/* The size of `loff_t', as computed by sizeof. */
+@SIZEOF_LOFF_T_CODE@
+
+/* The size of `long', as computed by sizeof. */
+@SIZEOF_LONG_CODE@
+
+/* The size of `long long', as computed by sizeof. */
+@SIZEOF_LONG_LONG_CODE@
+
+/* The size of `off64_t', as computed by sizeof. */
+@SIZEOF_OFF64_T_CODE@
+
+/* The size of `off_t', as computed by sizeof. */
+@SIZEOF_OFF_T_CODE@
+
+/* The size of `short', as computed by sizeof. */
+@SIZEOF_SHORT_CODE@
+
+/* The size of `size_t', as computed by sizeof. */
+@SIZEOF_SIZE_T_CODE@
+
+/* The size of `ssize_t', as computed by sizeof. */
+@SIZEOF_SSIZE_T_CODE@
+
+/* The size of `void*', as computed by sizeof. */
+@SIZEOF_VOIDP_CODE@
+
+/* The size of `wchar_t', as computed by sizeof. */
+@SIZEOF_WCHAR_T_CODE@
+
+/* Enable extensions on AIX 3, Interix.  */
+#ifndef _ALL_SOURCE
+# undef _ALL_SOURCE
+#endif
+/* Enable GNU extensions on systems that have them.  */
+#ifndef _GNU_SOURCE
+# undef _GNU_SOURCE
+#endif
+/* Enable threading extensions on Solaris.  */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# undef _POSIX_PTHREAD_SEMANTICS
+#endif
+/* Enable extensions on HP NonStop.  */
+#ifndef _TANDEM_SOURCE
+# undef _TANDEM_SOURCE
+#endif
+/* Enable general extensions on Solaris.  */
+#ifndef __EXTENSIONS__
+# undef __EXTENSIONS__
+#endif
+
+
+/* Set to 1 to use the native windows API */
+#cmakedefine01 USE_WINDOWS_API
+
+/* Version number of package */
+#define VERSION "@PROJECT_VERSION@"
+
+/* Set to 1 if windows DLL is being built. */
+#cmakedefine01 WIN32_TARGET_DLL
+
+/* Target processor is big endian. */
+#cmakedefine01 WORDS_BIGENDIAN
+
+/* Enable large inode numbers on Mac OS X 10.5.  */
+#ifndef _DARWIN_USE_64_BIT_INODE
+# define _DARWIN_USE_64_BIT_INODE 1
+#endif
+
+/* Define to 1 if on MINIX. */
+#cmakedefine01 _MINIX
+
+/* Define as `__inline' or '__inline__' if that's what the C compiler calls it, or to nothing if it is not supported. */
+@INLINE_CODE@
diff --git a/src/create_symbols_file.py b/src/create_symbols_file.py
new file mode 100644
index 0000000..c0debce
--- /dev/null
+++ b/src/create_symbols_file.py
@@ -0,0 +1,181 @@
+#!/usr/bin/python
+
+# Copyright (C) 2003-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#     * Neither the author nor the names of any contributors may be used
+#       to endorse or promote products derived from this software without
+#       specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import re, sys
+
+#----------------------------------------------------------------
+# These are all of the public functions exported from libsndfile.
+#
+# Its important not to change the order they are listed in or
+# the ordinal values in the second column.
+
+ALL_SYMBOLS = (
+	(	"sf_command",	 		1	),
+	(	"sf_open",				2	),
+	(	"sf_close",				3	),
+	(	"sf_seek",				4	),
+	(	"sf_error",				7	),
+	(	"sf_perror",			8	),
+	(	"sf_error_str",			9	),
+	(	"sf_error_number",		10	),
+	(	"sf_format_check",		11	),
+	(	"sf_read_raw",			16	),
+	(	"sf_readf_short",		17	),
+	(	"sf_readf_int",			18	),
+	(	"sf_readf_float",		19	),
+	(	"sf_readf_double",		20	),
+	(	"sf_read_short",		21	),
+	(	"sf_read_int",			22	),
+	(	"sf_read_float",		23	),
+	(	"sf_read_double",		24	),
+	(	"sf_write_raw",			32	),
+	(	"sf_writef_short",		33	),
+	(	"sf_writef_int",		34	),
+	(	"sf_writef_float",		35	),
+	(	"sf_writef_double",		36	),
+	(	"sf_write_short",		37	),
+	(	"sf_write_int",			38	),
+	(	"sf_write_float",		39	),
+	(	"sf_write_double",		40	),
+	(	"sf_strerror",			50	),
+	(	"sf_get_string",		60	),
+	(	"sf_set_string",		61	),
+	(	"sf_version_string",	68	),
+	(	"sf_open_fd",			70	),
+	(	"sf_wchar_open",		71  ),
+	(	"sf_open_virtual",		80	),
+	(	"sf_write_sync",		90	),
+	(	"sf_set_chunk",			100	),
+	(	"sf_get_chunk_size",	101 ),
+	(	"sf_get_chunk_data",	102 ),
+	(	"sf_get_chunk_iterator",	103 ),
+	(	"sf_next_chunk_iterator",	104 ),
+	(	"sf_current_byterate",	110 )
+	)
+
+#-------------------------------------------------------------------------------
+
+def linux_symbols (progname, version):
+	print ("# Auto-generated by %s\n" %progname)
+	print ("libsndfile.so.%s" % version)
+	print ("{")
+	print ("  global:")
+	for name, ordinal in ALL_SYMBOLS:
+		if  name == "sf_wchar_open":
+			continue
+		print ("    %s ;" % name)
+	print ("  local:")
+	print ("    * ;")
+	print ("} ;")
+	sys.stdout.write ("\n")
+	return
+
+def darwin_symbols (progname, version):
+	print ("# Auto-generated by %s\n" %progname)
+	for name, ordinal in ALL_SYMBOLS:
+		if  name == "sf_wchar_open":
+			continue
+		print ("_%s" % name)
+	sys.stdout.write ("\n")
+	return
+
+def win32_symbols (progname, version, name):
+	print ("; Auto-generated by %s\n" %progname)
+	print ("EXPORTS\n")
+	for name, ordinal in ALL_SYMBOLS:
+		print ("%-20s @%s" % (name, ordinal))
+	sys.stdout.write ("\n")
+	return
+
+def os2_symbols (progname, version, name):
+	print ("; Auto-generated by %s\n" %progname)
+	print ("LIBRARY %s%s" % (name, re.sub (r"\..*", "", version)))
+	print ("INITINSTANCE TERMINSTANCE")
+	print ("CODE PRELOAD MOVEABLE DISCARDABLE")
+	print ("DATA PRELOAD MOVEABLE MULTIPLE NONSHARED")
+	print ("EXPORTS\n")
+	for name, ordinal in ALL_SYMBOLS:
+		if  name == "sf_wchar_open":
+			continue
+		print ("_%-20s @%s" % (name, ordinal))
+	sys.stdout.write ("\n")
+	return
+
+def plain_symbols (progname, version, name):
+	for name, ordinal in ALL_SYMBOLS:
+		print (name)
+
+def no_symbols (os_name):
+	sys.stdout.write ("\n")
+	print ("No known way of restricting exported symbols on '%s'." % os_name)
+	print ("If you know a way, please contact the author.")
+	sys.stdout.write ("\n")
+	return
+
+#-------------------------------------------------------------------------------
+
+progname = re.sub (".*[\\/]", "", sys.argv [0])
+
+if len (sys.argv) != 3:
+	sys.stdout.write ("\n")
+	print ("Usage : %s <target OS name> <libsndfile version>." % progname)
+	sys.stdout.write ("\n")
+	print ("    Currently supported values for target OS are:")
+	print ("          linux")
+	print ("          darwin     (ie MacOSX)")
+	print ("          win32      (ie wintendo)")
+	print ("          cygwin     (Cygwin on wintendo)")
+	print ("          os2        (OS/2)")
+	print ("          plain      (plain list of symbols)")
+	sys.stdout.write ("\n")
+	sys.exit (1)
+
+os_name = sys.argv [1]
+version = re.sub (r"\.[a-z0-9]+$", "", sys.argv [2])
+
+if os_name == "linux" or os_name == "gnu" or os_name == "binutils":
+	linux_symbols (progname, version)
+elif os_name == "darwin":
+	darwin_symbols (progname, version)
+elif os_name == "win32":
+	win32_symbols (progname, version, "libsndfile")
+elif os_name == "cygwin":
+	win32_symbols (progname, version, "cygsndfile")
+elif os_name == "os2":
+	os2_symbols (progname, version, "sndfile")
+elif os_name == "static":
+	plain_symbols (progname, version, "")
+else:
+	no_symbols (os_name)
+
+sys.exit (0)
+
diff --git a/src/dither.c b/src/dither.c
new file mode 100644
index 0000000..b0ef731
--- /dev/null
+++ b/src/dither.c
@@ -0,0 +1,534 @@
+/*
+** Copyright (C) 2003-2011 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdlib.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+/*============================================================================
+**	Rule number 1 is to only apply dither when going from a larger bitwidth
+**	to a smaller bitwidth. This can happen on both read and write.
+**
+**	Need to apply dither on all conversions marked X below.
+**
+**	Dither on write:
+**
+**										Input
+**					|	short		int			float		double
+**			--------+-----------------------------------------------
+**		O	8 bit	|	X			X			X			X
+**		u	16 bit	|	none		X			X			X
+**		t	24 bit	|	none		X			X			X
+**		p	32 bit	|	none		none		X			X
+**		u	float	|	none		none		none		none
+**		t	double	|	none		none		none		none
+**
+**	Dither on read:
+**
+**										Input
+**		O			|	8 bit	16 bit	24 bit	32 bit	float	double
+**		u	--------+-------------------------------------------------
+**		t	short	|	none	none	X		X		X		X
+**		p	int		|	none	none	none	X		X		X
+**		u	float	|	none	none	none	none	none	none
+**		t	double	|	none	none	none	none	none	none
+*/
+
+#define	SFE_DITHER_BAD_PTR	666
+#define	SFE_DITHER_BAD_TYPE	667
+
+typedef struct
+{	int			read_short_dither_bits, read_int_dither_bits ;
+	int			write_short_dither_bits, write_int_dither_bits ;
+	double		read_float_dither_scale, read_double_dither_bits ;
+	double		write_float_dither_scale, write_double_dither_bits ;
+
+	sf_count_t	(*read_short)	(SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
+	sf_count_t	(*read_int)		(SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
+	sf_count_t	(*read_float)	(SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
+	sf_count_t	(*read_double)	(SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
+
+	sf_count_t	(*write_short)	(SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
+	sf_count_t	(*write_int)	(SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
+	sf_count_t	(*write_float)	(SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
+	sf_count_t	(*write_double)	(SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
+
+	double buffer [SF_BUFFER_LEN / sizeof (double)] ;
+} DITHER_DATA ;
+
+static sf_count_t dither_read_short		(SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
+static sf_count_t dither_read_int		(SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
+
+static sf_count_t dither_write_short	(SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
+static sf_count_t dither_write_int		(SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
+static sf_count_t dither_write_float	(SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
+static sf_count_t dither_write_double	(SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
+
+int
+dither_init (SF_PRIVATE *psf, int mode)
+{	DITHER_DATA *pdither ;
+
+	pdither = psf->dither ; /* This may be NULL. */
+
+	/* Turn off dither on read. */
+	if (mode == SFM_READ && psf->read_dither.type == SFD_NO_DITHER)
+	{	if (pdither == NULL)
+			return 0 ; /* Dither is already off, so just return. */
+
+		if (pdither->read_short)
+			psf->read_short = pdither->read_short ;
+		if (pdither->read_int)
+			psf->read_int = pdither->read_int ;
+		if (pdither->read_float)
+			psf->read_float = pdither->read_float ;
+		if (pdither->read_double)
+			psf->read_double = pdither->read_double ;
+		return 0 ;
+		} ;
+
+	/* Turn off dither on write. */
+	if (mode == SFM_WRITE && psf->write_dither.type == SFD_NO_DITHER)
+	{	if (pdither == NULL)
+			return 0 ; /* Dither is already off, so just return. */
+
+		if (pdither->write_short)
+			psf->write_short = pdither->write_short ;
+		if (pdither->write_int)
+			psf->write_int = pdither->write_int ;
+		if (pdither->write_float)
+			psf->write_float = pdither->write_float ;
+		if (pdither->write_double)
+			psf->write_double = pdither->write_double ;
+		return 0 ;
+		} ;
+
+	/* Turn on dither on read if asked. */
+	if (mode == SFM_READ && psf->read_dither.type != 0)
+	{	if (pdither == NULL)
+			pdither = psf->dither = calloc (1, sizeof (DITHER_DATA)) ;
+		if (pdither == NULL)
+			return SFE_MALLOC_FAILED ;
+
+		switch (SF_CODEC (psf->sf.format))
+		{	case SF_FORMAT_DOUBLE :
+			case SF_FORMAT_FLOAT :
+					pdither->read_int = psf->read_int ;
+					psf->read_int = dither_read_int ;
+					break ;
+
+			case SF_FORMAT_PCM_32 :
+			case SF_FORMAT_PCM_24 :
+			case SF_FORMAT_PCM_16 :
+			case SF_FORMAT_PCM_S8 :
+			case SF_FORMAT_PCM_U8 :
+					pdither->read_short = psf->read_short ;
+					psf->read_short = dither_read_short ;
+					break ;
+
+			default : break ;
+			} ;
+		} ;
+
+	/* Turn on dither on write if asked. */
+	if (mode == SFM_WRITE && psf->write_dither.type != 0)
+	{	if (pdither == NULL)
+			pdither = psf->dither = calloc (1, sizeof (DITHER_DATA)) ;
+		if (pdither == NULL)
+			return SFE_MALLOC_FAILED ;
+
+		switch (SF_CODEC (psf->sf.format))
+		{	case SF_FORMAT_DOUBLE :
+			case SF_FORMAT_FLOAT :
+					pdither->write_int = psf->write_int ;
+					psf->write_int = dither_write_int ;
+					break ;
+
+			case SF_FORMAT_PCM_32 :
+			case SF_FORMAT_PCM_24 :
+			case SF_FORMAT_PCM_16 :
+			case SF_FORMAT_PCM_S8 :
+			case SF_FORMAT_PCM_U8 :
+					break ;
+
+			default : break ;
+			} ;
+
+		pdither->write_short = psf->write_short ;
+		psf->write_short = dither_write_short ;
+
+		pdither->write_int = psf->write_int ;
+		psf->write_int = dither_write_int ;
+
+		pdither->write_float = psf->write_float ;
+		psf->write_float = dither_write_float ;
+
+		pdither->write_double = psf->write_double ;
+		psf->write_double = dither_write_double ;
+		} ;
+
+	return 0 ;
+} /* dither_init */
+
+/*==============================================================================
+*/
+
+static void dither_short	(const short *in, short *out, int frames, int channels) ;
+static void dither_int		(const int *in, int *out, int frames, int channels) ;
+
+static void dither_float	(const float *in, float *out, int frames, int channels) ;
+static void dither_double	(const double *in, double *out, int frames, int channels) ;
+
+static sf_count_t
+dither_read_short (SF_PRIVATE * UNUSED (psf), short * UNUSED (ptr), sf_count_t len)
+{
+	return len ;
+} /* dither_read_short */
+
+static sf_count_t
+dither_read_int (SF_PRIVATE * UNUSED (psf), int * UNUSED (ptr), sf_count_t len)
+{
+	return len ;
+} /* dither_read_int */
+
+/*------------------------------------------------------------------------------
+*/
+
+static sf_count_t
+dither_write_short	(SF_PRIVATE *psf, const short *ptr, sf_count_t len)
+{	DITHER_DATA *pdither ;
+	int			bufferlen, writecount, thiswrite ;
+	sf_count_t	total = 0 ;
+
+	if ((pdither = psf->dither) == NULL)
+	{	psf->error = SFE_DITHER_BAD_PTR ;
+		return 0 ;
+		} ;
+
+	switch (SF_CODEC (psf->sf.format))
+	{	case SF_FORMAT_PCM_S8 :
+		case SF_FORMAT_PCM_U8 :
+		case SF_FORMAT_DPCM_8 :
+				break ;
+
+		default :
+			return pdither->write_short (psf, ptr, len) ;
+		} ;
+
+	bufferlen = sizeof (pdither->buffer) / (sizeof (short)) ;
+
+	while (len > 0)
+	{	writecount = (len >= bufferlen) ? bufferlen : (int) len ;
+		writecount /= psf->sf.channels ;
+		writecount *= psf->sf.channels ;
+
+		dither_short (ptr, (short*) pdither->buffer, writecount / psf->sf.channels, psf->sf.channels) ;
+
+		thiswrite = (int) pdither->write_short (psf, (short*) pdither->buffer, writecount) ;
+		total += thiswrite ;
+		len -= thiswrite ;
+		if (thiswrite < writecount)
+			break ;
+		} ;
+
+	return total ;
+} /* dither_write_short */
+
+static sf_count_t
+dither_write_int	(SF_PRIVATE *psf, const int *ptr, sf_count_t len)
+{	DITHER_DATA *pdither ;
+	int			bufferlen, writecount, thiswrite ;
+	sf_count_t	total = 0 ;
+
+	if ((pdither = psf->dither) == NULL)
+	{	psf->error = SFE_DITHER_BAD_PTR ;
+		return 0 ;
+		} ;
+
+	switch (SF_CODEC (psf->sf.format))
+	{	case SF_FORMAT_PCM_S8 :
+		case SF_FORMAT_PCM_U8 :
+		case SF_FORMAT_PCM_16 :
+		case SF_FORMAT_PCM_24 :
+				break ;
+
+		case SF_FORMAT_DPCM_8 :
+		case SF_FORMAT_DPCM_16 :
+				break ;
+
+		default :
+			return pdither->write_int (psf, ptr, len) ;
+		} ;
+
+
+	bufferlen = sizeof (pdither->buffer) / (sizeof (int)) ;
+
+	while (len > 0)
+	{	writecount = (len >= bufferlen) ? bufferlen : (int) len ;
+		writecount /= psf->sf.channels ;
+		writecount *= psf->sf.channels ;
+
+		dither_int (ptr, (int*) pdither->buffer, writecount / psf->sf.channels, psf->sf.channels) ;
+
+		thiswrite = (int) pdither->write_int (psf, (int*) pdither->buffer, writecount) ;
+		total += thiswrite ;
+		len -= thiswrite ;
+		if (thiswrite < writecount)
+			break ;
+		} ;
+
+	return total ;
+} /* dither_write_int */
+
+static sf_count_t
+dither_write_float	(SF_PRIVATE *psf, const float *ptr, sf_count_t len)
+{	DITHER_DATA *pdither ;
+	int			bufferlen, writecount, thiswrite ;
+	sf_count_t	total = 0 ;
+
+	if ((pdither = psf->dither) == NULL)
+	{	psf->error = SFE_DITHER_BAD_PTR ;
+		return 0 ;
+		} ;
+
+	switch (SF_CODEC (psf->sf.format))
+	{	case SF_FORMAT_PCM_S8 :
+		case SF_FORMAT_PCM_U8 :
+		case SF_FORMAT_PCM_16 :
+		case SF_FORMAT_PCM_24 :
+				break ;
+
+		case SF_FORMAT_DPCM_8 :
+		case SF_FORMAT_DPCM_16 :
+				break ;
+
+		default :
+			return pdither->write_float (psf, ptr, len) ;
+		} ;
+
+	bufferlen = sizeof (pdither->buffer) / (sizeof (float)) ;
+
+	while (len > 0)
+	{	writecount = (len >= bufferlen) ? bufferlen : (int) len ;
+		writecount /= psf->sf.channels ;
+		writecount *= psf->sf.channels ;
+
+		dither_float (ptr, (float*) pdither->buffer, writecount / psf->sf.channels, psf->sf.channels) ;
+
+		thiswrite = (int) pdither->write_float (psf, (float*) pdither->buffer, writecount) ;
+		total += thiswrite ;
+		len -= thiswrite ;
+		if (thiswrite < writecount)
+			break ;
+		} ;
+
+	return total ;
+} /* dither_write_float */
+
+static sf_count_t
+dither_write_double	(SF_PRIVATE *psf, const double *ptr, sf_count_t len)
+{	DITHER_DATA *pdither ;
+	int			bufferlen, writecount, thiswrite ;
+	sf_count_t	total = 0 ;
+
+	if ((pdither = psf->dither) == NULL)
+	{	psf->error = SFE_DITHER_BAD_PTR ;
+		return 0 ;
+		} ;
+
+	switch (SF_CODEC (psf->sf.format))
+	{	case SF_FORMAT_PCM_S8 :
+		case SF_FORMAT_PCM_U8 :
+		case SF_FORMAT_PCM_16 :
+		case SF_FORMAT_PCM_24 :
+				break ;
+
+		case SF_FORMAT_DPCM_8 :
+		case SF_FORMAT_DPCM_16 :
+				break ;
+
+		default :
+			return pdither->write_double (psf, ptr, len) ;
+		} ;
+
+
+	bufferlen = sizeof (pdither->buffer) / sizeof (double) ;
+
+	while (len > 0)
+	{	writecount = (len >= bufferlen) ? bufferlen : (int) len ;
+		writecount /= psf->sf.channels ;
+		writecount *= psf->sf.channels ;
+
+		dither_double (ptr, (double*) pdither->buffer, writecount / psf->sf.channels, psf->sf.channels) ;
+
+		thiswrite = (int) pdither->write_double (psf, (double*) pdither->buffer, writecount) ;
+		total += thiswrite ;
+		len -= thiswrite ;
+		if (thiswrite < writecount)
+			break ;
+		} ;
+
+	return total ;
+} /* dither_write_double */
+
+/*==============================================================================
+*/
+
+static void
+dither_short (const short *in, short *out, int frames, int channels)
+{	int ch, k ;
+
+	for (ch = 0 ; ch < channels ; ch++)
+		for (k = ch ; k < channels * frames ; k += channels)
+			out [k] = in [k] ;
+
+} /* dither_short */
+
+static void
+dither_int (const int *in, int *out, int frames, int channels)
+{	int ch, k ;
+
+	for (ch = 0 ; ch < channels ; ch++)
+		for (k = ch ; k < channels * frames ; k += channels)
+			out [k] = in [k] ;
+
+} /* dither_int */
+
+static void
+dither_float (const float *in, float *out, int frames, int channels)
+{	int ch, k ;
+
+	for (ch = 0 ; ch < channels ; ch++)
+		for (k = ch ; k < channels * frames ; k += channels)
+			out [k] = in [k] ;
+
+} /* dither_float */
+
+static void
+dither_double (const double *in, double *out, int frames, int channels)
+{	int ch, k ;
+
+	for (ch = 0 ; ch < channels ; ch++)
+		for (k = ch ; k < channels * frames ; k += channels)
+			out [k] = in [k] ;
+
+} /* dither_double */
+
+/*==============================================================================
+*/
+#if 0
+
+/*
+** Not made public because this (maybe) requires storage of state information.
+**
+** Also maybe need separate state info for each channel!!!!
+*/
+
+int
+DO_NOT_USE_sf_dither_short (const SF_DITHER_INFO *dither, const short *in, short *out, int frames, int channels)
+{	int ch, k ;
+
+	if (! dither)
+		return SFE_DITHER_BAD_PTR ;
+
+	switch (dither->type & SFD_TYPEMASK)
+	{	case SFD_WHITE :
+		case SFD_TRIANGULAR_PDF :
+				for (ch = 0 ; ch < channels ; ch++)
+					for (k = ch ; k < channels * frames ; k += channels)
+						out [k] = in [k] ;
+				break ;
+
+		default :
+			return SFE_DITHER_BAD_TYPE ;
+		} ;
+
+	return 0 ;
+} /* DO_NOT_USE_sf_dither_short */
+
+int
+DO_NOT_USE_sf_dither_int (const SF_DITHER_INFO *dither, const int *in, int *out, int frames, int channels)
+{	int ch, k ;
+
+	if (! dither)
+		return SFE_DITHER_BAD_PTR ;
+
+	switch (dither->type & SFD_TYPEMASK)
+	{	case SFD_WHITE :
+		case SFD_TRIANGULAR_PDF :
+				for (ch = 0 ; ch < channels ; ch++)
+					for (k = ch ; k < channels * frames ; k += channels)
+						out [k] = in [k] ;
+				break ;
+
+		default :
+			return SFE_DITHER_BAD_TYPE ;
+		} ;
+
+	return 0 ;
+} /* DO_NOT_USE_sf_dither_int */
+
+int
+DO_NOT_USE_sf_dither_float (const SF_DITHER_INFO *dither, const float *in, float *out, int frames, int channels)
+{	int ch, k ;
+
+	if (! dither)
+		return SFE_DITHER_BAD_PTR ;
+
+	switch (dither->type & SFD_TYPEMASK)
+	{	case SFD_WHITE :
+		case SFD_TRIANGULAR_PDF :
+				for (ch = 0 ; ch < channels ; ch++)
+					for (k = ch ; k < channels * frames ; k += channels)
+						out [k] = in [k] ;
+				break ;
+
+		default :
+			return SFE_DITHER_BAD_TYPE ;
+		} ;
+
+	return 0 ;
+} /* DO_NOT_USE_sf_dither_float */
+
+int
+DO_NOT_USE_sf_dither_double (const SF_DITHER_INFO *dither, const double *in, double *out, int frames, int channels)
+{	int ch, k ;
+
+	if (! dither)
+		return SFE_DITHER_BAD_PTR ;
+
+	switch (dither->type & SFD_TYPEMASK)
+	{	case SFD_WHITE :
+		case SFD_TRIANGULAR_PDF :
+				for (ch = 0 ; ch < channels ; ch++)
+					for (k = ch ; k < channels * frames ; k += channels)
+						out [k] = in [k] ;
+				break ;
+
+		default :
+			return SFE_DITHER_BAD_TYPE ;
+		} ;
+
+	return 0 ;
+} /* DO_NOT_USE_sf_dither_double */
+
+#endif
+
diff --git a/src/double64.c b/src/double64.c
new file mode 100644
index 0000000..0c79cd5
--- /dev/null
+++ b/src/double64.c
@@ -0,0 +1,1063 @@
+/*
+** Copyright (C) 1999-2015 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<stdlib.h>
+#include	<string.h>
+#include	<limits.h>
+#include	<math.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+#if CPU_IS_LITTLE_ENDIAN
+	#define DOUBLE64_READ	double64_le_read
+	#define DOUBLE64_WRITE	double64_le_write
+#elif CPU_IS_BIG_ENDIAN
+	#define DOUBLE64_READ	double64_be_read
+	#define DOUBLE64_WRITE	double64_be_write
+#endif
+
+/* A 32 number which will not overflow when multiplied by sizeof (double). */
+#define SENSIBLE_LEN	(0x8000000)
+
+/*--------------------------------------------------------------------------------------------
+**	Processor floating point capabilities. double64_get_capability () returns one of the
+**	latter three values.
+*/
+
+enum
+{	DOUBLE_UNKNOWN		= 0x00,
+	DOUBLE_CAN_RW_LE	= 0x23,
+	DOUBLE_CAN_RW_BE	= 0x34,
+	DOUBLE_BROKEN_LE	= 0x45,
+	DOUBLE_BROKEN_BE	= 0x56
+} ;
+
+/*--------------------------------------------------------------------------------------------
+**	Prototypes for private functions.
+*/
+
+static sf_count_t		host_read_d2s	(SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
+static sf_count_t		host_read_d2i	(SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
+static sf_count_t		host_read_d2f	(SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
+static sf_count_t		host_read_d		(SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
+
+static sf_count_t		host_write_s2d	(SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
+static sf_count_t		host_write_i2d	(SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
+static sf_count_t		host_write_f2d	(SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
+static sf_count_t		host_write_d	(SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
+
+static void		double64_peak_update	(SF_PRIVATE *psf, const double *buffer, int count, sf_count_t indx) ;
+
+static int		double64_get_capability	(SF_PRIVATE *psf) ;
+
+static sf_count_t	replace_read_d2s	(SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
+static sf_count_t	replace_read_d2i	(SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
+static sf_count_t	replace_read_d2f	(SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
+static sf_count_t	replace_read_d	(SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
+
+static sf_count_t	replace_write_s2d	(SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
+static sf_count_t	replace_write_i2d	(SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
+static sf_count_t	replace_write_f2d	(SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
+static sf_count_t	replace_write_d	(SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
+
+static	void	d2bd_read (double *buffer, int count) ;
+static	void	bd2d_write (double *buffer, int count) ;
+
+/*--------------------------------------------------------------------------------------------
+**	Exported functions.
+*/
+
+int
+double64_init	(SF_PRIVATE *psf)
+{	static int double64_caps ;
+
+	if (psf->sf.channels < 1 || psf->sf.channels > SF_MAX_CHANNELS)
+	{	psf_log_printf (psf, "double64_init : internal error : channels = %d\n", psf->sf.channels) ;
+		return SFE_INTERNAL ;
+		} ;
+
+	double64_caps = double64_get_capability (psf) ;
+
+	psf->blockwidth = sizeof (double) * psf->sf.channels ;
+
+	if (psf->file.mode == SFM_READ || psf->file.mode == SFM_RDWR)
+	{	switch (psf->endian + double64_caps)
+		{	case (SF_ENDIAN_BIG + DOUBLE_CAN_RW_BE) :
+					psf->data_endswap = SF_FALSE ;
+					psf->read_short		= host_read_d2s ;
+					psf->read_int		= host_read_d2i ;
+					psf->read_float		= host_read_d2f ;
+					psf->read_double	= host_read_d ;
+					break ;
+
+			case (SF_ENDIAN_LITTLE + DOUBLE_CAN_RW_LE) :
+					psf->data_endswap = SF_FALSE ;
+					psf->read_short		= host_read_d2s ;
+					psf->read_int		= host_read_d2i ;
+					psf->read_float		= host_read_d2f ;
+					psf->read_double	= host_read_d ;
+					break ;
+
+			case (SF_ENDIAN_BIG + DOUBLE_CAN_RW_LE) :
+					psf->data_endswap = SF_TRUE ;
+					psf->read_short		= host_read_d2s ;
+					psf->read_int		= host_read_d2i ;
+					psf->read_float		= host_read_d2f ;
+					psf->read_double	= host_read_d ;
+					break ;
+
+			case (SF_ENDIAN_LITTLE + DOUBLE_CAN_RW_BE) :
+					psf->data_endswap = SF_TRUE ;
+					psf->read_short		= host_read_d2s ;
+					psf->read_int		= host_read_d2i ;
+					psf->read_float		= host_read_d2f ;
+					psf->read_double	= host_read_d ;
+					break ;
+
+			/* When the CPU is not IEEE compatible. */
+			case (SF_ENDIAN_BIG + DOUBLE_BROKEN_BE) :
+					psf->data_endswap = SF_FALSE ;
+					psf->read_short		= replace_read_d2s ;
+					psf->read_int		= replace_read_d2i ;
+					psf->read_float		= replace_read_d2f ;
+					psf->read_double	= replace_read_d ;
+					break ;
+
+			case (SF_ENDIAN_LITTLE + DOUBLE_BROKEN_LE) :
+					psf->data_endswap = SF_FALSE ;
+					psf->read_short		= replace_read_d2s ;
+					psf->read_int		= replace_read_d2i ;
+					psf->read_float		= replace_read_d2f ;
+					psf->read_double	= replace_read_d ;
+					break ;
+
+			case (SF_ENDIAN_BIG + DOUBLE_BROKEN_LE) :
+					psf->data_endswap = SF_TRUE ;
+					psf->read_short		= replace_read_d2s ;
+					psf->read_int		= replace_read_d2i ;
+					psf->read_float		= replace_read_d2f ;
+					psf->read_double	= replace_read_d ;
+					break ;
+
+			case (SF_ENDIAN_LITTLE + DOUBLE_BROKEN_BE) :
+					psf->data_endswap = SF_TRUE ;
+					psf->read_short		= replace_read_d2s ;
+					psf->read_int		= replace_read_d2i ;
+					psf->read_float		= replace_read_d2f ;
+					psf->read_double	= replace_read_d ;
+					break ;
+
+			default : break ;
+			} ;
+		} ;
+
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	switch (psf->endian + double64_caps)
+		{	case (SF_ENDIAN_LITTLE + DOUBLE_CAN_RW_LE) :
+					psf->data_endswap = SF_FALSE ;
+					psf->write_short	= host_write_s2d ;
+					psf->write_int		= host_write_i2d ;
+					psf->write_float	= host_write_f2d ;
+					psf->write_double	= host_write_d ;
+					break ;
+
+			case (SF_ENDIAN_BIG + DOUBLE_CAN_RW_BE) :
+					psf->data_endswap = SF_FALSE ;
+					psf->write_short	= host_write_s2d ;
+					psf->write_int		= host_write_i2d ;
+					psf->write_float	= host_write_f2d ;
+					psf->write_double	= host_write_d ;
+					break ;
+
+			case (SF_ENDIAN_BIG + DOUBLE_CAN_RW_LE) :
+					psf->data_endswap = SF_TRUE ;
+					psf->write_short	= host_write_s2d ;
+					psf->write_int		= host_write_i2d ;
+					psf->write_float	= host_write_f2d ;
+					psf->write_double	= host_write_d ;
+					break ;
+
+			case (SF_ENDIAN_LITTLE + DOUBLE_CAN_RW_BE) :
+					psf->data_endswap = SF_TRUE ;
+					psf->write_short	= host_write_s2d ;
+					psf->write_int		= host_write_i2d ;
+					psf->write_float	= host_write_f2d ;
+					psf->write_double	= host_write_d ;
+					break ;
+
+			/* When the CPU is not IEEE compatible. */
+			case (SF_ENDIAN_LITTLE + DOUBLE_BROKEN_LE) :
+					psf->data_endswap = SF_FALSE ;
+					psf->write_short	= replace_write_s2d ;
+					psf->write_int		= replace_write_i2d ;
+					psf->write_float	= replace_write_f2d ;
+					psf->write_double	= replace_write_d ;
+					break ;
+
+			case (SF_ENDIAN_BIG + DOUBLE_BROKEN_BE) :
+					psf->data_endswap = SF_FALSE ;
+					psf->write_short	= replace_write_s2d ;
+					psf->write_int		= replace_write_i2d ;
+					psf->write_float	= replace_write_f2d ;
+					psf->write_double	= replace_write_d ;
+					break ;
+
+			case (SF_ENDIAN_BIG + DOUBLE_BROKEN_LE) :
+					psf->data_endswap = SF_TRUE ;
+					psf->write_short	= replace_write_s2d ;
+					psf->write_int		= replace_write_i2d ;
+					psf->write_float	= replace_write_f2d ;
+					psf->write_double	= replace_write_d ;
+					break ;
+
+			case (SF_ENDIAN_LITTLE + DOUBLE_BROKEN_BE) :
+					psf->data_endswap = SF_TRUE ;
+					psf->write_short	= replace_write_s2d ;
+					psf->write_int		= replace_write_i2d ;
+					psf->write_float	= replace_write_f2d ;
+					psf->write_double	= replace_write_d ;
+					break ;
+
+			default : break ;
+			} ;
+		} ;
+
+	if (psf->filelength > psf->dataoffset)
+	{	psf->datalength = (psf->dataend > 0) ? psf->dataend - psf->dataoffset :
+							psf->filelength - psf->dataoffset ;
+		}
+	else
+		psf->datalength = 0 ;
+
+	psf->sf.frames = psf->datalength / psf->blockwidth ;
+
+	return 0 ;
+} /* double64_init */
+
+/*----------------------------------------------------------------------------
+** From : http://www.hpcf.cam.ac.uk/fp_formats.html
+**
+** 64 bit double precision layout (big endian)
+** 	  Sign				bit 0
+** 	  Exponent			bits 1-11
+** 	  Mantissa			bits 12-63
+** 	  Exponent Offset	1023
+**
+**            double             single
+**
+** +INF     7FF0000000000000     7F800000
+** -INF     FFF0000000000000     FF800000
+**  NaN     7FF0000000000001     7F800001
+**                to               to
+**          7FFFFFFFFFFFFFFF     7FFFFFFF
+**                and              and
+**          FFF0000000000001     FF800001
+**                to               to
+**          FFFFFFFFFFFFFFFF     FFFFFFFF
+** +OVER    7FEFFFFFFFFFFFFF     7F7FFFFF
+** -OVER    FFEFFFFFFFFFFFFF     FF7FFFFF
+** +UNDER   0010000000000000     00800000
+** -UNDER   8010000000000000     80800000
+*/
+
+double
+double64_be_read (const unsigned char *cptr)
+{	int		exponent, negative, upper, lower ;
+	double	dvalue ;
+
+	negative = (cptr [0] & 0x80) ? 1 : 0 ;
+	exponent = ((cptr [0] & 0x7F) << 4) | ((cptr [1] >> 4) & 0xF) ;
+
+	/* Might not have a 64 bit long, so load the mantissa into a double. */
+	upper = (((cptr [1] & 0xF) << 24) | (cptr [2] << 16) | (cptr [3] << 8) | cptr [4]) ;
+	lower = (cptr [5] << 16) | (cptr [6] << 8) | cptr [7] ;
+
+	if (exponent == 0 && upper == 0 && lower == 0)
+		return 0.0 ;
+
+	dvalue = upper + lower / ((double) 0x1000000) ;
+	dvalue += 0x10000000 ;
+
+	exponent = exponent - 0x3FF ;
+
+	dvalue = dvalue / ((double) 0x10000000) ;
+
+	if (negative)
+		dvalue *= -1 ;
+
+	if (exponent > 0)
+		dvalue *= pow (2.0, exponent) ;
+	else if (exponent < 0)
+		dvalue /= pow (2.0, abs (exponent)) ;
+
+	return dvalue ;
+} /* double64_be_read */
+
+double
+double64_le_read (const unsigned char *cptr)
+{	int		exponent, negative, upper, lower ;
+	double	dvalue ;
+
+	negative = (cptr [7] & 0x80) ? 1 : 0 ;
+	exponent = ((cptr [7] & 0x7F) << 4) | ((cptr [6] >> 4) & 0xF) ;
+
+	/* Might not have a 64 bit long, so load the mantissa into a double. */
+	upper = ((cptr [6] & 0xF) << 24) | (cptr [5] << 16) | (cptr [4] << 8) | cptr [3] ;
+	lower = (cptr [2] << 16) | (cptr [1] << 8) | cptr [0] ;
+
+	if (exponent == 0 && upper == 0 && lower == 0)
+		return 0.0 ;
+
+	dvalue = upper + lower / ((double) 0x1000000) ;
+	dvalue += 0x10000000 ;
+
+	exponent = exponent - 0x3FF ;
+
+	dvalue = dvalue / ((double) 0x10000000) ;
+
+	if (negative)
+		dvalue *= -1 ;
+
+	if (exponent > 0)
+		dvalue *= pow (2.0, exponent) ;
+	else if (exponent < 0)
+		dvalue /= pow (2.0, abs (exponent)) ;
+
+	return dvalue ;
+} /* double64_le_read */
+
+void
+double64_be_write (double in, unsigned char *out)
+{	int		exponent, mantissa ;
+
+	memset (out, 0, sizeof (double)) ;
+
+	if (fabs (in) < 1e-30)
+		return ;
+
+	if (in < 0.0)
+	{	in *= -1.0 ;
+		out [0] |= 0x80 ;
+		} ;
+
+	in = frexp (in, &exponent) ;
+
+	exponent += 1022 ;
+
+	out [0] |= (exponent >> 4) & 0x7F ;
+	out [1] |= (exponent << 4) & 0xF0 ;
+
+	in *= 0x20000000 ;
+	mantissa = psf_lrint (floor (in)) ;
+
+	out [1] |= (mantissa >> 24) & 0xF ;
+	out [2] = (mantissa >> 16) & 0xFF ;
+	out [3] = (mantissa >> 8) & 0xFF ;
+	out [4] = mantissa & 0xFF ;
+
+	in = fmod (in, 1.0) ;
+	in *= 0x1000000 ;
+	mantissa = psf_lrint (floor (in)) ;
+
+	out [5] = (mantissa >> 16) & 0xFF ;
+	out [6] = (mantissa >> 8) & 0xFF ;
+	out [7] = mantissa & 0xFF ;
+
+	return ;
+} /* double64_be_write */
+
+void
+double64_le_write (double in, unsigned char *out)
+{	int		exponent, mantissa ;
+
+	memset (out, 0, sizeof (double)) ;
+
+	if (fabs (in) < 1e-30)
+		return ;
+
+	if (in < 0.0)
+	{	in *= -1.0 ;
+		out [7] |= 0x80 ;
+		} ;
+
+	in = frexp (in, &exponent) ;
+
+	exponent += 1022 ;
+
+	out [7] |= (exponent >> 4) & 0x7F ;
+	out [6] |= (exponent << 4) & 0xF0 ;
+
+	in *= 0x20000000 ;
+	mantissa = psf_lrint (floor (in)) ;
+
+	out [6] |= (mantissa >> 24) & 0xF ;
+	out [5] = (mantissa >> 16) & 0xFF ;
+	out [4] = (mantissa >> 8) & 0xFF ;
+	out [3] = mantissa & 0xFF ;
+
+	in = fmod (in, 1.0) ;
+	in *= 0x1000000 ;
+	mantissa = psf_lrint (floor (in)) ;
+
+	out [2] = (mantissa >> 16) & 0xFF ;
+	out [1] = (mantissa >> 8) & 0xFF ;
+	out [0] = mantissa & 0xFF ;
+
+	return ;
+} /* double64_le_write */
+
+/*==============================================================================================
+**	Private functions.
+*/
+
+static void
+double64_peak_update	(SF_PRIVATE *psf, const double *buffer, int count, sf_count_t indx)
+{	int 	chan ;
+	int		k, position ;
+	float	fmaxval ;
+
+	for (chan = 0 ; chan < psf->sf.channels ; chan++)
+	{	fmaxval = fabs (buffer [chan]) ;
+		position = 0 ;
+		for (k = chan ; k < count ; k += psf->sf.channels)
+			if (fmaxval < fabs (buffer [k]))
+			{	fmaxval = fabs (buffer [k]) ;
+				position = k ;
+				} ;
+
+		if (fmaxval > psf->peak_info->peaks [chan].value)
+		{	psf->peak_info->peaks [chan].value = fmaxval ;
+			psf->peak_info->peaks [chan].position = psf->write_current + indx + (position / psf->sf.channels) ;
+			} ;
+		} ;
+
+	return ;
+} /* double64_peak_update */
+
+static int
+double64_get_capability	(SF_PRIVATE *psf)
+{	union
+	{	double			d ;
+		unsigned char	c [8] ;
+	} data ;
+
+	data.d = 1.234567890123456789 ; /* Some abitrary value. */
+
+	if (! psf->ieee_replace)
+	{	/* If this test is true ints and floats are compatible and little endian. */
+		if (data.c [0] == 0xfb && data.c [1] == 0x59 && data.c [2] == 0x8c && data.c [3] == 0x42 &&
+			data.c [4] == 0xca && data.c [5] == 0xc0 && data.c [6] == 0xf3 && data.c [7] == 0x3f)
+			return DOUBLE_CAN_RW_LE ;
+
+		/* If this test is true ints and floats are compatible and big endian. */
+		if (data.c [0] == 0x3f && data.c [1] == 0xf3 && data.c [2] == 0xc0 && data.c [3] == 0xca &&
+			data.c [4] == 0x42 && data.c [5] == 0x8c && data.c [6] == 0x59 && data.c [7] == 0xfb)
+			return DOUBLE_CAN_RW_BE ;
+		} ;
+
+	/* Doubles are broken. Don't expect reading or writing to be fast. */
+	psf_log_printf (psf, "Using IEEE replacement code for double.\n") ;
+
+	return (CPU_IS_LITTLE_ENDIAN) ? DOUBLE_BROKEN_LE : DOUBLE_BROKEN_BE ;
+} /* double64_get_capability */
+
+/*=======================================================================================
+*/
+
+static void
+d2s_array (const double *src, int count, short *dest, double scale)
+{	for (int i = 0 ; i < count ; i++)
+	{	dest [i] = psf_lrint (scale * src [i]) ;
+		} ;
+} /* d2s_array */
+
+static void
+d2s_clip_array (const double *src, int count, short *dest, double scale)
+{	for (int i = 0 ; i < count ; i++)
+	{	double tmp = scale * src [i] ;
+
+		if (tmp > 32767.0)
+			dest [i] = SHRT_MAX ;
+		else if (tmp < -32768.0)
+			dest [i] = SHRT_MIN ;
+		else
+			dest [i] = psf_lrint (tmp) ;
+		} ;
+} /* d2s_clip_array */
+
+static void
+d2i_array (const double *src, int count, int *dest, double scale)
+{	for (int i = 0 ; i < count ; i++)
+	{	dest [i] = psf_lrint (scale * src [i]) ;
+		} ;
+} /* d2i_array */
+
+static void
+d2i_clip_array (const double *src, int count, int *dest, double scale)
+{	for (int i = 0 ; i < count ; i++)
+	{	float tmp = scale * src [i] ;
+
+		if (CPU_CLIPS_POSITIVE == 0 && tmp > (1.0 * INT_MAX))
+			dest [i] = INT_MAX ;
+		else if (CPU_CLIPS_NEGATIVE == 0 && tmp < (-1.0 * INT_MAX))
+			dest [i] = INT_MIN ;
+		else
+			dest [i] = psf_lrint (tmp) ;
+		} ;
+} /* d2i_clip_array */
+
+static inline void
+d2f_array (const double *src, int count, float *dest)
+{	for (int i = 0 ; i < count ; i++)
+	{	dest [i] = src [i] ;
+		} ;
+} /* d2f_array */
+
+static inline void
+s2d_array (const short *src, double *dest, int count, double scale)
+{	for (int i = 0 ; i < count ; i++)
+	{	dest [i] = scale * src [i] ;
+		} ;
+} /* s2d_array */
+
+static inline void
+i2d_array (const int *src, double *dest, int count, double scale)
+{	for (int i = 0 ; i < count ; i++)
+	{	dest [i] = scale * src [i] ;
+		} ;
+} /* i2d_array */
+
+static inline void
+f2d_array (const float *src, double *dest, int count)
+{	for (int i = 0 ; i < count ; i++)
+	{	dest [i] = src [i] ;
+		} ;
+} /* f2d_array */
+
+/*----------------------------------------------------------------------------------------------
+*/
+
+static sf_count_t
+host_read_d2s	(SF_PRIVATE *psf, short *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	void		(*convert) (const double *, int, short *, double) ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+	double		scale ;
+
+	convert = (psf->add_clipping) ? d2s_clip_array : d2s_array ;
+	bufferlen = ARRAY_LEN (ubuf.dbuf) ;
+	scale = (psf->float_int_mult == 0) ? 1.0 : 0x7FFF / psf->float_max ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.dbuf, sizeof (double), bufferlen, psf) ;
+
+		if (psf->data_endswap == SF_TRUE)
+			endswap_double_array (ubuf.dbuf, readcount) ;
+
+		convert (ubuf.dbuf, readcount, ptr + total, scale) ;
+		total += readcount ;
+		len -= readcount ;
+		if (readcount < bufferlen)
+			break ;
+		} ;
+
+	return total ;
+} /* host_read_d2s */
+
+static sf_count_t
+host_read_d2i	(SF_PRIVATE *psf, int *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	void		(*convert) (const double *, int, int *, double) ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+	double		scale ;
+
+	convert = (psf->add_clipping) ? d2i_clip_array : d2i_array ;
+	bufferlen = ARRAY_LEN (ubuf.dbuf) ;
+	scale = (psf->float_int_mult == 0) ? 1.0 : 2147483648.0f / psf->float_max ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.dbuf, sizeof (double), bufferlen, psf) ;
+
+		if (psf->data_endswap == SF_TRUE)
+			endswap_double_array (ubuf.dbuf, bufferlen) ;
+
+		convert (ubuf.dbuf, readcount, ptr + total, scale) ;
+		total += readcount ;
+		len -= readcount ;
+		if (readcount < bufferlen)
+			break ;
+		} ;
+
+	return total ;
+} /* host_read_d2i */
+
+static sf_count_t
+host_read_d2f	(SF_PRIVATE *psf, float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.dbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.dbuf, sizeof (double), bufferlen, psf) ;
+
+		if (psf->data_endswap == SF_TRUE)
+			endswap_double_array (ubuf.dbuf, readcount) ;
+
+		d2f_array (ubuf.dbuf, readcount, ptr + total) ;
+		total += readcount ;
+		len -= readcount ;
+		if (readcount < bufferlen)
+			break ;
+		} ;
+
+	return total ;
+} /* host_read_d2f */
+
+static sf_count_t
+host_read_d	(SF_PRIVATE *psf, double *ptr, sf_count_t len)
+{	int			bufferlen ;
+	sf_count_t	readcount, total = 0 ;
+
+	readcount = psf_fread (ptr, sizeof (double), len, psf) ;
+
+	if (psf->data_endswap != SF_TRUE)
+		return readcount ;
+
+	/* If the read length was sensible, endswap output in one go. */
+	if (readcount < SENSIBLE_LEN)
+	{	endswap_double_array (ptr, readcount) ;
+		return readcount ;
+		} ;
+
+	bufferlen = SENSIBLE_LEN ;
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+
+		endswap_double_array (ptr + total, bufferlen) ;
+
+		total += bufferlen ;
+		len -= bufferlen ;
+		} ;
+
+	return total ;
+} /* host_read_d */
+
+static sf_count_t
+host_write_s2d	(SF_PRIVATE *psf, const short *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+	double		scale ;
+
+	scale = (psf->scale_int_float == 0) ? 1.0 : 1.0 / 0x8000 ;
+	bufferlen = ARRAY_LEN (ubuf.dbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+
+		s2d_array (ptr + total, ubuf.dbuf, bufferlen, scale) ;
+
+		if (psf->peak_info)
+			double64_peak_update (psf, ubuf.dbuf, bufferlen, total / psf->sf.channels) ;
+
+		if (psf->data_endswap == SF_TRUE)
+			endswap_double_array (ubuf.dbuf, bufferlen) ;
+
+		writecount = (int) psf_fwrite (ubuf.dbuf, sizeof (double), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* host_write_s2d */
+
+static sf_count_t
+host_write_i2d	(SF_PRIVATE *psf, const int *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+	double		scale ;
+
+	scale = (psf->scale_int_float == 0) ? 1.0 : 1.0 / (8.0 * 0x10000000) ;
+	bufferlen = ARRAY_LEN (ubuf.dbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		i2d_array (ptr + total, ubuf.dbuf, bufferlen, scale) ;
+
+		if (psf->peak_info)
+			double64_peak_update (psf, ubuf.dbuf, bufferlen, total / psf->sf.channels) ;
+
+		if (psf->data_endswap == SF_TRUE)
+			endswap_double_array (ubuf.dbuf, bufferlen) ;
+
+		writecount = (int) psf_fwrite (ubuf.dbuf, sizeof (double), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* host_write_i2d */
+
+static sf_count_t
+host_write_f2d	(SF_PRIVATE *psf, const float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.dbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		f2d_array (ptr + total, ubuf.dbuf, bufferlen) ;
+
+		if (psf->peak_info)
+			double64_peak_update (psf, ubuf.dbuf, bufferlen, total / psf->sf.channels) ;
+
+		if (psf->data_endswap == SF_TRUE)
+			endswap_double_array (ubuf.dbuf, bufferlen) ;
+
+		writecount = (int) psf_fwrite (ubuf.dbuf, sizeof (double), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* host_write_f2d */
+
+static sf_count_t
+host_write_d	(SF_PRIVATE *psf, const double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	if (psf->peak_info)
+		double64_peak_update (psf, ptr, len, 0) ;
+
+	if (psf->data_endswap != SF_TRUE)
+		return psf_fwrite (ptr, sizeof (double), len, psf) ;
+
+	bufferlen = ARRAY_LEN (ubuf.dbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+
+		endswap_double_copy (ubuf.dbuf, ptr + total, bufferlen) ;
+
+		writecount = (int) psf_fwrite (ubuf.dbuf, sizeof (double), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* host_write_d */
+
+/*=======================================================================================
+*/
+
+static sf_count_t
+replace_read_d2s	(SF_PRIVATE *psf, short *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+	double		scale ;
+
+	bufferlen = ARRAY_LEN (ubuf.dbuf) ;
+	scale = (psf->float_int_mult == 0) ? 1.0 : 0x7FFF / psf->float_max ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.dbuf, sizeof (double), bufferlen, psf) ;
+
+		if (psf->data_endswap == SF_TRUE)
+			endswap_double_array (ubuf.dbuf, bufferlen) ;
+
+		d2bd_read (ubuf.dbuf, bufferlen) ;
+
+		d2s_array (ubuf.dbuf, readcount, ptr + total, scale) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* replace_read_d2s */
+
+static sf_count_t
+replace_read_d2i	(SF_PRIVATE *psf, int *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+	double		scale ;
+
+	bufferlen = ARRAY_LEN (ubuf.dbuf) ;
+	scale = (psf->float_int_mult == 0) ? 1.0 : 2147483648.0f / psf->float_max ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.dbuf, sizeof (double), bufferlen, psf) ;
+
+		if (psf->data_endswap == SF_TRUE)
+			endswap_double_array (ubuf.dbuf, bufferlen) ;
+
+		d2bd_read (ubuf.dbuf, bufferlen) ;
+
+		d2i_array (ubuf.dbuf, readcount, ptr + total, scale) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* replace_read_d2i */
+
+static sf_count_t
+replace_read_d2f	(SF_PRIVATE *psf, float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.dbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.dbuf, sizeof (double), bufferlen, psf) ;
+
+		if (psf->data_endswap == SF_TRUE)
+			endswap_double_array (ubuf.dbuf, bufferlen) ;
+
+		d2bd_read (ubuf.dbuf, bufferlen) ;
+
+		memcpy (ptr + total, ubuf.dbuf, bufferlen * sizeof (double)) ;
+
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* replace_read_d2f */
+
+static sf_count_t
+replace_read_d	(SF_PRIVATE *psf, double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+
+	/* FIXME : This is probably nowhere near optimal. */
+	bufferlen = ARRAY_LEN (ubuf.dbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.dbuf, sizeof (double), bufferlen, psf) ;
+
+		if (psf->data_endswap == SF_TRUE)
+			endswap_double_array (ubuf.dbuf, readcount) ;
+
+		d2bd_read (ubuf.dbuf, readcount) ;
+
+		memcpy (ptr + total, ubuf.dbuf, readcount * sizeof (double)) ;
+
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* replace_read_d */
+
+static sf_count_t
+replace_write_s2d	(SF_PRIVATE *psf, const short *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+	double		scale ;
+
+	scale = (psf->scale_int_float == 0) ? 1.0 : 1.0 / 0x8000 ;
+	bufferlen = ARRAY_LEN (ubuf.dbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		s2d_array (ptr + total, ubuf.dbuf, bufferlen, scale) ;
+
+		if (psf->peak_info)
+			double64_peak_update (psf, ubuf.dbuf, bufferlen, total / psf->sf.channels) ;
+
+		bd2d_write (ubuf.dbuf, bufferlen) ;
+
+		if (psf->data_endswap == SF_TRUE)
+			endswap_double_array (ubuf.dbuf, bufferlen) ;
+
+		writecount = (int) psf_fwrite (ubuf.dbuf, sizeof (double), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* replace_write_s2d */
+
+static sf_count_t
+replace_write_i2d	(SF_PRIVATE *psf, const int *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+	double		scale ;
+
+	scale = (psf->scale_int_float == 0) ? 1.0 : 1.0 / (8.0 * 0x10000000) ;
+	bufferlen = ARRAY_LEN (ubuf.dbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		i2d_array (ptr + total, ubuf.dbuf, bufferlen, scale) ;
+
+		if (psf->peak_info)
+			double64_peak_update (psf, ubuf.dbuf, bufferlen, total / psf->sf.channels) ;
+
+		bd2d_write (ubuf.dbuf, bufferlen) ;
+
+		if (psf->data_endswap == SF_TRUE)
+			endswap_double_array (ubuf.dbuf, bufferlen) ;
+
+		writecount = (int) psf_fwrite (ubuf.dbuf, sizeof (double), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* replace_write_i2d */
+
+static sf_count_t
+replace_write_f2d	(SF_PRIVATE *psf, const float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.dbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		f2d_array (ptr + total, ubuf.dbuf, bufferlen) ;
+
+		bd2d_write (ubuf.dbuf, bufferlen) ;
+
+		if (psf->data_endswap == SF_TRUE)
+			endswap_double_array (ubuf.dbuf, bufferlen) ;
+
+		writecount = (int) psf_fwrite (ubuf.dbuf, sizeof (double), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* replace_write_f2d */
+
+static sf_count_t
+replace_write_d	(SF_PRIVATE *psf, const double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	/* FIXME : This is probably nowhere near optimal. */
+	if (psf->peak_info)
+		double64_peak_update (psf, ptr, len, 0) ;
+
+	bufferlen = ARRAY_LEN (ubuf.dbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+
+		memcpy (ubuf.dbuf, ptr + total, bufferlen * sizeof (double)) ;
+
+		bd2d_write (ubuf.dbuf, bufferlen) ;
+
+		if (psf->data_endswap == SF_TRUE)
+			endswap_double_array (ubuf.dbuf, bufferlen) ;
+
+		writecount = (int) psf_fwrite (ubuf.dbuf, sizeof (double), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* replace_write_d */
+
+/*----------------------------------------------------------------------------------------------
+*/
+
+static void
+d2bd_read (double *buffer, int count)
+{	for (int i = 0 ; i < count ; i++)
+	{	buffer [i] = DOUBLE64_READ ((unsigned char *) &buffer [i]) ;
+		} ;
+} /* d2bd_read */
+
+static void
+bd2d_write (double *buffer, int count)
+{	for (int i = 0 ; i < count ; i++)
+	{	DOUBLE64_WRITE (buffer [i], (unsigned char*) &buffer [i]) ;
+		} ;
+} /* bd2d_write */
+
diff --git a/src/dwd.c b/src/dwd.c
new file mode 100644
index 0000000..a15750d
--- /dev/null
+++ b/src/dwd.c
@@ -0,0 +1,201 @@
+/*
+** Copyright (C) 2002-2012 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<fcntl.h>
+#include	<string.h>
+#include	<ctype.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+#if (ENABLE_EXPERIMENTAL_CODE == 0)
+
+int
+dwd_open	(SF_PRIVATE *psf)
+{	if (psf)
+		return SFE_UNIMPLEMENTED ;
+	return 0 ;
+} /* dwd_open */
+
+#else
+
+/*------------------------------------------------------------------------------
+** Macros to handle big/little endian issues.
+*/
+
+#define SFE_DWD_NO_DWD			1666
+#define SFE_DWD_BAND_BIT_WIDTH 	1667
+#define SFE_DWD_COMPRESSION		1668
+
+#define	DWD_IDENTIFIER		"DiamondWare Digitized\n\0\x1a"
+#define	DWD_IDENTIFIER_LEN	24
+
+#define	DWD_HEADER_LEN		57
+
+/*------------------------------------------------------------------------------
+** Typedefs.
+*/
+
+/*------------------------------------------------------------------------------
+** Private static functions.
+*/
+
+static int	dwd_read_header (SF_PRIVATE *psf) ;
+
+static int	dwd_close		(SF_PRIVATE *psf) ;
+
+/*------------------------------------------------------------------------------
+** Public function.
+*/
+
+int
+dwd_open (SF_PRIVATE *psf)
+{	int	error = 0 ;
+
+	if (psf->file.mode == SFM_READ || (psf->file.mode == SFM_RDWR && psf->filelength > 0))
+	{	if ((error = dwd_read_header (psf)))
+			return error ;
+		} ;
+
+	if ((SF_CONTAINER (psf->sf.format)) != SF_FORMAT_DWD)
+		return	SFE_BAD_OPEN_FORMAT ;
+
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{
+		/*-psf->endian = SF_ENDIAN (psf->sf.format) ;
+		if (CPU_IS_LITTLE_ENDIAN && psf->endian == SF_ENDIAN_CPU)
+			psf->endian = SF_ENDIAN_LITTLE ;
+		else if (psf->endian != SF_ENDIAN_LITTLE)
+			psf->endian = SF_ENDIAN_BIG ;
+
+		if (! (encoding = dwd_write_header (psf, SF_FALSE)))
+			return psf->error ;
+
+		psf->write_header = dwd_write_header ;
+		-*/
+		} ;
+
+	psf->container_close = dwd_close ;
+
+	/*-psf->blockwidth = psf->bytewidth * psf->sf.channels ;-*/
+
+	return error ;
+} /* dwd_open */
+
+/*------------------------------------------------------------------------------
+*/
+
+static int
+dwd_close	(SF_PRIVATE * UNUSED (psf))
+{
+	return 0 ;
+} /* dwd_close */
+
+/* This struct contains all the fields of interest om the DWD header, but does not
+** do so in the same order and layout as the actual file, header.
+** No assumptions are made about the packing of this struct.
+*/
+typedef struct
+{	uint8_t major, minor, compression, channels, bitwidth ;
+	uint16_t srate, maxval ;
+	uint32_t id, datalen, frames, offset ;
+} DWD_HEADER ;
+
+static int
+dwd_read_header (SF_PRIVATE *psf)
+{	BUF_UNION	ubuf ;
+	DWD_HEADER	dwdh ;
+
+	memset (ubuf.cbuf, 0, sizeof (ubuf.cbuf)) ;
+	/* Set position to start of file to begin reading header. */
+	psf_binheader_readf (psf, "pb", 0, ubuf.cbuf, DWD_IDENTIFIER_LEN) ;
+
+	if (memcmp (ubuf.cbuf, DWD_IDENTIFIER, DWD_IDENTIFIER_LEN) != 0)
+		return SFE_DWD_NO_DWD ;
+
+	psf_log_printf (psf, "Read only : DiamondWare Digitized (.dwd)\n", ubuf.cbuf) ;
+
+	psf_binheader_readf (psf, "11", &dwdh.major, &dwdh.minor) ;
+	psf_binheader_readf (psf, "e4j1", &dwdh.id, 1, &dwdh.compression) ;
+	psf_binheader_readf (psf, "e211", &dwdh.srate, &dwdh.channels, &dwdh.bitwidth) ;
+	psf_binheader_readf (psf, "e24", &dwdh.maxval, &dwdh.datalen) ;
+	psf_binheader_readf (psf, "e44", &dwdh.frames, &dwdh.offset) ;
+
+	psf_log_printf (psf, "  Version Major : %d\n  Version Minor : %d\n  Unique ID     : %08X\n",
+						dwdh.major, dwdh.minor, dwdh.id) ;
+	psf_log_printf (psf, "  Compression   : %d => ", dwdh.compression) ;
+
+	if (dwdh.compression != 0)
+	{	psf_log_printf (psf, "Unsupported compression\n") ;
+		return SFE_DWD_COMPRESSION ;
+		}
+	else
+		psf_log_printf (psf, "None\n") ;
+
+	psf_log_printf (psf,	"  Sample Rate   : %d\n  Channels      : %d\n"
+							"  Bit Width     : %d\n",
+						dwdh.srate, dwdh.channels, dwdh.bitwidth) ;
+
+	switch (dwdh.bitwidth)
+	{	case 8 :
+				psf->sf.format = SF_FORMAT_DWD | SF_FORMAT_PCM_S8 ;
+				psf->bytewidth = 1 ;
+				break ;
+
+		case 16 :
+				psf->sf.format = SF_FORMAT_DWD | SF_FORMAT_PCM_16 ;
+				psf->bytewidth = 2 ;
+				break ;
+
+		default :
+				psf_log_printf (psf, "*** Bad bit width %d\n", dwdh.bitwidth) ;
+				return SFE_DWD_BAND_BIT_WIDTH ;
+				} ;
+
+	if (psf->filelength != dwdh.offset + dwdh.datalen)
+	{	psf_log_printf (psf, "  Data Length   : %d (should be %D)\n", dwdh.datalen, psf->filelength - dwdh.offset) ;
+		dwdh.datalen = (uint32_t) (psf->filelength - dwdh.offset) ;
+		}
+	else
+		psf_log_printf (psf, "  Data Length   : %d\n", dwdh.datalen) ;
+
+	psf_log_printf (psf, "  Max Value     : %d\n", dwdh.maxval) ;
+	psf_log_printf (psf, "  Frames        : %d\n", dwdh.frames) ;
+	psf_log_printf (psf, "  Data Offset   : %d\n", dwdh.offset) ;
+
+	psf->datalength = dwdh.datalen ;
+	psf->dataoffset = dwdh.offset ;
+
+	psf->endian = SF_ENDIAN_LITTLE ;
+
+	psf->sf.samplerate = dwdh.srate ;
+	psf->sf.channels = dwdh.channels ;
+	psf->sf.sections = 1 ;
+
+	return pcm_init (psf) ;
+} /* dwd_read_header */
+
+/*------------------------------------------------------------------------------
+*/
+
+#endif
+
diff --git a/src/dwvw.c b/src/dwvw.c
new file mode 100644
index 0000000..a015fce
--- /dev/null
+++ b/src/dwvw.c
@@ -0,0 +1,674 @@
+/*
+** Copyright (C) 2002-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*===========================================================================
+** Delta Word Variable Width
+**
+** This decoder and encoder were implemented using information found in this
+** document : http://home.swbell.net/rubywand/R011SNDFMTS.TXT
+**
+** According to the document, the algorithm "was invented 1991 by Magnus
+** Lidstrom and is copyright 1993 by NuEdge Development".
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<stdlib.h>
+#include	<string.h>
+#include	<math.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+typedef struct
+{	int		bit_width, dwm_maxsize, max_delta, span ;
+	int		samplecount ;
+	int		bit_count, bits, last_delta_width, last_sample ;
+	struct
+	{	int				index, end ;
+		unsigned char	buffer [256] ;
+	} b ;
+} DWVW_PRIVATE ;
+
+/*============================================================================================
+*/
+
+static sf_count_t dwvw_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
+static sf_count_t dwvw_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
+static sf_count_t dwvw_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
+static sf_count_t dwvw_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
+
+static sf_count_t dwvw_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
+static sf_count_t dwvw_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
+static sf_count_t dwvw_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
+static sf_count_t dwvw_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
+
+static sf_count_t	dwvw_seek	(SF_PRIVATE *psf, int mode, sf_count_t offset) ;
+static int	dwvw_close		(SF_PRIVATE *psf) ;
+static int	dwvw_byterate	(SF_PRIVATE *psf) ;
+
+static int	dwvw_decode_data (SF_PRIVATE *psf, DWVW_PRIVATE *pdwvw, int *ptr, int len) ;
+static int	dwvw_decode_load_bits (SF_PRIVATE *psf, DWVW_PRIVATE *pdwvw, int bit_count) ;
+
+static int	dwvw_encode_data (SF_PRIVATE *psf, DWVW_PRIVATE *pdwvw, const int *ptr, int len) ;
+static void dwvw_encode_store_bits (SF_PRIVATE *psf, DWVW_PRIVATE *pdwvw, int data, int new_bits) ;
+static void dwvw_read_reset (DWVW_PRIVATE *pdwvw) ;
+
+/*============================================================================================
+** DWVW initialisation function.
+*/
+
+int
+dwvw_init (SF_PRIVATE *psf, int bitwidth)
+{	DWVW_PRIVATE	*pdwvw ;
+
+	if (psf->codec_data != NULL)
+	{	psf_log_printf (psf, "*** psf->codec_data is not NULL.\n") ;
+		return SFE_INTERNAL ;
+		} ;
+
+	if (bitwidth > 24)
+		return SFE_DWVW_BAD_BITWIDTH ;
+
+	if (psf->file.mode == SFM_RDWR)
+		return SFE_BAD_MODE_RW ;
+
+	if ((pdwvw = calloc (1, sizeof (DWVW_PRIVATE))) == NULL)
+		return SFE_MALLOC_FAILED ;
+
+	psf->codec_data = (void*) pdwvw ;
+	pdwvw->bit_width 	= bitwidth ;
+	dwvw_read_reset (pdwvw) ;
+
+	if (psf->file.mode == SFM_READ)
+	{	psf->read_short		= dwvw_read_s ;
+		psf->read_int		= dwvw_read_i ;
+		psf->read_float		= dwvw_read_f ;
+		psf->read_double	= dwvw_read_d ;
+		} ;
+
+	if (psf->file.mode == SFM_WRITE)
+	{	psf->write_short	= dwvw_write_s ;
+		psf->write_int		= dwvw_write_i ;
+		psf->write_float	= dwvw_write_f ;
+		psf->write_double	= dwvw_write_d ;
+		} ;
+
+	psf->codec_close = dwvw_close ;
+	psf->seek = dwvw_seek ;
+	psf->byterate = dwvw_byterate ;
+
+	if (psf->file.mode == SFM_READ)
+	{	psf->sf.frames = psf_decode_frame_count (psf) ;
+		dwvw_read_reset (pdwvw) ;
+		} ;
+
+	return 0 ;
+} /* dwvw_init */
+
+/*--------------------------------------------------------------------------------------------
+*/
+
+static int
+dwvw_close (SF_PRIVATE *psf)
+{	DWVW_PRIVATE *pdwvw ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	pdwvw = (DWVW_PRIVATE*) psf->codec_data ;
+
+	if (psf->file.mode == SFM_WRITE)
+	{	static int last_values [12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } ;
+
+		/* Write 8 zero samples to fully flush output. */
+		dwvw_encode_data (psf, pdwvw, last_values, 12) ;
+
+		/* Write the last buffer worth of data to disk. */
+		psf_fwrite (pdwvw->b.buffer, 1, pdwvw->b.index, psf) ;
+
+		if (psf->write_header)
+			psf->write_header (psf, SF_TRUE) ;
+		} ;
+
+	return 0 ;
+} /* dwvw_close */
+
+static sf_count_t
+dwvw_seek	(SF_PRIVATE *psf, int UNUSED (mode), sf_count_t offset)
+{	DWVW_PRIVATE *pdwvw ;
+
+	if (! psf->codec_data)
+	{	psf->error = SFE_INTERNAL ;
+		return PSF_SEEK_ERROR ;
+		} ;
+
+	pdwvw = (DWVW_PRIVATE*) psf->codec_data ;
+
+	if (offset == 0)
+	{	psf_fseek (psf, psf->dataoffset, SEEK_SET) ;
+		dwvw_read_reset (pdwvw) ;
+		return 0 ;
+		} ;
+
+	psf->error = SFE_BAD_SEEK ;
+	return	PSF_SEEK_ERROR ;
+} /* dwvw_seek */
+
+static int
+dwvw_byterate	(SF_PRIVATE *psf)
+{
+	if (psf->file.mode == SFM_READ)
+		return (psf->datalength * psf->sf.samplerate) / psf->sf.frames ;
+
+	return -1 ;
+} /* dwvw_byterate */
+
+/*==============================================================================
+*/
+
+static sf_count_t
+dwvw_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len)
+{	DWVW_PRIVATE *pdwvw ;
+	BUF_UNION	ubuf ;
+	int		*iptr ;
+	int		k, bufferlen, readcount = 0, count ;
+	sf_count_t	total = 0 ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	pdwvw = (DWVW_PRIVATE*) psf->codec_data ;
+
+	iptr = ubuf.ibuf ;
+	bufferlen = ARRAY_LEN (ubuf.ibuf) ;
+	while (len > 0)
+	{	readcount = (len >= bufferlen) ? bufferlen : (int) len ;
+		count = dwvw_decode_data (psf, pdwvw, iptr, readcount) ;
+		for (k = 0 ; k < readcount ; k++)
+			ptr [total + k] = iptr [k] >> 16 ;
+
+		total += count ;
+		len -= readcount ;
+		if (count != readcount)
+			break ;
+		} ;
+
+	return total ;
+} /* dwvw_read_s */
+
+static sf_count_t
+dwvw_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len)
+{	DWVW_PRIVATE *pdwvw ;
+	int			readcount, count ;
+	sf_count_t	total = 0 ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	pdwvw = (DWVW_PRIVATE*) psf->codec_data ;
+
+	while (len > 0)
+	{	readcount = (len > 0x10000000) ? 0x10000000 : (int) len ;
+
+		count = dwvw_decode_data (psf, pdwvw, ptr, readcount) ;
+
+		total += count ;
+		len -= count ;
+
+		if (count != readcount)
+			break ;
+		} ;
+
+	return total ;
+} /* dwvw_read_i */
+
+static sf_count_t
+dwvw_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len)
+{	DWVW_PRIVATE *pdwvw ;
+	BUF_UNION	ubuf ;
+	int			*iptr ;
+	int			k, bufferlen, readcount = 0, count ;
+	sf_count_t	total = 0 ;
+	float	normfact ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	pdwvw = (DWVW_PRIVATE*) psf->codec_data ;
+
+	normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x80000000) : 1.0 ;
+
+	iptr = ubuf.ibuf ;
+	bufferlen = ARRAY_LEN (ubuf.ibuf) ;
+	while (len > 0)
+	{	readcount = (len >= bufferlen) ? bufferlen : (int) len ;
+		count = dwvw_decode_data (psf, pdwvw, iptr, readcount) ;
+		for (k = 0 ; k < readcount ; k++)
+			ptr [total + k] = normfact * (float) (iptr [k]) ;
+
+		total += count ;
+		len -= readcount ;
+		if (count != readcount)
+			break ;
+		} ;
+
+	return total ;
+} /* dwvw_read_f */
+
+static sf_count_t
+dwvw_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len)
+{	DWVW_PRIVATE *pdwvw ;
+	BUF_UNION	ubuf ;
+	int			*iptr ;
+	int			k, bufferlen, readcount = 0, count ;
+	sf_count_t	total = 0 ;
+	double 	normfact ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	pdwvw = (DWVW_PRIVATE*) psf->codec_data ;
+
+	normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((double) 0x80000000) : 1.0 ;
+
+	iptr = ubuf.ibuf ;
+	bufferlen = ARRAY_LEN (ubuf.ibuf) ;
+	while (len > 0)
+	{	readcount = (len >= bufferlen) ? bufferlen : (int) len ;
+		count = dwvw_decode_data (psf, pdwvw, iptr, readcount) ;
+		for (k = 0 ; k < readcount ; k++)
+			ptr [total + k] = normfact * (double) (iptr [k]) ;
+
+		total += count ;
+		len -= readcount ;
+		if (count != readcount)
+			break ;
+		} ;
+
+	return total ;
+} /* dwvw_read_d */
+
+static int
+dwvw_decode_data (SF_PRIVATE *psf, DWVW_PRIVATE *pdwvw, int *ptr, int len)
+{	int	count ;
+	int delta_width_modifier, delta_width, delta_negative, delta, sample ;
+
+	/* Restore state from last decode call. */
+	delta_width = pdwvw->last_delta_width ;
+	sample = pdwvw->last_sample ;
+
+	for (count = 0 ; count < len ; count++)
+	{	/* If bit_count parameter is zero get the delta_width_modifier. */
+		delta_width_modifier = dwvw_decode_load_bits (psf, pdwvw, -1) ;
+
+		/* Check for end of input bit stream. Break loop if end. */
+		if (delta_width_modifier < 0 || (pdwvw->b.end == 0 && count == 0))
+			break ;
+
+		if (delta_width_modifier && dwvw_decode_load_bits (psf, pdwvw, 1))
+			delta_width_modifier = - delta_width_modifier ;
+
+		/* Calculate the current word width. */
+		delta_width = (delta_width + delta_width_modifier + pdwvw->bit_width) % pdwvw->bit_width ;
+
+		/* Load the delta. */
+		delta = 0 ;
+		if (delta_width)
+		{	delta = dwvw_decode_load_bits (psf, pdwvw, delta_width - 1) | (1 << (delta_width - 1)) ;
+			delta_negative = dwvw_decode_load_bits (psf, pdwvw, 1) ;
+			if (delta == pdwvw->max_delta - 1)
+				delta += dwvw_decode_load_bits (psf, pdwvw, 1) ;
+			if (delta_negative)
+				delta = -delta ;
+			} ;
+
+		/* Calculate the sample */
+		sample += delta ;
+
+		if (sample >= pdwvw->max_delta)
+			sample -= pdwvw->span ;
+		else if (sample < - pdwvw->max_delta)
+			sample += pdwvw->span ;
+
+		/* Store the sample justifying to the most significant bit. */
+		ptr [count] = arith_shift_left (sample, 32 - pdwvw->bit_width) ;
+
+		if (pdwvw->b.end == 0 && pdwvw->bit_count == 0)
+			break ;
+		} ;
+
+	pdwvw->last_delta_width = delta_width ;
+	pdwvw->last_sample = sample ;
+
+	pdwvw->samplecount += count ;
+
+	return count ;
+} /* dwvw_decode_data */
+
+static int
+dwvw_decode_load_bits (SF_PRIVATE *psf, DWVW_PRIVATE *pdwvw, int bit_count)
+{	int output = 0, get_dwm = SF_FALSE ;
+
+	/*
+	**	Depending on the value of parameter bit_count, either get the
+	**	required number of bits (ie bit_count > 0) or the
+	**	delta_width_modifier (otherwise).
+	*/
+
+	if (bit_count < 0)
+	{	get_dwm = SF_TRUE ;
+		/* modify bit_count to ensure we have enought bits for finding dwm. */
+		bit_count = pdwvw->dwm_maxsize ;
+		} ;
+
+	/* Load bits in bit reseviour. */
+	while (pdwvw->bit_count < bit_count)
+	{	if (pdwvw->b.index >= pdwvw->b.end)
+		{	pdwvw->b.end = (int) psf_fread (pdwvw->b.buffer, 1, sizeof (pdwvw->b.buffer), psf) ;
+			pdwvw->b.index = 0 ;
+			} ;
+
+		/* Check for end of input stream. */
+		if (bit_count < 8 && pdwvw->b.end == 0)
+			return -1 ;
+
+		pdwvw->bits = arith_shift_left (pdwvw->bits, 8) ;
+
+		if (pdwvw->b.index < pdwvw->b.end)
+		{	pdwvw->bits |= pdwvw->b.buffer [pdwvw->b.index] ;
+			pdwvw->b.index ++ ;
+			} ;
+		pdwvw->bit_count += 8 ;
+		} ;
+
+	/* If asked to get bits do so. */
+	if (! get_dwm)
+	{	output = (pdwvw->bits >> (pdwvw->bit_count - bit_count)) & ((1 << bit_count) - 1) ;
+		pdwvw->bit_count -= bit_count ;
+		return output ;
+		} ;
+
+	/* Otherwise must have been asked to get delta_width_modifier. */
+	while (output < (pdwvw->dwm_maxsize))
+	{	pdwvw->bit_count -= 1 ;
+		if (pdwvw->bits & (1 << pdwvw->bit_count))
+			break ;
+		output += 1 ;
+		} ;
+
+	return output ;
+} /* dwvw_decode_load_bits */
+
+static void
+dwvw_read_reset (DWVW_PRIVATE *pdwvw)
+{	int bitwidth = pdwvw->bit_width ;
+
+	memset (pdwvw, 0, sizeof (DWVW_PRIVATE)) ;
+
+	pdwvw->bit_width	= bitwidth ;
+	pdwvw->dwm_maxsize	= bitwidth / 2 ;
+	pdwvw->max_delta	= 1 << (bitwidth - 1) ;
+	pdwvw->span			= 1 << bitwidth ;
+} /* dwvw_read_reset */
+
+static void
+dwvw_encode_store_bits (SF_PRIVATE *psf, DWVW_PRIVATE *pdwvw, int data, int new_bits)
+{	int 	byte ;
+
+	/* Shift the bits into the resevoir. */
+	pdwvw->bits = arith_shift_left (pdwvw->bits, new_bits) | (data & (arith_shift_left (1, new_bits) - 1)) ;
+	pdwvw->bit_count += new_bits ;
+
+	/* Transfer bit to buffer. */
+	while (pdwvw->bit_count >= 8)
+	{	byte = pdwvw->bits >> (pdwvw->bit_count - 	8) ;
+		pdwvw->bit_count -= 8 ;
+		pdwvw->b.buffer [pdwvw->b.index] = byte & 0xFF ;
+		pdwvw->b.index ++ ;
+		} ;
+
+	if (pdwvw->b.index > SIGNED_SIZEOF (pdwvw->b.buffer) - 4)
+	{	psf_fwrite (pdwvw->b.buffer, 1, pdwvw->b.index, psf) ;
+		pdwvw->b.index = 0 ;
+		} ;
+
+	return ;
+} /* dwvw_encode_store_bits */
+
+#if 0
+/* Debigging routine. */
+static void
+dump_bits (DWVW_PRIVATE *pdwvw)
+{	int k, mask ;
+
+	for (k = 0 ; k < 10 && k < pdwvw->b.index ; k++)
+	{	mask = 0x80 ;
+		while (mask)
+		{	putchar (mask & pdwvw->b.buffer [k] ? '1' : '0') ;
+			mask >>= 1 ;
+			} ;
+		putchar (' ') ;
+		}
+
+	for (k = pdwvw->bit_count - 1 ; k >= 0 ; k --)
+		putchar (pdwvw->bits & (1 << k) ? '1' : '0') ;
+
+	putchar ('\n') ;
+} /* dump_bits */
+#endif
+
+#define HIGHEST_BIT(x, count)		\
+			{	int y = x ;			\
+				(count) = 0 ;		\
+				while (y)			\
+				{	(count) ++ ;	\
+					y >>= 1 ;		\
+					} ;				\
+				} ;
+
+static int
+dwvw_encode_data (SF_PRIVATE *psf, DWVW_PRIVATE *pdwvw, const int *ptr, int len)
+{	int	count ;
+	int delta_width_modifier, delta, delta_negative, delta_width, extra_bit ;
+
+	for (count = 0 ; count < len ; count++)
+	{	delta = (ptr [count] >> (32 - pdwvw->bit_width)) - pdwvw->last_sample ;
+
+		/* Calculate extra_bit if needed. */
+		extra_bit = -1 ;
+		delta_negative = 0 ;
+		if (delta < -pdwvw->max_delta)
+			delta = pdwvw->max_delta + (delta % pdwvw->max_delta) ;
+		else if (delta == -pdwvw->max_delta)
+		{	extra_bit = 1 ;
+			delta_negative = 1 ;
+			delta = pdwvw->max_delta - 1 ;
+			}
+		else if (delta > pdwvw->max_delta)
+		{	delta_negative = 1 ;
+			delta = pdwvw->span - delta ;
+			delta = abs (delta) ;
+			}
+		else if (delta == pdwvw->max_delta)
+		{	extra_bit = 1 ;
+			delta = pdwvw->max_delta - 1 ;
+			}
+		else if (delta < 0)
+		{	delta_negative = 1 ;
+			delta = abs (delta) ;
+			} ;
+
+		if (delta == pdwvw->max_delta - 1 && extra_bit == -1)
+			extra_bit = 0 ;
+
+		/* Find width in bits of delta */
+		HIGHEST_BIT (delta, delta_width) ;
+
+		/* Calculate the delta_width_modifier */
+		delta_width_modifier = (delta_width - pdwvw->last_delta_width) % pdwvw->bit_width ;
+		if (delta_width_modifier > pdwvw->dwm_maxsize)
+			delta_width_modifier -= pdwvw->bit_width ;
+		if (delta_width_modifier < -pdwvw->dwm_maxsize)
+			delta_width_modifier += pdwvw->bit_width ;
+
+		/* Write delta_width_modifier zeros, followed by terminating '1'. */
+		dwvw_encode_store_bits (psf, pdwvw, 0, abs (delta_width_modifier)) ;
+		if (abs (delta_width_modifier) != pdwvw->dwm_maxsize)
+			dwvw_encode_store_bits (psf, pdwvw, 1, 1) ;
+
+		/*  Write delta_width_modifier sign. */
+		if (delta_width_modifier < 0)
+			dwvw_encode_store_bits (psf, pdwvw, 1, 1) ;
+		if (delta_width_modifier > 0)
+			dwvw_encode_store_bits (psf, pdwvw, 0, 1) ;
+
+		/* Write delta and delta sign bit. */
+		if (delta_width)
+		{	dwvw_encode_store_bits (psf, pdwvw, delta, abs (delta_width) - 1) ;
+			dwvw_encode_store_bits (psf, pdwvw, (delta_negative ? 1 : 0), 1) ;
+			} ;
+
+		/* Write extra bit!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+		if (extra_bit >= 0)
+			dwvw_encode_store_bits (psf, pdwvw, extra_bit, 1) ;
+
+		pdwvw->last_sample = ptr [count] >> (32 - pdwvw->bit_width) ;
+		pdwvw->last_delta_width = delta_width ;
+		} ;
+
+	pdwvw->samplecount += count ;
+
+	return count ;
+} /* dwvw_encode_data */
+
+static sf_count_t
+dwvw_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len)
+{	DWVW_PRIVATE *pdwvw ;
+	BUF_UNION	ubuf ;
+	int		*iptr ;
+	int		k, bufferlen, writecount = 0, count ;
+	sf_count_t	total = 0 ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	pdwvw = (DWVW_PRIVATE*) psf->codec_data ;
+
+	iptr = ubuf.ibuf ;
+	bufferlen = ARRAY_LEN (ubuf.ibuf) ;
+	while (len > 0)
+	{	writecount = (len >= bufferlen) ? bufferlen : (int) len ;
+		for (k = 0 ; k < writecount ; k++)
+			iptr [k] = arith_shift_left (ptr [total + k], 16) ;
+		count = dwvw_encode_data (psf, pdwvw, iptr, writecount) ;
+
+		total += count ;
+		len -= writecount ;
+		if (count != writecount)
+			break ;
+		} ;
+
+	return total ;
+} /* dwvw_write_s */
+
+static sf_count_t
+dwvw_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len)
+{	DWVW_PRIVATE *pdwvw ;
+	int			writecount, count ;
+	sf_count_t	total = 0 ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	pdwvw = (DWVW_PRIVATE*) psf->codec_data ;
+
+	while (len > 0)
+	{	writecount = (len > 0x10000000) ? 0x10000000 : (int) len ;
+
+		count = dwvw_encode_data (psf, pdwvw, ptr, writecount) ;
+
+		total += count ;
+		len -= count ;
+
+		if (count != writecount)
+			break ;
+		} ;
+
+	return total ;
+} /* dwvw_write_i */
+
+static sf_count_t
+dwvw_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len)
+{	DWVW_PRIVATE *pdwvw ;
+	BUF_UNION	ubuf ;
+	int			*iptr ;
+	int			k, bufferlen, writecount = 0, count ;
+	sf_count_t	total = 0 ;
+	float		normfact ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	pdwvw = (DWVW_PRIVATE*) psf->codec_data ;
+
+	normfact = (psf->norm_float == SF_TRUE) ? (1.0 * 0x7FFFFFFF) : 1.0 ;
+
+	iptr = ubuf.ibuf ;
+	bufferlen = ARRAY_LEN (ubuf.ibuf) ;
+	while (len > 0)
+	{	writecount = (len >= bufferlen) ? bufferlen : (int) len ;
+		for (k = 0 ; k < writecount ; k++)
+			iptr [k] = psf_lrintf (normfact * ptr [total + k]) ;
+		count = dwvw_encode_data (psf, pdwvw, iptr, writecount) ;
+
+		total += count ;
+		len -= writecount ;
+		if (count != writecount)
+			break ;
+		} ;
+
+	return total ;
+} /* dwvw_write_f */
+
+static sf_count_t
+dwvw_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len)
+{	DWVW_PRIVATE *pdwvw ;
+	BUF_UNION	ubuf ;
+	int			*iptr ;
+	int			k, bufferlen, writecount = 0, count ;
+	sf_count_t	total = 0 ;
+	double 		normfact ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	pdwvw = (DWVW_PRIVATE*) psf->codec_data ;
+
+	normfact = (psf->norm_double == SF_TRUE) ? (1.0 * 0x7FFFFFFF) : 1.0 ;
+
+	iptr = ubuf.ibuf ;
+	bufferlen = ARRAY_LEN (ubuf.ibuf) ;
+	while (len > 0)
+	{	writecount = (len >= bufferlen) ? bufferlen : (int) len ;
+		for (k = 0 ; k < writecount ; k++)
+			iptr [k] = psf_lrint (normfact * ptr [total + k]) ;
+		count = dwvw_encode_data (psf, pdwvw, iptr, writecount) ;
+
+		total += count ;
+		len -= writecount ;
+		if (count != writecount)
+			break ;
+		} ;
+
+	return total ;
+} /* dwvw_write_d */
+
diff --git a/src/file_io.c b/src/file_io.c
new file mode 100644
index 0000000..b7f7ed7
--- /dev/null
+++ b/src/file_io.c
@@ -0,0 +1,1206 @@
+/*
+** Copyright (C) 2002-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2003 Ross Bencina <rbencina@iprimus.com.au>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*
+**	The file is split into three sections as follows:
+**		- The top section (USE_WINDOWS_API == 0) for Linux, Unix and MacOSX
+**			systems (including Cygwin).
+**		- The middle section (USE_WINDOWS_API == 1) for microsoft windows
+**			(including MinGW) using the native windows API.
+**		- A legacy windows section which attempted to work around grevious
+**			bugs in microsoft's POSIX implementation.
+*/
+
+/*
+**	The header file sfconfig.h MUST be included before the others to ensure
+**	that large file support is enabled correctly on Unix systems.
+*/
+
+#include "sfconfig.h"
+
+#if USE_WINDOWS_API
+
+/* Don't include rarely used headers, speed up build */
+#define WIN32_LEAN_AND_MEAN
+
+#include <windows.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#else
+#include <io.h>
+#endif
+
+#if (HAVE_DECL_S_IRGRP == 0)
+#include <sf_unistd.h>
+#endif
+
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+#include "sndfile.h"
+#include "common.h"
+
+#define	SENSIBLE_SIZE	(0x40000000)
+
+/*
+**	Neat solution to the Win32/OS2 binary file flage requirement.
+**	If O_BINARY isn't already defined by the inclusion of the system
+**	headers, set it to zero.
+*/
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+static void psf_log_syserr (SF_PRIVATE *psf, int error) ;
+
+int
+psf_copy_filename (SF_PRIVATE *psf, const char *path)
+{	const char *ccptr ;
+	char *cptr ;
+
+	if (strlen (path) > 1 && strlen (path) - 1 >= sizeof (psf->file.path))
+	{	psf->error = SFE_FILENAME_TOO_LONG ;
+		return psf->error ;
+		} ;
+
+	snprintf (psf->file.path, sizeof (psf->file.path), "%s", path) ;
+	if ((ccptr = strrchr (path, '/')) || (ccptr = strrchr (path, '\\')))
+		ccptr ++ ;
+	else
+		ccptr = path ;
+
+	snprintf (psf->file.name, sizeof (psf->file.name), "%s", ccptr) ;
+
+	/* Now grab the directory. */
+	snprintf (psf->file.dir, sizeof (psf->file.dir), "%s", path) ;
+	if ((cptr = strrchr (psf->file.dir, '/')) || (cptr = strrchr (psf->file.dir, '\\')))
+		cptr [1] = 0 ;
+	else
+		psf->file.dir [0] = 0 ;
+
+	return 0 ;
+} /* psf_copy_filename */
+
+#if (USE_WINDOWS_API == 0)
+
+/*------------------------------------------------------------------------------
+** Win32 stuff at the bottom of the file. Unix and other sensible OSes here.
+*/
+
+static int psf_close_fd (int fd) ;
+static int psf_open_fd (PSF_FILE * pfile) ;
+static sf_count_t psf_get_filelen_fd (int fd) ;
+
+int
+psf_fopen (SF_PRIVATE *psf)
+{
+	psf->error = 0 ;
+	psf->file.filedes = psf_open_fd (&psf->file) ;
+
+	if (psf->file.filedes == - SFE_BAD_OPEN_MODE)
+	{	psf->error = SFE_BAD_OPEN_MODE ;
+		psf->file.filedes = -1 ;
+		return psf->error ;
+		} ;
+
+	if (psf->file.filedes == -1)
+		psf_log_syserr (psf, errno) ;
+
+	return psf->error ;
+} /* psf_fopen */
+
+int
+psf_fclose (SF_PRIVATE *psf)
+{	int retval ;
+
+	if (psf->virtual_io)
+		return 0 ;
+
+	if (psf->file.do_not_close_descriptor)
+	{	psf->file.filedes = -1 ;
+		return 0 ;
+		} ;
+
+	if ((retval = psf_close_fd (psf->file.filedes)) == -1)
+		psf_log_syserr (psf, errno) ;
+
+	psf->file.filedes = -1 ;
+
+	return retval ;
+} /* psf_fclose */
+
+int
+psf_open_rsrc (SF_PRIVATE *psf)
+{	size_t count ;
+
+	if (psf->rsrc.filedes > 0)
+		return 0 ;
+
+	/* Test for MacOSX style resource fork on HPFS or HPFS+ filesystems. */
+	count = snprintf (psf->rsrc.path, sizeof (psf->rsrc.path), "%s/..namedfork/rsrc", psf->file.path) ;
+	psf->error = SFE_NO_ERROR ;
+	if (count < sizeof (psf->rsrc.path))
+	{	if ((psf->rsrc.filedes = psf_open_fd (&psf->rsrc)) >= 0)
+		{	psf->rsrclength = psf_get_filelen_fd (psf->rsrc.filedes) ;
+			if (psf->rsrclength > 0 || (psf->rsrc.mode & SFM_WRITE))
+				return SFE_NO_ERROR ;
+			psf_close_fd (psf->rsrc.filedes) ;
+			psf->rsrc.filedes = -1 ;
+			} ;
+
+		if (psf->rsrc.filedes == - SFE_BAD_OPEN_MODE)
+		{	psf->error = SFE_BAD_OPEN_MODE ;
+			return psf->error ;
+			} ;
+		} ;
+
+	/*
+	** Now try for a resource fork stored as a separate file in the same
+	** directory, but preceded with a dot underscore.
+	*/
+	count = snprintf (psf->rsrc.path, sizeof (psf->rsrc.path), "%s._%s", psf->file.dir, psf->file.name) ;
+	psf->error = SFE_NO_ERROR ;
+	if (count < sizeof (psf->rsrc.path) && (psf->rsrc.filedes = psf_open_fd (&psf->rsrc)) >= 0)
+	{	psf->rsrclength = psf_get_filelen_fd (psf->rsrc.filedes) ;
+		return SFE_NO_ERROR ;
+		} ;
+
+	/*
+	** Now try for a resource fork stored in a separate file in the
+	** .AppleDouble/ directory.
+	*/
+	count = snprintf (psf->rsrc.path, sizeof (psf->rsrc.path), "%s.AppleDouble/%s", psf->file.dir, psf->file.name) ;
+	psf->error = SFE_NO_ERROR ;
+	if (count < sizeof (psf->rsrc.path))
+	{	if ((psf->rsrc.filedes = psf_open_fd (&psf->rsrc)) >= 0)
+		{	psf->rsrclength = psf_get_filelen_fd (psf->rsrc.filedes) ;
+			return SFE_NO_ERROR ;
+			} ;
+
+		/* No resource file found. */
+		if (psf->rsrc.filedes == -1)
+			psf_log_syserr (psf, errno) ;
+		}
+	else
+	{	psf->error = SFE_OPEN_FAILED ;
+		} ;
+
+	psf->rsrc.filedes = -1 ;
+
+	return psf->error ;
+} /* psf_open_rsrc */
+
+sf_count_t
+psf_get_filelen (SF_PRIVATE *psf)
+{	sf_count_t	filelen ;
+
+	if (psf->virtual_io)
+		return psf->vio.get_filelen (psf->vio_user_data) ;
+
+	filelen = psf_get_filelen_fd (psf->file.filedes) ;
+
+	if (filelen == -1)
+	{	psf_log_syserr (psf, errno) ;
+		return (sf_count_t) -1 ;
+		} ;
+
+	if (filelen == -SFE_BAD_STAT_SIZE)
+	{	psf->error = SFE_BAD_STAT_SIZE ;
+		return (sf_count_t) -1 ;
+		} ;
+
+	switch (psf->file.mode)
+	{	case SFM_WRITE :
+			filelen = filelen - psf->fileoffset ;
+			break ;
+
+		case SFM_READ :
+			if (psf->fileoffset > 0 && psf->filelength > 0)
+				filelen = psf->filelength ;
+			break ;
+
+		case SFM_RDWR :
+			/*
+			** Cannot open embedded files SFM_RDWR so we don't need to
+			** subtract psf->fileoffset. We already have the answer we
+			** need.
+			*/
+			break ;
+
+		default :
+			/* Shouldn't be here, so return error. */
+			filelen = -1 ;
+		} ;
+
+	return filelen ;
+} /* psf_get_filelen */
+
+int
+psf_close_rsrc (SF_PRIVATE *psf)
+{	psf_close_fd (psf->rsrc.filedes) ;
+	psf->rsrc.filedes = -1 ;
+	return 0 ;
+} /* psf_close_rsrc */
+
+int
+psf_set_stdio (SF_PRIVATE *psf)
+{	int	error = 0 ;
+
+	switch (psf->file.mode)
+	{	case SFM_RDWR :
+				error = SFE_OPEN_PIPE_RDWR ;
+				break ;
+
+		case SFM_READ :
+				psf->file.filedes = 0 ;
+				break ;
+
+		case SFM_WRITE :
+				psf->file.filedes = 1 ;
+				break ;
+
+		default :
+				error = SFE_BAD_OPEN_MODE ;
+				break ;
+		} ;
+	psf->filelength = 0 ;
+
+	return error ;
+} /* psf_set_stdio */
+
+void
+psf_set_file (SF_PRIVATE *psf, int fd)
+{	psf->file.filedes = fd ;
+} /* psf_set_file */
+
+int
+psf_file_valid (SF_PRIVATE *psf)
+{	return (psf->file.filedes >= 0) ? SF_TRUE : SF_FALSE ;
+} /* psf_set_file */
+
+sf_count_t
+psf_fseek (SF_PRIVATE *psf, sf_count_t offset, int whence)
+{	sf_count_t	absolute_position ;
+
+	if (psf->virtual_io)
+		return psf->vio.seek (offset, whence, psf->vio_user_data) ;
+
+	/* When decoding from pipes sometimes see seeks to the pipeoffset, which appears to mean do nothing. */
+	if (psf->is_pipe)
+	{	if (whence != SEEK_SET || offset != psf->pipeoffset)
+			psf_log_printf (psf, "psf_fseek : pipe seek to value other than pipeoffset\n") ;
+		return offset ;
+		}
+
+	switch (whence)
+	{	case SEEK_SET :
+				offset += psf->fileoffset ;
+				break ;
+
+		case SEEK_END :
+				break ;
+
+		case SEEK_CUR :
+				break ;
+
+		default :
+				/* We really should not be here. */
+				psf_log_printf (psf, "psf_fseek : whence is %d *****.\n", whence) ;
+				return 0 ;
+		} ;
+
+	absolute_position = lseek (psf->file.filedes, offset, whence) ;
+
+	if (absolute_position < 0)
+		psf_log_syserr (psf, errno) ;
+
+	return absolute_position - psf->fileoffset ;
+} /* psf_fseek */
+
+sf_count_t
+psf_fread (void *ptr, sf_count_t bytes, sf_count_t items, SF_PRIVATE *psf)
+{	sf_count_t total = 0 ;
+	ssize_t	count ;
+
+	if (psf->virtual_io)
+		return psf->vio.read (ptr, bytes*items, psf->vio_user_data) / bytes ;
+
+	items *= bytes ;
+
+	/* Do this check after the multiplication above. */
+	if (items <= 0)
+		return 0 ;
+
+	while (items > 0)
+	{	/* Break the read down to a sensible size. */
+		count = (items > SENSIBLE_SIZE) ? SENSIBLE_SIZE : (ssize_t) items ;
+
+		count = read (psf->file.filedes, ((char*) ptr) + total, (size_t) count) ;
+
+		if (count == -1)
+		{	if (errno == EINTR)
+				continue ;
+
+			psf_log_syserr (psf, errno) ;
+			break ;
+			} ;
+
+		if (count == 0)
+			break ;
+
+		total += count ;
+		items -= count ;
+		} ;
+
+	if (psf->is_pipe)
+		psf->pipeoffset += total ;
+
+	return total / bytes ;
+} /* psf_fread */
+
+sf_count_t
+psf_fwrite (const void *ptr, sf_count_t bytes, sf_count_t items, SF_PRIVATE *psf)
+{	sf_count_t total = 0 ;
+	ssize_t	count ;
+
+	if (bytes == 0 || items == 0)
+		return 0 ;
+
+	if (psf->virtual_io)
+		return psf->vio.write (ptr, bytes*items, psf->vio_user_data) / bytes ;
+
+	items *= bytes ;
+
+	/* Do this check after the multiplication above. */
+	if (items <= 0)
+		return 0 ;
+
+	while (items > 0)
+	{	/* Break the writes down to a sensible size. */
+		count = (items > SENSIBLE_SIZE) ? SENSIBLE_SIZE : items ;
+
+		count = write (psf->file.filedes, ((const char*) ptr) + total, count) ;
+
+		if (count == -1)
+		{	if (errno == EINTR)
+				continue ;
+
+			psf_log_syserr (psf, errno) ;
+			break ;
+			} ;
+
+		if (count == 0)
+			break ;
+
+		total += count ;
+		items -= count ;
+		} ;
+
+	if (psf->is_pipe)
+		psf->pipeoffset += total ;
+
+	return total / bytes ;
+} /* psf_fwrite */
+
+sf_count_t
+psf_ftell (SF_PRIVATE *psf)
+{	sf_count_t pos ;
+
+	if (psf->virtual_io)
+		return psf->vio.tell (psf->vio_user_data) ;
+
+	if (psf->is_pipe)
+		return psf->pipeoffset ;
+
+	pos = lseek (psf->file.filedes, 0, SEEK_CUR) ;
+
+	if (pos == ((sf_count_t) -1))
+	{	psf_log_syserr (psf, errno) ;
+		return -1 ;
+		} ;
+
+	return pos - psf->fileoffset ;
+} /* psf_ftell */
+
+static int
+psf_close_fd (int fd)
+{	int retval ;
+
+	if (fd < 0)
+		return 0 ;
+
+	while ((retval = close (fd)) == -1 && errno == EINTR)
+		/* Do nothing. */ ;
+
+	return retval ;
+} /* psf_close_fd */
+
+sf_count_t
+psf_fgets (char *buffer, sf_count_t bufsize, SF_PRIVATE *psf)
+{	sf_count_t	k = 0 ;
+	sf_count_t		count ;
+
+	while (k < bufsize - 1)
+	{	count = read (psf->file.filedes, &(buffer [k]), 1) ;
+
+		if (count == -1)
+		{	if (errno == EINTR)
+				continue ;
+
+			psf_log_syserr (psf, errno) ;
+			break ;
+			} ;
+
+		if (count == 0 || buffer [k++] == '\n')
+			break ;
+		} ;
+
+	buffer [k] = 0 ;
+
+	return k ;
+} /* psf_fgets */
+
+int
+psf_is_pipe (SF_PRIVATE *psf)
+{	struct stat statbuf ;
+
+	if (psf->virtual_io)
+		return SF_FALSE ;
+
+	if (fstat (psf->file.filedes, &statbuf) == -1)
+	{	psf_log_syserr (psf, errno) ;
+		/* Default to maximum safety. */
+		return SF_TRUE ;
+		} ;
+
+	if (S_ISFIFO (statbuf.st_mode) || S_ISSOCK (statbuf.st_mode))
+		return SF_TRUE ;
+
+	return SF_FALSE ;
+} /* psf_is_pipe */
+
+static sf_count_t
+psf_get_filelen_fd (int fd)
+{
+#if (SIZEOF_OFF_T == 4 && HAVE_FSTAT64)
+	struct stat64 statbuf ;
+
+	if (fstat64 (fd, &statbuf) == -1)
+		return (sf_count_t) -1 ;
+
+	return statbuf.st_size ;
+#else
+	struct stat statbuf ;
+
+	if (fstat (fd, &statbuf) == -1)
+		return (sf_count_t) -1 ;
+
+	return statbuf.st_size ;
+#endif
+} /* psf_get_filelen_fd */
+
+int
+psf_ftruncate (SF_PRIVATE *psf, sf_count_t len)
+{	int retval ;
+
+	/* Returns 0 on success, non-zero on failure. */
+	if (len < 0)
+		return -1 ;
+
+	if ((sizeof (off_t) < sizeof (sf_count_t)) && len > 0x7FFFFFFF)
+		return -1 ;
+
+	retval = ftruncate (psf->file.filedes, len) ;
+
+	if (retval == -1)
+		psf_log_syserr (psf, errno) ;
+
+	return retval ;
+} /* psf_ftruncate */
+
+void
+psf_init_files (SF_PRIVATE *psf)
+{	psf->file.filedes = -1 ;
+	psf->rsrc.filedes = -1 ;
+	psf->file.savedes = -1 ;
+} /* psf_init_files */
+
+void
+psf_use_rsrc (SF_PRIVATE *psf, int on_off)
+{
+	if (on_off)
+	{	if (psf->file.filedes != psf->rsrc.filedes)
+		{	psf->file.savedes = psf->file.filedes ;
+			psf->file.filedes = psf->rsrc.filedes ;
+			} ;
+		}
+	else if (psf->file.filedes == psf->rsrc.filedes)
+		psf->file.filedes = psf->file.savedes ;
+
+	return ;
+} /* psf_use_rsrc */
+
+static int
+psf_open_fd (PSF_FILE * pfile)
+{	int fd, oflag, mode ;
+
+	/*
+	** Sanity check. If everything is OK, this test and the printfs will
+	** be optimised out. This is meant to catch the problems caused by
+	** "sfconfig.h" being included after <stdio.h>.
+	*/
+	if (sizeof (sf_count_t) != 8)
+	{	puts ("\n\n*** Fatal error : sizeof (sf_count_t) != 8") ;
+		puts ("*** This means that libsndfile was not configured correctly.\n") ;
+		exit (1) ;
+		} ;
+
+	switch (pfile->mode)
+	{	case SFM_READ :
+				oflag = O_RDONLY | O_BINARY ;
+				mode = 0 ;
+				break ;
+
+		case SFM_WRITE :
+				oflag = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY ;
+				mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH ;
+				break ;
+
+		case SFM_RDWR :
+				oflag = O_RDWR | O_CREAT | O_BINARY ;
+				mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH ;
+				break ;
+
+		default :
+				return - SFE_BAD_OPEN_MODE ;
+				break ;
+		} ;
+
+	if (mode == 0)
+		fd = open (pfile->path, oflag) ;
+	else
+		fd = open (pfile->path, oflag, mode) ;
+
+	return fd ;
+} /* psf_open_fd */
+
+static void
+psf_log_syserr (SF_PRIVATE *psf, int error)
+{
+	/* Only log an error if no error has been set yet. */
+	if (psf->error == 0)
+	{	psf->error = SFE_SYSTEM ;
+		snprintf (psf->syserr, sizeof (psf->syserr), "System error : %s.", strerror (error)) ;
+		} ;
+
+	return ;
+} /* psf_log_syserr */
+
+void
+psf_fsync (SF_PRIVATE *psf)
+{
+#if HAVE_FSYNC
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+		fsync (psf->file.filedes) ;
+#else
+	psf = NULL ;
+#endif
+} /* psf_fsync */
+
+#else
+
+/* Win32 file i/o functions implemented using native Win32 API */
+
+#ifndef WINAPI_PARTITION_SYSTEM
+#define WINAPI_PARTITION_SYSTEM 0
+#endif
+
+static int psf_close_handle (HANDLE handle) ;
+static HANDLE psf_open_handle (PSF_FILE * pfile) ;
+static sf_count_t psf_get_filelen_handle (HANDLE handle) ;
+
+/* USE_WINDOWS_API */ int
+psf_fopen (SF_PRIVATE *psf)
+{
+	psf->error = 0 ;
+	psf->file.handle = psf_open_handle (&psf->file) ;
+
+	if (psf->file.handle == INVALID_HANDLE_VALUE)
+		psf_log_syserr (psf, GetLastError ()) ;
+
+	return psf->error ;
+} /* psf_fopen */
+
+/* USE_WINDOWS_API */ int
+psf_fclose (SF_PRIVATE *psf)
+{	int retval ;
+
+	if (psf->virtual_io)
+		return 0 ;
+
+	if (psf->file.do_not_close_descriptor)
+	{	psf->file.handle = INVALID_HANDLE_VALUE ;
+		return 0 ;
+		} ;
+
+	if ((retval = psf_close_handle (psf->file.handle)) == -1)
+		psf_log_syserr (psf, GetLastError ()) ;
+
+	psf->file.handle = INVALID_HANDLE_VALUE ;
+
+	return retval ;
+} /* psf_fclose */
+
+/* USE_WINDOWS_API */ int
+psf_open_rsrc (SF_PRIVATE *psf)
+{
+	if (psf->rsrc.handle != INVALID_HANDLE_VALUE)
+		return 0 ;
+
+	/* Test for MacOSX style resource fork on HPFS or HPFS+ filesystems. */
+	snprintf (psf->rsrc.path, sizeof (psf->rsrc.path), "%s/rsrc", psf->file.path) ;
+	psf->error = SFE_NO_ERROR ;
+	if ((psf->rsrc.handle = psf_open_handle (&psf->rsrc)) != INVALID_HANDLE_VALUE)
+	{	psf->rsrclength = psf_get_filelen_handle (psf->rsrc.handle) ;
+		return SFE_NO_ERROR ;
+		} ;
+
+	/*
+	** Now try for a resource fork stored as a separate file in the same
+	** directory, but preceded with a dot underscore.
+	*/
+	snprintf (psf->rsrc.path, sizeof (psf->rsrc.path), "%s._%s", psf->file.dir, psf->file.name) ;
+	psf->error = SFE_NO_ERROR ;
+	if ((psf->rsrc.handle = psf_open_handle (&psf->rsrc)) != INVALID_HANDLE_VALUE)
+	{	psf->rsrclength = psf_get_filelen_handle (psf->rsrc.handle) ;
+		return SFE_NO_ERROR ;
+		} ;
+
+	/*
+	** Now try for a resource fork stored in a separate file in the
+	** .AppleDouble/ directory.
+	*/
+	snprintf (psf->rsrc.path, sizeof (psf->rsrc.path), "%s.AppleDouble/%s", psf->file.dir, psf->file.name) ;
+	psf->error = SFE_NO_ERROR ;
+	if ((psf->rsrc.handle = psf_open_handle (&psf->rsrc)) != INVALID_HANDLE_VALUE)
+	{	psf->rsrclength = psf_get_filelen_handle (psf->rsrc.handle) ;
+		return SFE_NO_ERROR ;
+		} ;
+
+	/* No resource file found. */
+	if (psf->rsrc.handle == INVALID_HANDLE_VALUE)
+		psf_log_syserr (psf, GetLastError ()) ;
+
+	return psf->error ;
+} /* psf_open_rsrc */
+
+/* USE_WINDOWS_API */ sf_count_t
+psf_get_filelen (SF_PRIVATE *psf)
+{	sf_count_t	filelen ;
+
+	if (psf->virtual_io)
+		return psf->vio.get_filelen (psf->vio_user_data) ;
+
+	filelen = psf_get_filelen_handle (psf->file.handle) ;
+
+	if (filelen == -1)
+	{	psf_log_syserr (psf, errno) ;
+		return (sf_count_t) -1 ;
+		} ;
+
+	if (filelen == -SFE_BAD_STAT_SIZE)
+	{	psf->error = SFE_BAD_STAT_SIZE ;
+		return (sf_count_t) -1 ;
+		} ;
+
+	switch (psf->file.mode)
+	{	case SFM_WRITE :
+			filelen = filelen - psf->fileoffset ;
+			break ;
+
+		case SFM_READ :
+			if (psf->fileoffset > 0 && psf->filelength > 0)
+				filelen = psf->filelength ;
+			break ;
+
+		case SFM_RDWR :
+			/*
+			** Cannot open embedded files SFM_RDWR so we don't need to
+			** subtract psf->fileoffset. We already have the answer we
+			** need.
+			*/
+			break ;
+
+		default :
+			/* Shouldn't be here, so return error. */
+			filelen = -1 ;
+		} ;
+
+	return filelen ;
+} /* psf_get_filelen */
+
+/* USE_WINDOWS_API */ void
+psf_init_files (SF_PRIVATE *psf)
+{	psf->file.handle = INVALID_HANDLE_VALUE ;
+	psf->rsrc.handle = INVALID_HANDLE_VALUE ;
+	psf->file.hsaved = INVALID_HANDLE_VALUE ;
+} /* psf_init_files */
+
+/* USE_WINDOWS_API */ void
+psf_use_rsrc (SF_PRIVATE *psf, int on_off)
+{
+	if (on_off)
+	{	if (psf->file.handle != psf->rsrc.handle)
+		{	psf->file.hsaved = psf->file.handle ;
+			psf->file.handle = psf->rsrc.handle ;
+			} ;
+		}
+	else if (psf->file.handle == psf->rsrc.handle)
+		psf->file.handle = psf->file.hsaved ;
+
+	return ;
+} /* psf_use_rsrc */
+
+/* USE_WINDOWS_API */ static HANDLE
+psf_open_handle (PSF_FILE * pfile)
+{	DWORD dwDesiredAccess ;
+	DWORD dwShareMode ;
+	DWORD dwCreationDistribution ;
+	HANDLE handle ;
+	LPWSTR pwszPath = NULL ;
+
+	switch (pfile->mode)
+	{	case SFM_READ :
+				dwDesiredAccess = GENERIC_READ ;
+				dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE ;
+				dwCreationDistribution = OPEN_EXISTING ;
+				break ;
+
+		case SFM_WRITE :
+				dwDesiredAccess = GENERIC_WRITE ;
+				dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE ;
+				dwCreationDistribution = CREATE_ALWAYS ;
+				break ;
+
+		case SFM_RDWR :
+				dwDesiredAccess = GENERIC_READ | GENERIC_WRITE ;
+				dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE ;
+				dwCreationDistribution = OPEN_ALWAYS ;
+				break ;
+
+		default :
+				return INVALID_HANDLE_VALUE ;
+		} ;
+
+	int nResult = MultiByteToWideChar (CP_UTF8, 0, pfile->path, -1, NULL, 0) ;
+	pwszPath = malloc (nResult * sizeof (WCHAR)) ;
+	if (!pwszPath)
+		return INVALID_HANDLE_VALUE ;
+	
+	int nResult2 = MultiByteToWideChar (CP_UTF8, 0, pfile->path, -1, pwszPath, nResult) ;
+	if (nResult != nResult2)
+	{	free (pwszPath) ;
+		return INVALID_HANDLE_VALUE ;
+		} ;
+
+#if defined (WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
+	CREATEFILE2_EXTENDED_PARAMETERS cfParams = { 0 } ;
+	cfParams.dwSize = sizeof (CREATEFILE2_EXTENDED_PARAMETERS) ;
+	cfParams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL ;
+
+	handle = CreateFile2 (pwszPath, dwDesiredAccess, dwShareMode, dwCreationDistribution, &cfParams) ;
+#else
+	handle = CreateFileW (
+				pwszPath,					/* pointer to name of the file */
+				dwDesiredAccess,			/* access (read-write) mode */
+				dwShareMode,				/* share mode */
+				0,							/* pointer to security attributes */
+				dwCreationDistribution,		/* how to create */
+				FILE_ATTRIBUTE_NORMAL,		/* file attributes (could use FILE_FLAG_SEQUENTIAL_SCAN) */
+				NULL						/* handle to file with attributes to copy */
+				) ;
+#endif
+	free (pwszPath) ;
+
+	return handle ;
+} /* psf_open_handle */
+
+/* USE_WINDOWS_API */ static void
+psf_log_syserr (SF_PRIVATE *psf, int error)
+{	LPVOID lpMsgBuf ;
+
+	/* Only log an error if no error has been set yet. */
+	if (psf->error == 0)
+	{	psf->error = SFE_SYSTEM ;
+
+		FormatMessage (
+			FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+			NULL,
+			error,
+			MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
+			(LPTSTR) &lpMsgBuf,
+			0,
+			NULL
+			) ;
+
+		snprintf (psf->syserr, sizeof (psf->syserr), "System error : %s", (char*) lpMsgBuf) ;
+		LocalFree (lpMsgBuf) ;
+		} ;
+
+	return ;
+} /* psf_log_syserr */
+
+
+/* USE_WINDOWS_API */ int
+psf_close_rsrc (SF_PRIVATE *psf)
+{	psf_close_handle (psf->rsrc.handle) ;
+	psf->rsrc.handle = INVALID_HANDLE_VALUE ;
+	return 0 ;
+} /* psf_close_rsrc */
+
+
+/* USE_WINDOWS_API */ int
+psf_set_stdio (SF_PRIVATE *psf)
+{	HANDLE	handle = INVALID_HANDLE_VALUE ;
+	int	error = 0 ;
+
+	switch (psf->file.mode)
+	{	case SFM_RDWR :
+				error = SFE_OPEN_PIPE_RDWR ;
+				break ;
+
+		case SFM_READ :
+				handle = GetStdHandle (STD_INPUT_HANDLE) ;
+				psf->file.do_not_close_descriptor = 1 ;
+				break ;
+
+		case SFM_WRITE :
+				handle = GetStdHandle (STD_OUTPUT_HANDLE) ;
+				psf->file.do_not_close_descriptor = 1 ;
+				break ;
+
+		default :
+				error = SFE_BAD_OPEN_MODE ;
+				break ;
+		} ;
+
+	psf->file.handle = handle ;
+	psf->filelength = 0 ;
+
+	return error ;
+} /* psf_set_stdio */
+
+/* USE_WINDOWS_API */ void
+psf_set_file (SF_PRIVATE *psf, int fd)
+{	HANDLE handle ;
+	intptr_t osfhandle ;
+
+	osfhandle = _get_osfhandle (fd) ;
+	handle = (HANDLE) osfhandle ;
+
+	psf->file.handle = handle ;
+} /* psf_set_file */
+
+/* USE_WINDOWS_API */ int
+psf_file_valid (SF_PRIVATE *psf)
+{	if (psf->file.handle == INVALID_HANDLE_VALUE)
+		return SF_FALSE ;
+	return SF_TRUE ;
+} /* psf_set_file */
+
+/* USE_WINDOWS_API */ sf_count_t
+psf_fseek (SF_PRIVATE *psf, sf_count_t offset, int whence)
+{	sf_count_t new_position ;
+	LARGE_INTEGER liDistanceToMove, liNewFilePointer ;
+	DWORD dwMoveMethod ;
+	BOOL fResult ;
+	DWORD dwError ;
+
+	if (psf->virtual_io)
+		return psf->vio.seek (offset, whence, psf->vio_user_data) ;
+
+	switch (whence)
+	{	case SEEK_SET :
+				offset += psf->fileoffset ;
+				dwMoveMethod = FILE_BEGIN ;
+				break ;
+
+		case SEEK_END :
+				dwMoveMethod = FILE_END ;
+				break ;
+
+		default :
+				dwMoveMethod = FILE_CURRENT ;
+				break ;
+		} ;
+
+	liDistanceToMove.QuadPart = offset ;
+
+	fResult = SetFilePointerEx (psf->file.handle, liDistanceToMove, &liNewFilePointer, dwMoveMethod) ;
+
+	if (fResult == FALSE)
+		dwError = GetLastError () ;
+	else
+		dwError = NO_ERROR ;
+
+	if (dwError != NO_ERROR)
+	{	psf_log_syserr (psf, dwError) ;
+		return -1 ;
+		} ;
+
+	new_position = liNewFilePointer.QuadPart - psf->fileoffset ;
+
+	return new_position ;
+} /* psf_fseek */
+
+/* USE_WINDOWS_API */ sf_count_t
+psf_fread (void *ptr, sf_count_t bytes, sf_count_t items, SF_PRIVATE *psf)
+{	sf_count_t total = 0 ;
+	ssize_t count ;
+	DWORD dwNumberOfBytesRead ;
+
+	if (psf->virtual_io)
+		return psf->vio.read (ptr, bytes*items, psf->vio_user_data) / bytes ;
+
+	items *= bytes ;
+
+	/* Do this check after the multiplication above. */
+	if (items <= 0)
+		return 0 ;
+
+	while (items > 0)
+	{	/* Break the writes down to a sensible size. */
+		count = (items > SENSIBLE_SIZE) ? SENSIBLE_SIZE : (ssize_t) items ;
+
+		if (ReadFile (psf->file.handle, ((char*) ptr) + total, count, &dwNumberOfBytesRead, 0) == 0)
+		{	psf_log_syserr (psf, GetLastError ()) ;
+			break ;
+			}
+		else
+			count = dwNumberOfBytesRead ;
+
+		if (count == 0)
+			break ;
+
+		total += count ;
+		items -= count ;
+		} ;
+
+	if (psf->is_pipe)
+		psf->pipeoffset += total ;
+
+	return total / bytes ;
+} /* psf_fread */
+
+/* USE_WINDOWS_API */ sf_count_t
+psf_fwrite (const void *ptr, sf_count_t bytes, sf_count_t items, SF_PRIVATE *psf)
+{	sf_count_t total = 0 ;
+	ssize_t	count ;
+	DWORD dwNumberOfBytesWritten ;
+
+	if (psf->virtual_io)
+		return psf->vio.write (ptr, bytes * items, psf->vio_user_data) / bytes ;
+
+	items *= bytes ;
+
+	/* Do this check after the multiplication above. */
+	if (items <= 0)
+		return 0 ;
+
+	while (items > 0)
+	{	/* Break the writes down to a sensible size. */
+		count = (items > SENSIBLE_SIZE) ? SENSIBLE_SIZE : (ssize_t) items ;
+
+		if (WriteFile (psf->file.handle, ((const char*) ptr) + total, count, &dwNumberOfBytesWritten, 0) == 0)
+		{	psf_log_syserr (psf, GetLastError ()) ;
+			break ;
+			}
+		else
+			count = dwNumberOfBytesWritten ;
+
+		if (count == 0)
+			break ;
+
+		total += count ;
+		items -= count ;
+		} ;
+
+	if (psf->is_pipe)
+		psf->pipeoffset += total ;
+
+	return total / bytes ;
+} /* psf_fwrite */
+
+/* USE_WINDOWS_API */ sf_count_t
+psf_ftell (SF_PRIVATE *psf)
+{	sf_count_t pos ;
+	LARGE_INTEGER liDistanceToMove, liNewFilePointer ;
+	BOOL fResult ;
+	DWORD dwError ;
+
+	if (psf->virtual_io)
+		return psf->vio.tell (psf->vio_user_data) ;
+
+	if (psf->is_pipe)
+		return psf->pipeoffset ;
+
+	liDistanceToMove.QuadPart = 0 ;
+
+	fResult = SetFilePointerEx (psf->file.handle, liDistanceToMove, &liNewFilePointer, FILE_CURRENT) ;
+
+	if (fResult == FALSE)
+		dwError = GetLastError () ;
+	else
+		dwError = NO_ERROR ;
+
+	if (dwError != NO_ERROR)
+	{	psf_log_syserr (psf, dwError) ;
+		return -1 ;
+		} ;
+
+	pos = liNewFilePointer.QuadPart ;
+
+	return pos - psf->fileoffset ;
+} /* psf_ftell */
+
+/* USE_WINDOWS_API */ static int
+psf_close_handle (HANDLE handle)
+{	if (handle == INVALID_HANDLE_VALUE)
+		return 0 ;
+
+	if (CloseHandle (handle) == 0)
+		return -1 ;
+
+	return 0 ;
+} /* psf_close_handle */
+
+/* USE_WINDOWS_API */ sf_count_t
+psf_fgets (char *buffer, sf_count_t bufsize, SF_PRIVATE *psf)
+{	sf_count_t k = 0 ;
+	sf_count_t count ;
+	DWORD dwNumberOfBytesRead ;
+
+	while (k < bufsize - 1)
+	{	if (ReadFile (psf->file.handle, &(buffer [k]), 1, &dwNumberOfBytesRead, 0) == 0)
+		{	psf_log_syserr (psf, GetLastError ()) ;
+			break ;
+			}
+		else
+		{	count = dwNumberOfBytesRead ;
+			/* note that we only check for '\n' not other line endings such as CRLF */
+			if (count == 0 || buffer [k++] == '\n')
+				break ;
+			} ;
+		} ;
+
+	buffer [k] = 0 ;
+
+	return k ;
+} /* psf_fgets */
+
+/* USE_WINDOWS_API */ int
+psf_is_pipe (SF_PRIVATE *psf)
+{
+	if (psf->virtual_io)
+		return SF_FALSE ;
+
+	if (GetFileType (psf->file.handle) == FILE_TYPE_DISK)
+		return SF_FALSE ;
+
+	/* Default to maximum safety. */
+	return SF_TRUE ;
+} /* psf_is_pipe */
+
+/* USE_WINDOWS_API */ sf_count_t
+psf_get_filelen_handle (HANDLE handle)
+{	sf_count_t filelen ;
+	LARGE_INTEGER liFileSize ;
+	BOOL fResult ;
+	DWORD dwError = NO_ERROR ;
+
+	fResult = GetFileSizeEx (handle, &liFileSize) ;
+
+	if (fResult == FALSE)
+		dwError = GetLastError () ;
+
+	if (dwError != NO_ERROR)
+		return (sf_count_t) -1 ;
+
+	filelen = liFileSize.QuadPart ;
+
+	return filelen ;
+} /* psf_get_filelen_handle */
+
+/* USE_WINDOWS_API */ void
+psf_fsync (SF_PRIVATE *psf)
+{	FlushFileBuffers (psf->file.handle) ;
+} /* psf_fsync */
+
+
+/* USE_WINDOWS_API */ int
+psf_ftruncate (SF_PRIVATE *psf, sf_count_t len)
+{	int retval = 0 ;
+	LARGE_INTEGER liDistanceToMove ;
+	BOOL fResult ;
+	DWORD dwError = NO_ERROR ;
+
+	/* This implementation trashes the current file position.
+	** should it save and restore it? what if the current position is past
+	** the new end of file?
+	*/
+
+	/* Returns 0 on success, non-zero on failure. */
+	if (len < 0)
+		return 1 ;
+
+	liDistanceToMove.QuadPart = (sf_count_t) len ;
+
+	fResult = SetFilePointerEx (psf->file.handle, liDistanceToMove, NULL, FILE_BEGIN) ;
+
+	if (fResult == FALSE)
+		dwError = GetLastError () ;
+
+	if (dwError != NO_ERROR)
+	{	retval = -1 ;
+		psf_log_syserr (psf, dwError) ;
+		}
+	else
+	{	/* Note: when SetEndOfFile is used to extend a file, the contents of the
+		** new portion of the file is undefined. This is unlike chsize(),
+		** which guarantees that the new portion of the file will be zeroed.
+		** Not sure if this is important or not.
+		*/
+		if (SetEndOfFile (psf->file.handle) == 0)
+		{	retval = -1 ;
+			psf_log_syserr (psf, GetLastError ()) ;
+			} ;
+		} ;
+
+	return retval ;
+} /* psf_ftruncate */
+
+#endif
+
diff --git a/src/flac.c b/src/flac.c
new file mode 100644
index 0000000..073ce55
--- /dev/null
+++ b/src/flac.c
@@ -0,0 +1,1463 @@
+/*
+** Copyright (C) 2004-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2004 Tobias Gehrig <tgehrig@ira.uka.de>
+**
+** This program is free software ; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation ; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program ; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<stdlib.h>
+#include	<fcntl.h>
+#include	<string.h>
+#include	<ctype.h>
+#include	<math.h>
+
+#include	"sndfile.h"
+#include	"common.h"
+
+#if HAVE_EXTERNAL_XIPH_LIBS
+
+#include	<FLAC/stream_decoder.h>
+#include	<FLAC/stream_encoder.h>
+#include	<FLAC/metadata.h>
+
+/*------------------------------------------------------------------------------
+** Private static functions.
+*/
+
+#define	FLAC_DEFAULT_COMPRESSION_LEVEL	5
+
+#define ENC_BUFFER_SIZE 8192
+
+/*
+** READ_LOOP_MAX_LEN is the maximum 'len' that will be passed to
+** flac_read_loop().  This is somewhat arbitrary, but must be less
+** than (UINT_MAX - FLAC__MAX_CHANNELS * FLAC__MAX_BLOCK_SIZE) to
+** avoid overflows, and must also be a multiple of the number of
+** channels (which is between 1 and 8.)
+*/
+#define READ_LOOP_MAX_LEN (0x10000 * 3 * 5 * 7)
+
+typedef enum
+{	PFLAC_PCM_SHORT = 50,
+	PFLAC_PCM_INT = 51,
+	PFLAC_PCM_FLOAT = 52,
+	PFLAC_PCM_DOUBLE = 53
+} PFLAC_PCM ;
+
+typedef struct
+{
+	FLAC__StreamDecoder *fsd ;
+	FLAC__StreamEncoder *fse ;
+
+	PFLAC_PCM pcmtype ;
+	void* ptr ;
+	unsigned pos, len, remain ;
+
+	FLAC__StreamMetadata *metadata ;
+
+	const int32_t * const * wbuffer ;
+	int32_t * rbuffer [FLAC__MAX_CHANNELS] ;
+
+	int32_t* encbuffer ;
+	unsigned bufferpos ;
+
+	const FLAC__Frame *frame ;
+
+	unsigned compression ;
+
+} FLAC_PRIVATE ;
+
+typedef struct
+{	const char *tag ;
+	int type ;
+} FLAC_TAG ;
+
+static sf_count_t	flac_seek (SF_PRIVATE *psf, int mode, sf_count_t offset) ;
+static int			flac_byterate (SF_PRIVATE *psf) ;
+static int			flac_close (SF_PRIVATE *psf) ;
+
+static int			flac_enc_init (SF_PRIVATE *psf) ;
+static int			flac_read_header (SF_PRIVATE *psf) ;
+
+static sf_count_t	flac_read_flac2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
+static sf_count_t	flac_read_flac2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
+static sf_count_t	flac_read_flac2f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
+static sf_count_t	flac_read_flac2d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
+
+static sf_count_t	flac_write_s2flac (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
+static sf_count_t	flac_write_i2flac (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
+static sf_count_t	flac_write_f2flac (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
+static sf_count_t	flac_write_d2flac (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
+
+static void		f2flac8_array (const float *src, int32_t *dest, int count, int normalize) ;
+static void		f2flac16_array (const float *src, int32_t *dest, int count, int normalize) ;
+static void		f2flac24_array (const float *src, int32_t *dest, int count, int normalize) ;
+static void		f2flac8_clip_array (const float *src, int32_t *dest, int count, int normalize) ;
+static void		f2flac16_clip_array (const float *src, int32_t *dest, int count, int normalize) ;
+static void		f2flac24_clip_array (const float *src, int32_t *dest, int count, int normalize) ;
+static void		d2flac8_array (const double *src, int32_t *dest, int count, int normalize) ;
+static void		d2flac16_array (const double *src, int32_t *dest, int count, int normalize) ;
+static void		d2flac24_array (const double *src, int32_t *dest, int count, int normalize) ;
+static void		d2flac8_clip_array (const double *src, int32_t *dest, int count, int normalize) ;
+static void		d2flac16_clip_array (const double *src, int32_t *dest, int count, int normalize) ;
+static void		d2flac24_clip_array (const double *src, int32_t *dest, int count, int normalize) ;
+
+static int flac_command (SF_PRIVATE *psf, int command, void *data, int datasize) ;
+
+/* Decoder Callbacks */
+static FLAC__StreamDecoderReadStatus sf_flac_read_callback (const FLAC__StreamDecoder *decoder, FLAC__byte buffer [], size_t *bytes, void *client_data) ;
+static FLAC__StreamDecoderSeekStatus sf_flac_seek_callback (const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data) ;
+static FLAC__StreamDecoderTellStatus sf_flac_tell_callback (const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) ;
+static FLAC__StreamDecoderLengthStatus sf_flac_length_callback (const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data) ;
+static FLAC__bool sf_flac_eof_callback (const FLAC__StreamDecoder *decoder, void *client_data) ;
+static FLAC__StreamDecoderWriteStatus sf_flac_write_callback (const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const int32_t * const buffer [], void *client_data) ;
+static void sf_flac_meta_callback (const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) ;
+static void sf_flac_error_callback (const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) ;
+
+/* Encoder Callbacks */
+static FLAC__StreamEncoderSeekStatus sf_flac_enc_seek_callback (const FLAC__StreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data) ;
+static FLAC__StreamEncoderTellStatus sf_flac_enc_tell_callback (const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data) ;
+static FLAC__StreamEncoderWriteStatus sf_flac_enc_write_callback (const FLAC__StreamEncoder *encoder, const FLAC__byte buffer [], size_t bytes, unsigned samples, unsigned current_frame, void *client_data) ;
+
+static void
+s2flac8_array (const short *src, int32_t *dest, int count)
+{	for (int i = 0 ; i < count ; i++)
+		dest [i] = src [i] >> 8 ;
+} /* s2flac8_array */
+
+static void
+s2flac16_array (const short *src, int32_t *dest, int count)
+{	for (int i = 0 ; i < count ; i++)
+		dest [i] = src [i] ;
+} /* s2flac16_array */
+
+static void
+s2flac24_array (const short *src, int32_t *dest, int count)
+{	for (int i = 0 ; i < count ; i++)
+		dest [i] = src [i] << 8 ;
+} /* s2flac24_array */
+
+static void
+i2flac8_array (const int *src, int32_t *dest, int count)
+{	for (int i = 0 ; i < count ; i++)
+		dest [i] = src [i] >> 24 ;
+} /* i2flac8_array */
+
+static void
+i2flac16_array (const int *src, int32_t *dest, int count)
+{
+	for (int i = 0 ; i < count ; i++)
+		dest [i] = src [i] >> 16 ;
+} /* i2flac16_array */
+
+static void
+i2flac24_array (const int *src, int32_t *dest, int count)
+{	for (int i = 0 ; i < count ; i++)
+		dest [i] = src [i] >> 8 ;
+} /* i2flac24_array */
+
+static sf_count_t
+flac_buffer_copy (SF_PRIVATE *psf)
+{	FLAC_PRIVATE* pflac = (FLAC_PRIVATE*) psf->codec_data ;
+	const FLAC__Frame *frame = pflac->frame ;
+	const int32_t* const *buffer = pflac->wbuffer ;
+	unsigned i = 0, j, offset, channels, len ;
+
+	if (psf->sf.channels != (int) frame->header.channels)
+	{	psf_log_printf (psf, "Error: FLAC frame changed from %d to %d channels\n"
+									"Nothing to do but to error out.\n" ,
+									psf->sf.channels, frame->header.channels) ;
+		psf->error = SFE_FLAC_CHANNEL_COUNT_CHANGED ;
+		return 0 ;
+		} ;
+
+	/*
+	**	frame->header.blocksize is variable and we're using a constant blocksize
+	**	of FLAC__MAX_BLOCK_SIZE.
+	**	Check our assumptions here.
+	*/
+	if (frame->header.blocksize > FLAC__MAX_BLOCK_SIZE)
+	{	psf_log_printf (psf, "Ooops : frame->header.blocksize (%d) > FLAC__MAX_BLOCK_SIZE (%d)\n", __func__, __LINE__, frame->header.blocksize, FLAC__MAX_BLOCK_SIZE) ;
+		psf->error = SFE_INTERNAL ;
+		return 0 ;
+		} ;
+
+	if (frame->header.channels > FLAC__MAX_CHANNELS)
+		psf_log_printf (psf, "Ooops : frame->header.channels (%d) > FLAC__MAX_BLOCK_SIZE (%d)\n", __func__, __LINE__, frame->header.channels, FLAC__MAX_CHANNELS) ;
+
+	channels = SF_MIN (frame->header.channels, FLAC__MAX_CHANNELS) ;
+
+	if (pflac->ptr == NULL)
+	{	/*
+		** This pointer is reset to NULL each time the current frame has been
+		** decoded. Somehow its used during encoding and decoding.
+		*/
+		for (i = 0 ; i < channels ; i++)
+		{
+			if (pflac->rbuffer [i] == NULL)
+				pflac->rbuffer [i] = calloc (FLAC__MAX_BLOCK_SIZE, sizeof (int32_t)) ;
+
+			memcpy (pflac->rbuffer [i], buffer [i], frame->header.blocksize * sizeof (int32_t)) ;
+			} ;
+		pflac->wbuffer = (const int32_t* const*) pflac->rbuffer ;
+
+		return 0 ;
+		} ;
+
+	len = SF_MIN (pflac->len, frame->header.blocksize) ;
+
+	if (pflac->remain % channels != 0)
+	{	psf_log_printf (psf, "Error: pflac->remain %u    channels %u\n", pflac->remain, channels) ;
+		return 0 ;
+		} ;
+
+	switch (pflac->pcmtype)
+	{	case PFLAC_PCM_SHORT :
+			{	short *retpcm = (short*) pflac->ptr ;
+				int shift = 16 - frame->header.bits_per_sample ;
+				if (shift < 0)
+				{	shift = abs (shift) ;
+					for (i = 0 ; i < len && pflac->remain > 0 ; i++)
+					{	offset = pflac->pos + i * channels ;
+
+						if (pflac->bufferpos >= frame->header.blocksize)
+							break ;
+
+						if (offset + channels > pflac->len)
+							break ;
+
+						for (j = 0 ; j < channels ; j++)
+							retpcm [offset + j] = buffer [j][pflac->bufferpos] >> shift ;
+						pflac->remain -= channels ;
+						pflac->bufferpos ++ ;
+						}
+					}
+				else
+				{	for (i = 0 ; i < len && pflac->remain > 0 ; i++)
+					{	offset = pflac->pos + i * channels ;
+
+						if (pflac->bufferpos >= frame->header.blocksize)
+							break ;
+
+						if (offset + channels > pflac->len)
+							break ;
+
+						for (j = 0 ; j < channels ; j++)
+							retpcm [offset + j] = ((uint16_t) buffer [j][pflac->bufferpos]) << shift ;
+
+						pflac->remain -= channels ;
+						pflac->bufferpos ++ ;
+						} ;
+					} ;
+				} ;
+			break ;
+
+		case PFLAC_PCM_INT :
+			{	int *retpcm = (int*) pflac->ptr ;
+				int shift = 32 - frame->header.bits_per_sample ;
+				for (i = 0 ; i < len && pflac->remain > 0 ; i++)
+				{	offset = pflac->pos + i * channels ;
+
+					if (pflac->bufferpos >= frame->header.blocksize)
+						break ;
+
+					if (offset + channels > pflac->len)
+						break ;
+
+					for (j = 0 ; j < channels ; j++)
+						retpcm [offset + j] = ((uint32_t) buffer [j][pflac->bufferpos]) << shift ;
+					pflac->remain -= channels ;
+					pflac->bufferpos++ ;
+					} ;
+				} ;
+			break ;
+
+		case PFLAC_PCM_FLOAT :
+			{	float *retpcm = (float*) pflac->ptr ;
+				float norm = (psf->norm_float == SF_TRUE) ? 1.0 / (1 << (frame->header.bits_per_sample - 1)) : 1.0 ;
+
+				for (i = 0 ; i < len && pflac->remain > 0 ; i++)
+				{	offset = pflac->pos + i * channels ;
+
+					if (pflac->bufferpos >= frame->header.blocksize)
+						break ;
+
+					if (offset + channels > pflac->len)
+						break ;
+
+					for (j = 0 ; j < channels ; j++)
+						retpcm [offset + j] = buffer [j][pflac->bufferpos] * norm ;
+					pflac->remain -= channels ;
+					pflac->bufferpos++ ;
+					} ;
+				} ;
+			break ;
+
+		case PFLAC_PCM_DOUBLE :
+			{	double *retpcm = (double*) pflac->ptr ;
+				double norm = (psf->norm_double == SF_TRUE) ? 1.0 / (1 << (frame->header.bits_per_sample - 1)) : 1.0 ;
+
+				for (i = 0 ; i < len && pflac->remain > 0 ; i++)
+				{	offset = pflac->pos + i * channels ;
+
+					if (pflac->bufferpos >= frame->header.blocksize)
+						break ;
+
+					if (offset + channels > pflac->len)
+						break ;
+
+					for (j = 0 ; j < channels ; j++)
+						retpcm [offset + j] = buffer [j][pflac->bufferpos] * norm ;
+					pflac->remain -= channels ;
+					pflac->bufferpos++ ;
+					} ;
+				} ;
+			break ;
+
+		default :
+			return 0 ;
+		} ;
+
+	offset = i * channels ;
+	pflac->pos += i * channels ;
+
+	return offset ;
+} /* flac_buffer_copy */
+
+
+static FLAC__StreamDecoderReadStatus
+sf_flac_read_callback (const FLAC__StreamDecoder * UNUSED (decoder), FLAC__byte buffer [], size_t *bytes, void *client_data)
+{	SF_PRIVATE *psf = (SF_PRIVATE*) client_data ;
+
+	*bytes = psf_fread (buffer, 1, *bytes, psf) ;
+	if (*bytes > 0 && psf->error == 0)
+		return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE ;
+
+	return FLAC__STREAM_DECODER_READ_STATUS_ABORT ;
+} /* sf_flac_read_callback */
+
+static FLAC__StreamDecoderSeekStatus
+sf_flac_seek_callback (const FLAC__StreamDecoder * UNUSED (decoder), FLAC__uint64 absolute_byte_offset, void *client_data)
+{	SF_PRIVATE *psf = (SF_PRIVATE*) client_data ;
+
+	psf_fseek (psf, absolute_byte_offset, SEEK_SET) ;
+	if (psf->error)
+		return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR ;
+
+	return FLAC__STREAM_DECODER_SEEK_STATUS_OK ;
+} /* sf_flac_seek_callback */
+
+static FLAC__StreamDecoderTellStatus
+sf_flac_tell_callback (const FLAC__StreamDecoder * UNUSED (decoder), FLAC__uint64 *absolute_byte_offset, void *client_data)
+{	SF_PRIVATE *psf = (SF_PRIVATE*) client_data ;
+
+	*absolute_byte_offset = psf_ftell (psf) ;
+	if (psf->error)
+		return FLAC__STREAM_DECODER_TELL_STATUS_ERROR ;
+
+	return FLAC__STREAM_DECODER_TELL_STATUS_OK ;
+} /* sf_flac_tell_callback */
+
+static FLAC__StreamDecoderLengthStatus
+sf_flac_length_callback (const FLAC__StreamDecoder * UNUSED (decoder), FLAC__uint64 *stream_length, void *client_data)
+{	SF_PRIVATE *psf = (SF_PRIVATE*) client_data ;
+
+	if ((*stream_length = psf->filelength) == 0)
+		return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR ;
+
+	return FLAC__STREAM_DECODER_LENGTH_STATUS_OK ;
+} /* sf_flac_length_callback */
+
+static FLAC__bool
+sf_flac_eof_callback (const FLAC__StreamDecoder *UNUSED (decoder), void *client_data)
+{	SF_PRIVATE *psf = (SF_PRIVATE*) client_data ;
+
+	if (psf_ftell (psf) == psf->filelength)
+		return SF_TRUE ;
+
+	return SF_FALSE ;
+} /* sf_flac_eof_callback */
+
+static FLAC__StreamDecoderWriteStatus
+sf_flac_write_callback (const FLAC__StreamDecoder * UNUSED (decoder), const FLAC__Frame *frame, const int32_t * const buffer [], void *client_data)
+{	SF_PRIVATE *psf = (SF_PRIVATE*) client_data ;
+	FLAC_PRIVATE* pflac = (FLAC_PRIVATE*) psf->codec_data ;
+
+	pflac->frame = frame ;
+	pflac->bufferpos = 0 ;
+
+	pflac->wbuffer = buffer ;
+
+	flac_buffer_copy (psf) ;
+
+	return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE ;
+} /* sf_flac_write_callback */
+
+static void
+sf_flac_meta_get_vorbiscomments (SF_PRIVATE *psf, const FLAC__StreamMetadata *metadata)
+{	static FLAC_TAG tags [] =
+	{ 	{ "title", SF_STR_TITLE },
+		{ "copyright", SF_STR_COPYRIGHT },
+		{ "software", SF_STR_SOFTWARE },
+		{ "artist", SF_STR_ARTIST },
+		{ "comment", SF_STR_COMMENT },
+		{ "date", SF_STR_DATE },
+		{ "album", SF_STR_ALBUM },
+		{ "license", SF_STR_LICENSE },
+		{ "tracknumber", SF_STR_TRACKNUMBER },
+		{ "genre", SF_STR_GENRE }
+		} ;
+
+	const char *value, *cptr ;
+	int k, tag_num ;
+
+	for (k = 0 ; k < ARRAY_LEN (tags) ; k++)
+	{	tag_num = FLAC__metadata_object_vorbiscomment_find_entry_from (metadata, 0, tags [k].tag) ;
+
+		if (tag_num < 0)
+			continue ;
+
+		value = (const char*) metadata->data.vorbis_comment.comments [tag_num].entry ;
+		if ((cptr = strchr (value, '=')) != NULL)
+			value = cptr + 1 ;
+
+		psf_log_printf (psf, "  %-12s : %s\n", tags [k].tag, value) ;
+		psf_store_string (psf, tags [k].type, value) ;
+		} ;
+
+	return ;
+} /* sf_flac_meta_get_vorbiscomments */
+
+static void
+sf_flac_meta_callback (const FLAC__StreamDecoder * UNUSED (decoder), const FLAC__StreamMetadata *metadata, void *client_data)
+{	SF_PRIVATE *psf = (SF_PRIVATE*) client_data ;
+	int bitwidth = 0 ;
+
+	switch (metadata->type)
+	{	case FLAC__METADATA_TYPE_STREAMINFO :
+			if (psf->sf.channels > 0 && psf->sf.channels != (int) metadata->data.stream_info.channels)
+			{	psf_log_printf (psf, "Error: FLAC stream changed from %d to %d channels\n"
+									"Nothing to do but to error out.\n" ,
+									psf->sf.channels, metadata->data.stream_info.channels) ;
+				psf->error = SFE_FLAC_CHANNEL_COUNT_CHANGED ;
+				return ;
+				} ;
+
+			if (psf->sf.channels > 0 && psf->sf.samplerate != (int) metadata->data.stream_info.sample_rate)
+			{	psf_log_printf (psf, "Warning: FLAC stream changed sample rates from %d to %d.\n"
+									"Carrying on as if nothing happened.",
+									psf->sf.samplerate, metadata->data.stream_info.sample_rate) ;
+				} ;
+			psf->sf.channels = metadata->data.stream_info.channels ;
+			psf->sf.samplerate = metadata->data.stream_info.sample_rate ;
+			psf->sf.frames = metadata->data.stream_info.total_samples ;
+
+			psf_log_printf (psf, "FLAC Stream Metadata\n  Channels    : %d\n  Sample rate : %d\n", psf->sf.channels, psf->sf.samplerate) ;
+
+			if (psf->sf.frames == 0)
+			{	psf_log_printf (psf, "  Frames      : 0 (bumping to SF_COUNT_MAX)\n") ;
+				psf->sf.frames = SF_COUNT_MAX ;
+				}
+			else
+				psf_log_printf (psf, "  Frames      : %D\n", psf->sf.frames) ;
+
+			switch (metadata->data.stream_info.bits_per_sample)
+			{	case 8 :
+					psf->sf.format |= SF_FORMAT_PCM_S8 ;
+					bitwidth = 8 ;
+					break ;
+				case 16 :
+					psf->sf.format |= SF_FORMAT_PCM_16 ;
+					bitwidth = 16 ;
+					break ;
+				case 24 :
+					psf->sf.format |= SF_FORMAT_PCM_24 ;
+					bitwidth = 24 ;
+					break ;
+				default :
+					psf_log_printf (psf, "sf_flac_meta_callback : bits_per_sample %d not yet implemented.\n", metadata->data.stream_info.bits_per_sample) ;
+					break ;
+				} ;
+
+			if (bitwidth > 0)
+				psf_log_printf (psf, "  Bit width   : %d\n", bitwidth) ;
+			break ;
+
+		case FLAC__METADATA_TYPE_VORBIS_COMMENT :
+			psf_log_printf (psf, "Vorbis Comment Metadata\n") ;
+			sf_flac_meta_get_vorbiscomments (psf, metadata) ;
+			break ;
+
+		case FLAC__METADATA_TYPE_PADDING :
+			psf_log_printf (psf, "Padding Metadata\n") ;
+			break ;
+
+		case FLAC__METADATA_TYPE_APPLICATION :
+			psf_log_printf (psf, "Application Metadata\n") ;
+			break ;
+
+		case FLAC__METADATA_TYPE_SEEKTABLE :
+			psf_log_printf (psf, "Seektable Metadata\n") ;
+			break ;
+
+		case FLAC__METADATA_TYPE_CUESHEET :
+			psf_log_printf (psf, "Cuesheet Metadata\n") ;
+			break ;
+
+		case FLAC__METADATA_TYPE_PICTURE :
+			psf_log_printf (psf, "Picture Metadata\n") ;
+			break ;
+
+		case FLAC__METADATA_TYPE_UNDEFINED :
+			psf_log_printf (psf, "Undefined Metadata\n") ;
+			break ;
+
+		default :
+			psf_log_printf (psf, "sf_flac_meta_callback : metadata-type %d not yet implemented.\n", metadata->type) ;
+			break ;
+		} ;
+
+	return ;
+} /* sf_flac_meta_callback */
+
+static void
+sf_flac_error_callback (const FLAC__StreamDecoder * UNUSED (decoder), FLAC__StreamDecoderErrorStatus status, void *client_data)
+{	SF_PRIVATE *psf = (SF_PRIVATE*) client_data ;
+
+	psf_log_printf (psf, "ERROR : %s\n", FLAC__StreamDecoderErrorStatusString [status]) ;
+
+	switch (status)
+	{	case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC :
+			psf->error = SFE_FLAC_LOST_SYNC ;
+			break ;
+		case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER :
+			psf->error = SFE_FLAC_BAD_HEADER ;
+			break ;
+		default :
+			psf->error = SFE_FLAC_UNKOWN_ERROR ;
+			break ;
+		} ;
+
+	return ;
+} /* sf_flac_error_callback */
+
+static FLAC__StreamEncoderSeekStatus
+sf_flac_enc_seek_callback (const FLAC__StreamEncoder * UNUSED (encoder), FLAC__uint64 absolute_byte_offset, void *client_data)
+{	SF_PRIVATE *psf = (SF_PRIVATE*) client_data ;
+
+	psf_fseek (psf, absolute_byte_offset, SEEK_SET) ;
+	if (psf->error)
+		return FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR ;
+
+	return FLAC__STREAM_ENCODER_SEEK_STATUS_OK ;
+} /* sf_flac_enc_seek_callback */
+
+static FLAC__StreamEncoderTellStatus
+sf_flac_enc_tell_callback (const FLAC__StreamEncoder *UNUSED (encoder), FLAC__uint64 *absolute_byte_offset, void *client_data)
+{	SF_PRIVATE *psf = (SF_PRIVATE*) client_data ;
+
+	*absolute_byte_offset = psf_ftell (psf) ;
+	if (psf->error)
+		return FLAC__STREAM_ENCODER_TELL_STATUS_ERROR ;
+
+	return FLAC__STREAM_ENCODER_TELL_STATUS_OK ;
+} /* sf_flac_enc_tell_callback */
+
+static FLAC__StreamEncoderWriteStatus
+sf_flac_enc_write_callback (const FLAC__StreamEncoder * UNUSED (encoder), const FLAC__byte buffer [], size_t bytes, unsigned UNUSED (samples), unsigned UNUSED (current_frame), void *client_data)
+{	SF_PRIVATE *psf = (SF_PRIVATE*) client_data ;
+
+	if (psf_fwrite (buffer, 1, bytes, psf) == (sf_count_t) bytes && psf->error == 0)
+		return FLAC__STREAM_ENCODER_WRITE_STATUS_OK ;
+
+	return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR ;
+} /* sf_flac_enc_write_callback */
+
+static void
+flac_write_strings (SF_PRIVATE *psf, FLAC_PRIVATE* pflac)
+{	FLAC__StreamMetadata_VorbisComment_Entry entry ;
+	int	k, string_count = 0 ;
+
+	for (k = 0 ; k < SF_MAX_STRINGS ; k++)
+	{	if (psf->strings.data [k].type != 0)
+			string_count ++ ;
+		} ;
+
+	if (string_count == 0)
+		return ;
+
+	if (pflac->metadata == NULL && (pflac->metadata = FLAC__metadata_object_new (FLAC__METADATA_TYPE_VORBIS_COMMENT)) == NULL)
+	{	psf_log_printf (psf, "FLAC__metadata_object_new returned NULL\n") ;
+		return ;
+		} ;
+
+	for (k = 0 ; k < SF_MAX_STRINGS && psf->strings.data [k].type != 0 ; k++)
+	{	const char * key, * value ;
+
+		switch (psf->strings.data [k].type)
+		{	case SF_STR_SOFTWARE :
+				key = "software" ;
+				break ;
+			case SF_STR_TITLE :
+				key = "title" ;
+				break ;
+			case SF_STR_COPYRIGHT :
+				key = "copyright" ;
+				break ;
+			case SF_STR_ARTIST :
+				key = "artist" ;
+				break ;
+			case SF_STR_COMMENT :
+				key = "comment" ;
+				break ;
+			case SF_STR_DATE :
+				key = "date" ;
+				break ;
+			case SF_STR_ALBUM :
+				key = "album" ;
+				break ;
+			case SF_STR_LICENSE :
+				key = "license" ;
+				break ;
+			case SF_STR_TRACKNUMBER :
+				key = "tracknumber" ;
+				break ;
+			case SF_STR_GENRE :
+				key = "genre" ;
+				break ;
+			default :
+				continue ;
+			} ;
+
+		value = psf->strings.storage + psf->strings.data [k].offset ;
+
+		FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair (&entry, key, value) ;
+		FLAC__metadata_object_vorbiscomment_append_comment (pflac->metadata, entry, /* copy */ SF_FALSE) ;
+		} ;
+
+	if (! FLAC__stream_encoder_set_metadata (pflac->fse, &pflac->metadata, 1))
+	{	printf ("%s %d : fail\n", __func__, __LINE__) ;
+		return ;
+		} ;
+
+	return ;
+} /* flac_write_strings */
+
+static int
+flac_write_header (SF_PRIVATE *psf, int UNUSED (calc_length))
+{	FLAC_PRIVATE* pflac = (FLAC_PRIVATE*) psf->codec_data ;
+	int err ;
+
+	flac_write_strings (psf, pflac) ;
+
+	if ((err = FLAC__stream_encoder_init_stream (pflac->fse, sf_flac_enc_write_callback, sf_flac_enc_seek_callback, sf_flac_enc_tell_callback, NULL, psf)) != FLAC__STREAM_DECODER_INIT_STATUS_OK)
+	{	psf_log_printf (psf, "Error : FLAC encoder init returned error : %s\n", FLAC__StreamEncoderInitStatusString [err]) ;
+		return SFE_FLAC_INIT_DECODER ;
+		} ;
+
+	if (psf->error == 0)
+		psf->dataoffset = psf_ftell (psf) ;
+	pflac->encbuffer = calloc (ENC_BUFFER_SIZE, sizeof (int32_t)) ;
+
+	/* can only call init_stream once */
+	psf->write_header = NULL ;
+
+	return psf->error ;
+} /* flac_write_header */
+
+/*------------------------------------------------------------------------------
+** Public function.
+*/
+
+int
+flac_open	(SF_PRIVATE *psf)
+{	int		subformat ;
+	int		error = 0 ;
+
+	FLAC_PRIVATE* pflac = calloc (1, sizeof (FLAC_PRIVATE)) ;
+	psf->codec_data = pflac ;
+
+	/* Set the default value here. Over-ridden later if necessary. */
+	pflac->compression = FLAC_DEFAULT_COMPRESSION_LEVEL ;
+
+	if (psf->file.mode == SFM_RDWR)
+		return SFE_BAD_MODE_RW ;
+
+	if (psf->file.mode == SFM_READ)
+	{	if ((error = flac_read_header (psf)))
+			return error ;
+		} ;
+
+	subformat = SF_CODEC (psf->sf.format) ;
+
+	if (psf->file.mode == SFM_WRITE)
+	{	if ((SF_CONTAINER (psf->sf.format)) != SF_FORMAT_FLAC)
+			return	SFE_BAD_OPEN_FORMAT ;
+
+		psf->endian = SF_ENDIAN_BIG ;
+		psf->sf.seekable = 0 ;
+
+		psf->strings.flags = SF_STR_ALLOW_START ;
+
+		if ((error = flac_enc_init (psf)))
+			return error ;
+
+		/* In an ideal world we would write the header at this point. Unfortunately
+		** that would prevent string metadata being added so we have to hold off.
+		*/
+
+		psf->write_header = flac_write_header ;
+		} ;
+
+	psf->datalength = psf->filelength ;
+	psf->dataoffset = 0 ;
+
+	psf->container_close = flac_close ;
+	psf->seek = flac_seek ;
+	psf->byterate = flac_byterate ;
+
+	psf->command = flac_command ;
+
+	switch (subformat)
+	{	case SF_FORMAT_PCM_S8 :	/* 8-bit FLAC.  */
+		case SF_FORMAT_PCM_16 :	/* 16-bit FLAC. */
+		case SF_FORMAT_PCM_24 :	/* 24-bit FLAC. */
+			error = flac_init (psf) ;
+			break ;
+
+		default : return SFE_UNIMPLEMENTED ;
+		} ;
+
+	return error ;
+} /* flac_open */
+
+/*------------------------------------------------------------------------------
+*/
+
+static int
+flac_close	(SF_PRIVATE *psf)
+{	FLAC_PRIVATE* pflac ;
+	int k ;
+
+	if ((pflac = (FLAC_PRIVATE*) psf->codec_data) == NULL)
+		return 0 ;
+
+	if (pflac->metadata != NULL)
+		FLAC__metadata_object_delete (pflac->metadata) ;
+
+	if (psf->file.mode == SFM_WRITE)
+	{	FLAC__stream_encoder_finish (pflac->fse) ;
+		FLAC__stream_encoder_delete (pflac->fse) ;
+		free (pflac->encbuffer) ;
+		} ;
+
+	if (psf->file.mode == SFM_READ)
+	{	FLAC__stream_decoder_finish (pflac->fsd) ;
+		FLAC__stream_decoder_delete (pflac->fsd) ;
+		} ;
+
+	for (k = 0 ; k < ARRAY_LEN (pflac->rbuffer) ; k++)
+		free (pflac->rbuffer [k]) ;
+
+	free (pflac) ;
+	psf->codec_data = NULL ;
+
+	return 0 ;
+} /* flac_close */
+
+static int
+flac_enc_init (SF_PRIVATE *psf)
+{	FLAC_PRIVATE* pflac = (FLAC_PRIVATE*) psf->codec_data ;
+	unsigned bps ;
+
+	/* To cite the flac FAQ at
+	** http://flac.sourceforge.net/faq.html#general__samples
+	**     "FLAC supports linear sample rates from 1Hz - 655350Hz in 1Hz
+	**     increments."
+	*/
+	if (psf->sf.samplerate < 1 || psf->sf.samplerate > 655350)
+	{	psf_log_printf (psf, "flac sample rate out of range.\n", psf->sf.samplerate) ;
+		return SFE_FLAC_BAD_SAMPLE_RATE ;
+		} ;
+
+	psf_fseek (psf, 0, SEEK_SET) ;
+
+	switch (SF_CODEC (psf->sf.format))
+	{	case SF_FORMAT_PCM_S8 :
+			bps = 8 ;
+			break ;
+		case SF_FORMAT_PCM_16 :
+			bps = 16 ;
+			break ;
+		case SF_FORMAT_PCM_24 :
+			bps = 24 ;
+			break ;
+
+		default :
+			bps = 0 ;
+			break ;
+		} ;
+
+	if (pflac->fse)
+		FLAC__stream_encoder_delete (pflac->fse) ;
+	if ((pflac->fse = FLAC__stream_encoder_new ()) == NULL)
+		return SFE_FLAC_NEW_DECODER ;
+
+	if (! FLAC__stream_encoder_set_channels (pflac->fse, psf->sf.channels))
+	{	psf_log_printf (psf, "FLAC__stream_encoder_set_channels (%d) return false.\n", psf->sf.channels) ;
+		return SFE_FLAC_INIT_DECODER ;
+		} ;
+
+	if (! FLAC__stream_encoder_set_sample_rate (pflac->fse, psf->sf.samplerate))
+	{	psf_log_printf (psf, "FLAC__stream_encoder_set_sample_rate (%d) returned false.\n", psf->sf.samplerate) ;
+		return SFE_FLAC_BAD_SAMPLE_RATE ;
+		} ;
+
+	if (! FLAC__stream_encoder_set_bits_per_sample (pflac->fse, bps))
+	{	psf_log_printf (psf, "FLAC__stream_encoder_set_bits_per_sample (%d) return false.\n", bps) ;
+		return SFE_FLAC_INIT_DECODER ;
+		} ;
+
+	if (! FLAC__stream_encoder_set_compression_level (pflac->fse, pflac->compression))
+	{	psf_log_printf (psf, "FLAC__stream_encoder_set_compression_level (%d) return false.\n", pflac->compression) ;
+		return SFE_FLAC_INIT_DECODER ;
+		} ;
+
+	return 0 ;
+} /* flac_enc_init */
+
+static int
+flac_read_header (SF_PRIVATE *psf)
+{	FLAC_PRIVATE* pflac = (FLAC_PRIVATE*) psf->codec_data ;
+
+	psf_fseek (psf, 0, SEEK_SET) ;
+	if (pflac->fsd)
+		FLAC__stream_decoder_delete (pflac->fsd) ;
+	if ((pflac->fsd = FLAC__stream_decoder_new ()) == NULL)
+		return SFE_FLAC_NEW_DECODER ;
+
+	FLAC__stream_decoder_set_metadata_respond_all (pflac->fsd) ;
+
+	if (FLAC__stream_decoder_init_stream (pflac->fsd, sf_flac_read_callback, sf_flac_seek_callback, sf_flac_tell_callback, sf_flac_length_callback, sf_flac_eof_callback, sf_flac_write_callback, sf_flac_meta_callback, sf_flac_error_callback, psf) != FLAC__STREAM_DECODER_INIT_STATUS_OK)
+		return SFE_FLAC_INIT_DECODER ;
+
+	FLAC__stream_decoder_process_until_end_of_metadata (pflac->fsd) ;
+
+	psf_log_printf (psf, "End\n") ;
+
+	if (psf->error != 0)
+		FLAC__stream_decoder_delete (pflac->fsd) ;
+	else
+	{	FLAC__uint64 position ;
+
+		FLAC__stream_decoder_get_decode_position (pflac->fsd, &position) ;
+		psf->dataoffset = position ;
+		} ;
+
+	return psf->error ;
+} /* flac_read_header */
+
+static int
+flac_command (SF_PRIVATE * psf, int command, void * data, int datasize)
+{	FLAC_PRIVATE* pflac = (FLAC_PRIVATE*) psf->codec_data ;
+	double quality ;
+
+	switch (command)
+	{	case SFC_SET_COMPRESSION_LEVEL :
+			if (data == NULL || datasize != sizeof (double))
+				return SF_FALSE ;
+
+			if (psf->have_written)
+				return SF_FALSE ;
+
+			/* FLAC compression level is in the range [0, 8] while libsndfile takes
+			** values in the range [0.0, 1.0]. Massage the libsndfile value here.
+			*/
+			quality = (*((double *) data)) * 8.0 ;
+			/* Clip range. */
+			pflac->compression = psf_lrint (SF_MAX (0.0, SF_MIN (8.0, quality))) ;
+
+			psf_log_printf (psf, "%s : Setting SFC_SET_COMPRESSION_LEVEL to %u.\n", __func__, pflac->compression) ;
+
+			if (flac_enc_init (psf))
+				return SF_FALSE ;
+
+			return SF_TRUE ;
+
+		default :
+			return SF_FALSE ;
+		} ;
+
+	return SF_FALSE ;
+} /* flac_command */
+
+int
+flac_init (SF_PRIVATE *psf)
+{
+	if (psf->file.mode == SFM_RDWR)
+		return SFE_BAD_MODE_RW ;
+
+	if (psf->file.mode == SFM_READ)
+	{	psf->read_short		= flac_read_flac2s ;
+		psf->read_int		= flac_read_flac2i ;
+		psf->read_float		= flac_read_flac2f ;
+		psf->read_double	= flac_read_flac2d ;
+		} ;
+
+	if (psf->file.mode == SFM_WRITE)
+	{	psf->write_short	= flac_write_s2flac ;
+		psf->write_int		= flac_write_i2flac ;
+		psf->write_float	= flac_write_f2flac ;
+		psf->write_double	= flac_write_d2flac ;
+		} ;
+
+	if (psf->filelength > psf->dataoffset)
+		psf->datalength = (psf->dataend) ? psf->dataend - psf->dataoffset : psf->filelength - psf->dataoffset ;
+	else
+		psf->datalength = 0 ;
+
+	return 0 ;
+} /* flac_init */
+
+static unsigned
+flac_read_loop (SF_PRIVATE *psf, unsigned len)
+{	FLAC_PRIVATE* pflac = (FLAC_PRIVATE*) psf->codec_data ;
+	FLAC__StreamDecoderState state ;
+
+	pflac->pos = 0 ;
+	pflac->len = len ;
+	pflac->remain = len ;
+
+	state = FLAC__stream_decoder_get_state (pflac->fsd) ;
+	if (state > FLAC__STREAM_DECODER_END_OF_STREAM)
+	{	psf_log_printf (psf, "FLAC__stream_decoder_get_state returned %s\n", FLAC__StreamDecoderStateString [state]) ;
+		/* Current frame is busted, so NULL the pointer. */
+		pflac->frame = NULL ;
+		} ;
+
+	/* First copy data that has already been decoded and buffered. */
+	if (pflac->frame != NULL && pflac->bufferpos < pflac->frame->header.blocksize)
+		flac_buffer_copy (psf) ;
+
+	/* Decode some more. */
+	while (pflac->pos < pflac->len)
+	{	if (FLAC__stream_decoder_process_single (pflac->fsd) == 0)
+		{	psf_log_printf (psf, "FLAC__stream_decoder_process_single returned false\n") ;
+			/* Current frame is busted, so NULL the pointer. */
+			pflac->frame = NULL ;
+			break ;
+			} ;
+		state = FLAC__stream_decoder_get_state (pflac->fsd) ;
+		if (state >= FLAC__STREAM_DECODER_END_OF_STREAM)
+		{	psf_log_printf (psf, "FLAC__stream_decoder_get_state returned %s\n", FLAC__StreamDecoderStateString [state]) ;
+			/* Current frame is busted, so NULL the pointer. */
+			pflac->frame = NULL ;
+			break ;
+			} ;
+		} ;
+
+	pflac->ptr = NULL ;
+
+	return pflac->pos ;
+} /* flac_read_loop */
+
+static sf_count_t
+flac_read_flac2s (SF_PRIVATE *psf, short *ptr, sf_count_t len)
+{	FLAC_PRIVATE* pflac = (FLAC_PRIVATE*) psf->codec_data ;
+	sf_count_t total = 0, current ;
+	unsigned readlen ;
+
+	pflac->pcmtype = PFLAC_PCM_SHORT ;
+
+	while (total < len)
+	{	pflac->ptr = ptr + total ;
+		readlen = (len - total > READ_LOOP_MAX_LEN) ? READ_LOOP_MAX_LEN : (unsigned) (len - total) ;
+		current = flac_read_loop (psf, readlen) ;
+		if (current == 0)
+			break ;
+		total += current ;
+		} ;
+
+	return total ;
+} /* flac_read_flac2s */
+
+static sf_count_t
+flac_read_flac2i (SF_PRIVATE *psf, int *ptr, sf_count_t len)
+{	FLAC_PRIVATE* pflac = (FLAC_PRIVATE*) psf->codec_data ;
+	sf_count_t total = 0, current ;
+	unsigned readlen ;
+
+	pflac->pcmtype = PFLAC_PCM_INT ;
+
+	while (total < len)
+	{	pflac->ptr = ptr + total ;
+		readlen = (len - total > READ_LOOP_MAX_LEN) ? READ_LOOP_MAX_LEN : (unsigned) (len - total) ;
+		current = flac_read_loop (psf, readlen) ;
+		if (current == 0)
+			break ;
+		total += current ;
+		} ;
+
+	return total ;
+} /* flac_read_flac2i */
+
+static sf_count_t
+flac_read_flac2f (SF_PRIVATE *psf, float *ptr, sf_count_t len)
+{	FLAC_PRIVATE* pflac = (FLAC_PRIVATE*) psf->codec_data ;
+	sf_count_t total = 0, current ;
+	unsigned readlen ;
+
+	pflac->pcmtype = PFLAC_PCM_FLOAT ;
+
+	while (total < len)
+	{	pflac->ptr = ptr + total ;
+		readlen = (len - total > READ_LOOP_MAX_LEN) ? READ_LOOP_MAX_LEN : (unsigned) (len - total) ;
+		current = flac_read_loop (psf, readlen) ;
+		if (current == 0)
+			break ;
+		total += current ;
+		} ;
+
+	return total ;
+} /* flac_read_flac2f */
+
+static sf_count_t
+flac_read_flac2d (SF_PRIVATE *psf, double *ptr, sf_count_t len)
+{	FLAC_PRIVATE* pflac = (FLAC_PRIVATE*) psf->codec_data ;
+	sf_count_t total = 0, current ;
+	unsigned readlen ;
+
+	pflac->pcmtype = PFLAC_PCM_DOUBLE ;
+
+	while (total < len)
+	{	pflac->ptr = ptr + total ;
+		readlen = (len - total > READ_LOOP_MAX_LEN) ? READ_LOOP_MAX_LEN : (unsigned) (len - total) ;
+
+		current = flac_read_loop (psf, readlen) ;
+		if (current == 0)
+			break ;
+		total += current ;
+		} ;
+
+	return total ;
+} /* flac_read_flac2d */
+
+static sf_count_t
+flac_write_s2flac (SF_PRIVATE *psf, const short *ptr, sf_count_t len)
+{	FLAC_PRIVATE* pflac = (FLAC_PRIVATE*) psf->codec_data ;
+	void (*convert) (const short *, int32_t *, int) ;
+	int bufferlen, writecount, thiswrite ;
+	sf_count_t	total = 0 ;
+	int32_t* buffer = pflac->encbuffer ;
+
+	switch (SF_CODEC (psf->sf.format))
+	{	case SF_FORMAT_PCM_S8 :
+			convert = s2flac8_array ;
+			break ;
+		case SF_FORMAT_PCM_16 :
+			convert = s2flac16_array ;
+			break ;
+		case SF_FORMAT_PCM_24 :
+			convert = s2flac24_array ;
+			break ;
+		default :
+			return -1 ;
+		} ;
+
+	bufferlen = ENC_BUFFER_SIZE / (sizeof (int32_t) * psf->sf.channels) ;
+	bufferlen *= psf->sf.channels ;
+
+	while (len > 0)
+	{	writecount = (len >= bufferlen) ? bufferlen : (int) len ;
+		convert (ptr + total, buffer, writecount) ;
+		if (FLAC__stream_encoder_process_interleaved (pflac->fse, buffer, writecount / psf->sf.channels))
+			thiswrite = writecount ;
+		else
+			break ;
+		total += thiswrite ;
+		if (thiswrite < writecount)
+			break ;
+
+		len -= thiswrite ;
+		} ;
+
+	return total ;
+} /* flac_write_s2flac */
+
+static sf_count_t
+flac_write_i2flac (SF_PRIVATE *psf, const int *ptr, sf_count_t len)
+{	FLAC_PRIVATE* pflac = (FLAC_PRIVATE*) psf->codec_data ;
+	void (*convert) (const int *, int32_t *, int) ;
+	int bufferlen, writecount, thiswrite ;
+	sf_count_t	total = 0 ;
+	int32_t* buffer = pflac->encbuffer ;
+
+	switch (SF_CODEC (psf->sf.format))
+	{	case SF_FORMAT_PCM_S8 :
+			convert = i2flac8_array ;
+			break ;
+		case SF_FORMAT_PCM_16 :
+			convert = i2flac16_array ;
+			break ;
+		case SF_FORMAT_PCM_24 :
+			convert = i2flac24_array ;
+			break ;
+		default :
+			return -1 ;
+		} ;
+
+	bufferlen = ENC_BUFFER_SIZE / (sizeof (int32_t) * psf->sf.channels) ;
+	bufferlen *= psf->sf.channels ;
+
+	while (len > 0)
+	{	writecount = (len >= bufferlen) ? bufferlen : (int) len ;
+		convert (ptr + total, buffer, writecount) ;
+		if (FLAC__stream_encoder_process_interleaved (pflac->fse, buffer, writecount / psf->sf.channels))
+			thiswrite = writecount ;
+		else
+			break ;
+		total += thiswrite ;
+		if (thiswrite < writecount)
+			break ;
+
+		len -= thiswrite ;
+		} ;
+
+	return total ;
+} /* flac_write_i2flac */
+
+static sf_count_t
+flac_write_f2flac (SF_PRIVATE *psf, const float *ptr, sf_count_t len)
+{	FLAC_PRIVATE* pflac = (FLAC_PRIVATE*) psf->codec_data ;
+	void (*convert) (const float *, int32_t *, int, int) ;
+	int bufferlen, writecount, thiswrite ;
+	sf_count_t	total = 0 ;
+	int32_t* buffer = pflac->encbuffer ;
+
+	switch (SF_CODEC (psf->sf.format))
+	{	case SF_FORMAT_PCM_S8 :
+			convert = (psf->add_clipping) ? f2flac8_clip_array : f2flac8_array ;
+			break ;
+		case SF_FORMAT_PCM_16 :
+			convert = (psf->add_clipping) ? f2flac16_clip_array : f2flac16_array ;
+			break ;
+		case SF_FORMAT_PCM_24 :
+			convert = (psf->add_clipping) ? f2flac24_clip_array : f2flac24_array ;
+			break ;
+		default :
+			return -1 ;
+		} ;
+
+	bufferlen = ENC_BUFFER_SIZE / (sizeof (int32_t) * psf->sf.channels) ;
+	bufferlen *= psf->sf.channels ;
+
+	while (len > 0)
+	{	writecount = (len >= bufferlen) ? bufferlen : (int) len ;
+		convert (ptr + total, buffer, writecount, psf->norm_float) ;
+		if (FLAC__stream_encoder_process_interleaved (pflac->fse, buffer, writecount / psf->sf.channels))
+			thiswrite = writecount ;
+		else
+			break ;
+		total += thiswrite ;
+		if (thiswrite < writecount)
+			break ;
+
+		len -= thiswrite ;
+		} ;
+
+	return total ;
+} /* flac_write_f2flac */
+
+static void
+f2flac8_clip_array (const float *src, int32_t *dest, int count, int normalize)
+{	float normfact, scaled_value ;
+
+	normfact = normalize ? (8.0 * 0x10) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	scaled_value = src [i] * normfact ;
+		if (scaled_value >= (1.0 * 0x7F))
+		{	dest [i] = 0x7F ;
+			continue ;
+			} ;
+		if (scaled_value <= (-8.0 * 0x10))
+		{	dest [i] = -0x80 ;
+			continue ;
+			} ;
+		dest [i] = psf_lrintf (scaled_value) ;
+		} ;
+
+	return ;
+} /* f2flac8_clip_array */
+
+static void
+f2flac16_clip_array (const float *src, int32_t *dest, int count, int normalize)
+{	float normfact, scaled_value ;
+
+	normfact = normalize ? (8.0 * 0x1000) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	scaled_value = src [i] * normfact ;
+		if (scaled_value >= (1.0 * 0x7FFF))
+		{	dest [i] = 0x7FFF ;
+			continue ;
+			} ;
+		if (scaled_value <= (-8.0 * 0x1000))
+		{	dest [i] = -0x8000 ;
+			continue ;
+			} ;
+		dest [i] = psf_lrintf (scaled_value) ;
+		} ;
+} /* f2flac16_clip_array */
+
+static void
+f2flac24_clip_array (const float *src, int32_t *dest, int count, int normalize)
+{	float normfact, scaled_value ;
+
+	normfact = normalize ? (8.0 * 0x100000) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	scaled_value = src [i] * normfact ;
+		if (scaled_value >= (1.0 * 0x7FFFFF))
+		{	dest [i] = 0x7FFFFF ;
+			continue ;
+			} ;
+
+		if (scaled_value <= (-8.0 * 0x100000))
+		{	dest [i] = -0x800000 ;
+			continue ;
+			}
+		dest [i] = psf_lrintf (scaled_value) ;
+		} ;
+
+	return ;
+} /* f2flac24_clip_array */
+
+static void
+f2flac8_array (const float *src, int32_t *dest, int count, int normalize)
+{	float normfact = normalize ? (1.0 * 0x7F) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+		dest [i] = psf_lrintf (src [i] * normfact) ;
+} /* f2flac8_array */
+
+static void
+f2flac16_array (const float *src, int32_t *dest, int count, int normalize)
+{	float normfact = normalize ? (1.0 * 0x7FFF) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+		dest [i] = psf_lrintf (src [i] * normfact) ;
+} /* f2flac16_array */
+
+static void
+f2flac24_array (const float *src, int32_t *dest, int count, int normalize)
+{	float normfact = normalize ? (1.0 * 0x7FFFFF) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+		dest [i] = psf_lrintf (src [i] * normfact) ;
+} /* f2flac24_array */
+
+static sf_count_t
+flac_write_d2flac (SF_PRIVATE *psf, const double *ptr, sf_count_t len)
+{	FLAC_PRIVATE* pflac = (FLAC_PRIVATE*) psf->codec_data ;
+	void (*convert) (const double *, int32_t *, int, int) ;
+	int bufferlen, writecount, thiswrite ;
+	sf_count_t	total = 0 ;
+	int32_t* buffer = pflac->encbuffer ;
+
+	switch (SF_CODEC (psf->sf.format))
+	{	case SF_FORMAT_PCM_S8 :
+			convert = (psf->add_clipping) ? d2flac8_clip_array : d2flac8_array ;
+			break ;
+		case SF_FORMAT_PCM_16 :
+			convert = (psf->add_clipping) ? d2flac16_clip_array : d2flac16_array ;
+			break ;
+		case SF_FORMAT_PCM_24 :
+			convert = (psf->add_clipping) ? d2flac24_clip_array : d2flac24_array ;
+			break ;
+		default :
+			return -1 ;
+		} ;
+
+	bufferlen = ENC_BUFFER_SIZE / (sizeof (int32_t) * psf->sf.channels) ;
+	bufferlen *= psf->sf.channels ;
+
+	while (len > 0)
+	{	writecount = (len >= bufferlen) ? bufferlen : (int) len ;
+		convert (ptr + total, buffer, writecount, psf->norm_double) ;
+		if (FLAC__stream_encoder_process_interleaved (pflac->fse, buffer, writecount / psf->sf.channels))
+			thiswrite = writecount ;
+		else
+			break ;
+		total += thiswrite ;
+		if (thiswrite < writecount)
+			break ;
+
+		len -= thiswrite ;
+		} ;
+
+	return total ;
+} /* flac_write_d2flac */
+
+static void
+d2flac8_clip_array (const double *src, int32_t *dest, int count, int normalize)
+{	double normfact, scaled_value ;
+
+	normfact = normalize ? (8.0 * 0x10) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	scaled_value = src [i] * normfact ;
+		if (scaled_value >= (1.0 * 0x7F))
+		{	dest [i] = 0x7F ;
+			continue ;
+			} ;
+		if (scaled_value <= (-8.0 * 0x10))
+		{	dest [i] = -0x80 ;
+			continue ;
+			} ;
+		dest [i] = psf_lrint (scaled_value) ;
+		} ;
+
+	return ;
+} /* d2flac8_clip_array */
+
+static void
+d2flac16_clip_array (const double *src, int32_t *dest, int count, int normalize)
+{	double normfact, scaled_value ;
+
+	normfact = normalize ? (8.0 * 0x1000) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	scaled_value = src [i] * normfact ;
+		if (scaled_value >= (1.0 * 0x7FFF))
+		{	dest [i] = 0x7FFF ;
+			continue ;
+			} ;
+		if (scaled_value <= (-8.0 * 0x1000))
+		{	dest [i] = -0x8000 ;
+			continue ;
+			} ;
+		dest [i] = psf_lrint (scaled_value) ;
+		} ;
+
+	return ;
+} /* d2flac16_clip_array */
+
+static void
+d2flac24_clip_array (const double *src, int32_t *dest, int count, int normalize)
+{	double normfact, scaled_value ;
+
+	normfact = normalize ? (8.0 * 0x100000) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	scaled_value = src [i] * normfact ;
+		if (scaled_value >= (1.0 * 0x7FFFFF))
+		{	dest [i] = 0x7FFFFF ;
+			continue ;
+			} ;
+		if (scaled_value <= (-8.0 * 0x100000))
+		{	dest [i] = -0x800000 ;
+			continue ;
+			} ;
+		dest [i] = psf_lrint (scaled_value) ;
+		} ;
+
+	return ;
+} /* d2flac24_clip_array */
+
+static void
+d2flac8_array (const double *src, int32_t *dest, int count, int normalize)
+{	double normfact = normalize ? (1.0 * 0x7F) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+		dest [i] = psf_lrint (src [i] * normfact) ;
+} /* d2flac8_array */
+
+static void
+d2flac16_array (const double *src, int32_t *dest, int count, int normalize)
+{	double normfact = normalize ? (1.0 * 0x7FFF) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+		dest [i] = psf_lrint (src [i] * normfact) ;
+} /* d2flac16_array */
+
+static void
+d2flac24_array (const double *src, int32_t *dest, int count, int normalize)
+{	double normfact = normalize ? (1.0 * 0x7FFFFF) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+		dest [i] = psf_lrint (src [i] * normfact) ;
+} /* d2flac24_array */
+
+static sf_count_t
+flac_seek (SF_PRIVATE *psf, int UNUSED (mode), sf_count_t offset)
+{	FLAC_PRIVATE* pflac = (FLAC_PRIVATE*) psf->codec_data ;
+
+	if (pflac == NULL)
+		return 0 ;
+
+	if (psf->dataoffset < 0)
+	{	psf->error = SFE_BAD_SEEK ;
+		return ((sf_count_t) -1) ;
+		} ;
+
+	pflac->frame = NULL ;
+
+	if (psf->file.mode == SFM_READ)
+	{	if (FLAC__stream_decoder_seek_absolute (pflac->fsd, offset))
+			return offset ;
+
+		if (offset == psf->sf.frames)
+		{	/*
+			** If we've been asked to seek to the very end of the file, libFLAC
+			** will return an error. However, we know the length of the file so
+			** instead of returning an error, we can return the offset.
+			*/
+			return offset ;
+			} ;
+
+		psf->error = SFE_BAD_SEEK ;
+		return ((sf_count_t) -1) ;
+		} ;
+
+	/* Seeking in write mode not yet supported. */
+	psf->error = SFE_BAD_SEEK ;
+
+	return ((sf_count_t) -1) ;
+} /* flac_seek */
+
+static int
+flac_byterate (SF_PRIVATE *psf)
+{
+	if (psf->file.mode == SFM_READ)
+		return (psf->datalength * psf->sf.samplerate) / psf->sf.frames ;
+
+	return -1 ;
+} /* flac_byterate */
+
+
+#else /* HAVE_EXTERNAL_XIPH_LIBS */
+
+int
+flac_open	(SF_PRIVATE *psf)
+{
+	psf_log_printf (psf, "This version of libsndfile was compiled without FLAC support.\n") ;
+	return SFE_UNIMPLEMENTED ;
+} /* flac_open */
+
+#endif
diff --git a/src/float32.c b/src/float32.c
new file mode 100644
index 0000000..431e39b
--- /dev/null
+++ b/src/float32.c
@@ -0,0 +1,1017 @@
+/*
+** Copyright (C) 1999-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<stdlib.h>
+#include	<string.h>
+#include	<limits.h>
+#include	<math.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+#if CPU_IS_LITTLE_ENDIAN
+	#define FLOAT32_READ	float32_le_read
+	#define FLOAT32_WRITE	float32_le_write
+#elif CPU_IS_BIG_ENDIAN
+	#define FLOAT32_READ	float32_be_read
+	#define FLOAT32_WRITE	float32_be_write
+#endif
+
+/*--------------------------------------------------------------------------------------------
+**	Processor floating point capabilities. float32_get_capability () returns one of the
+**	latter four values.
+*/
+
+enum
+{	FLOAT_UNKNOWN		= 0x00,
+	FLOAT_CAN_RW_LE		= 0x12,
+	FLOAT_CAN_RW_BE		= 0x23,
+	FLOAT_BROKEN_LE		= 0x34,
+	FLOAT_BROKEN_BE		= 0x45
+} ;
+
+/*--------------------------------------------------------------------------------------------
+**	Prototypes for private functions.
+*/
+
+static sf_count_t	host_read_f2s	(SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
+static sf_count_t	host_read_f2i	(SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
+static sf_count_t	host_read_f	(SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
+static sf_count_t	host_read_f2d	(SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
+
+static sf_count_t	host_write_s2f	(SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
+static sf_count_t	host_write_i2f	(SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
+static sf_count_t	host_write_f	(SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
+static sf_count_t	host_write_d2f	(SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
+
+static void		float32_peak_update	(SF_PRIVATE *psf, const float *buffer, int count, sf_count_t indx) ;
+
+static sf_count_t	replace_read_f2s	(SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
+static sf_count_t	replace_read_f2i	(SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
+static sf_count_t	replace_read_f	(SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
+static sf_count_t	replace_read_f2d	(SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
+
+static sf_count_t	replace_write_s2f	(SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
+static sf_count_t	replace_write_i2f	(SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
+static sf_count_t	replace_write_f	(SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
+static sf_count_t	replace_write_d2f	(SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
+
+static	void	bf2f_array (float *buffer, int count) ;
+static	void	f2bf_array (float *buffer, int count) ;
+
+static int		float32_get_capability	(SF_PRIVATE *psf) ;
+
+/*--------------------------------------------------------------------------------------------
+**	Exported functions.
+*/
+
+int
+float32_init	(SF_PRIVATE *psf)
+{	static int float_caps ;
+
+	if (psf->sf.channels < 1)
+	{	psf_log_printf (psf, "float32_init : internal error : channels = %d\n", psf->sf.channels) ;
+		return SFE_INTERNAL ;
+		} ;
+
+	float_caps = float32_get_capability (psf) ;
+
+	psf->blockwidth = sizeof (float) * psf->sf.channels ;
+
+	if (psf->file.mode == SFM_READ || psf->file.mode == SFM_RDWR)
+	{	switch (psf->endian + float_caps)
+		{	case (SF_ENDIAN_BIG + FLOAT_CAN_RW_BE) :
+					psf->data_endswap = SF_FALSE ;
+					psf->read_short		= host_read_f2s ;
+					psf->read_int		= host_read_f2i ;
+					psf->read_float		= host_read_f ;
+					psf->read_double	= host_read_f2d ;
+					break ;
+
+			case (SF_ENDIAN_LITTLE + FLOAT_CAN_RW_LE) :
+					psf->data_endswap = SF_FALSE ;
+					psf->read_short		= host_read_f2s ;
+					psf->read_int		= host_read_f2i ;
+					psf->read_float		= host_read_f ;
+					psf->read_double	= host_read_f2d ;
+					break ;
+
+			case (SF_ENDIAN_BIG + FLOAT_CAN_RW_LE) :
+					psf->data_endswap = SF_TRUE ;
+					psf->read_short		= host_read_f2s ;
+					psf->read_int		= host_read_f2i ;
+					psf->read_float		= host_read_f ;
+					psf->read_double	= host_read_f2d ;
+					break ;
+
+			case (SF_ENDIAN_LITTLE + FLOAT_CAN_RW_BE) :
+					psf->data_endswap = SF_TRUE ;
+					psf->read_short		= host_read_f2s ;
+					psf->read_int		= host_read_f2i ;
+					psf->read_float		= host_read_f ;
+					psf->read_double	= host_read_f2d ;
+					break ;
+
+			/* When the CPU is not IEEE compatible. */
+			case (SF_ENDIAN_BIG + FLOAT_BROKEN_LE) :
+					psf->data_endswap = SF_TRUE ;
+					psf->read_short		= replace_read_f2s ;
+					psf->read_int		= replace_read_f2i ;
+					psf->read_float		= replace_read_f ;
+					psf->read_double	= replace_read_f2d ;
+					break ;
+
+			case (SF_ENDIAN_LITTLE + FLOAT_BROKEN_LE) :
+					psf->data_endswap = SF_FALSE ;
+					psf->read_short		= replace_read_f2s ;
+					psf->read_int		= replace_read_f2i ;
+					psf->read_float		= replace_read_f ;
+					psf->read_double	= replace_read_f2d ;
+					break ;
+
+			case (SF_ENDIAN_BIG + FLOAT_BROKEN_BE) :
+					psf->data_endswap = SF_FALSE ;
+					psf->read_short		= replace_read_f2s ;
+					psf->read_int		= replace_read_f2i ;
+					psf->read_float		= replace_read_f ;
+					psf->read_double	= replace_read_f2d ;
+					break ;
+
+			case (SF_ENDIAN_LITTLE + FLOAT_BROKEN_BE) :
+					psf->data_endswap = SF_TRUE ;
+					psf->read_short		= replace_read_f2s ;
+					psf->read_int		= replace_read_f2i ;
+					psf->read_float		= replace_read_f ;
+					psf->read_double	= replace_read_f2d ;
+					break ;
+
+			default : break ;
+			} ;
+		} ;
+
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	switch (psf->endian + float_caps)
+		{	case (SF_ENDIAN_LITTLE + FLOAT_CAN_RW_LE) :
+					psf->data_endswap = SF_FALSE ;
+					psf->write_short	= host_write_s2f ;
+					psf->write_int		= host_write_i2f ;
+					psf->write_float	= host_write_f ;
+					psf->write_double	= host_write_d2f ;
+					break ;
+
+			case (SF_ENDIAN_BIG + FLOAT_CAN_RW_BE) :
+					psf->data_endswap = SF_FALSE ;
+					psf->write_short	= host_write_s2f ;
+					psf->write_int		= host_write_i2f ;
+					psf->write_float	= host_write_f ;
+					psf->write_double	= host_write_d2f ;
+					break ;
+
+			case (SF_ENDIAN_BIG + FLOAT_CAN_RW_LE) :
+					psf->data_endswap = SF_TRUE ;
+					psf->write_short	= host_write_s2f ;
+					psf->write_int		= host_write_i2f ;
+					psf->write_float	= host_write_f ;
+					psf->write_double	= host_write_d2f ;
+					break ;
+
+			case (SF_ENDIAN_LITTLE + FLOAT_CAN_RW_BE) :
+					psf->data_endswap = SF_TRUE ;
+					psf->write_short	= host_write_s2f ;
+					psf->write_int		= host_write_i2f ;
+					psf->write_float	= host_write_f ;
+					psf->write_double	= host_write_d2f ;
+					break ;
+
+			/* When the CPU is not IEEE compatible. */
+			case (SF_ENDIAN_BIG + FLOAT_BROKEN_LE) :
+					psf->data_endswap = SF_TRUE ;
+					psf->write_short	= replace_write_s2f ;
+					psf->write_int		= replace_write_i2f ;
+					psf->write_float	= replace_write_f ;
+					psf->write_double	= replace_write_d2f ;
+					break ;
+
+			case (SF_ENDIAN_LITTLE + FLOAT_BROKEN_LE) :
+					psf->data_endswap = SF_FALSE ;
+					psf->write_short	= replace_write_s2f ;
+					psf->write_int		= replace_write_i2f ;
+					psf->write_float	= replace_write_f ;
+					psf->write_double	= replace_write_d2f ;
+					break ;
+
+			case (SF_ENDIAN_BIG + FLOAT_BROKEN_BE) :
+					psf->data_endswap = SF_FALSE ;
+					psf->write_short	= replace_write_s2f ;
+					psf->write_int		= replace_write_i2f ;
+					psf->write_float	= replace_write_f ;
+					psf->write_double	= replace_write_d2f ;
+					break ;
+
+			case (SF_ENDIAN_LITTLE + FLOAT_BROKEN_BE) :
+					psf->data_endswap = SF_TRUE ;
+					psf->write_short	= replace_write_s2f ;
+					psf->write_int		= replace_write_i2f ;
+					psf->write_float	= replace_write_f ;
+					psf->write_double	= replace_write_d2f ;
+					break ;
+
+			default : break ;
+			} ;
+		} ;
+
+	if (psf->filelength > psf->dataoffset)
+	{	psf->datalength = (psf->dataend > 0) ? psf->dataend - psf->dataoffset :
+							psf->filelength - psf->dataoffset ;
+		}
+	else
+		psf->datalength = 0 ;
+
+	psf->sf.frames = psf->blockwidth > 0 ? psf->datalength / psf->blockwidth : 0 ;
+
+	return 0 ;
+} /* float32_init */
+
+float
+float32_be_read (const unsigned char *cptr)
+{	int		exponent, mantissa, negative ;
+	float	fvalue ;
+
+	negative = cptr [0] & 0x80 ;
+	exponent = ((cptr [0] & 0x7F) << 1) | ((cptr [1] & 0x80) ? 1 : 0) ;
+	mantissa = ((cptr [1] & 0x7F) << 16) | (cptr [2] << 8) | (cptr [3]) ;
+
+	if (! (exponent || mantissa))
+		return 0.0 ;
+
+	mantissa |= 0x800000 ;
+	exponent = exponent ? exponent - 127 : 0 ;
+
+	fvalue = mantissa ? ((float) mantissa) / ((float) 0x800000) : 0.0 ;
+
+	if (negative)
+		fvalue *= -1 ;
+
+	if (exponent > 0)
+		fvalue *= pow (2.0, exponent) ;
+	else if (exponent < 0)
+		fvalue /= pow (2.0, abs (exponent)) ;
+
+	return fvalue ;
+} /* float32_be_read */
+
+float
+float32_le_read (const unsigned char *cptr)
+{	int		exponent, mantissa, negative ;
+	float	fvalue ;
+
+	negative = cptr [3] & 0x80 ;
+	exponent = ((cptr [3] & 0x7F) << 1) | ((cptr [2] & 0x80) ? 1 : 0) ;
+	mantissa = ((cptr [2] & 0x7F) << 16) | (cptr [1] << 8) | (cptr [0]) ;
+
+	if (! (exponent || mantissa))
+		return 0.0 ;
+
+	mantissa |= 0x800000 ;
+	exponent = exponent ? exponent - 127 : 0 ;
+
+	fvalue = mantissa ? ((float) mantissa) / ((float) 0x800000) : 0.0 ;
+
+	if (negative)
+		fvalue *= -1 ;
+
+	if (exponent > 0)
+		fvalue *= pow (2.0, exponent) ;
+	else if (exponent < 0)
+		fvalue /= pow (2.0, abs (exponent)) ;
+
+	return fvalue ;
+} /* float32_le_read */
+
+void
+float32_le_write (float in, unsigned char *out)
+{	int		exponent, mantissa, negative = 0 ;
+
+	memset (out, 0, sizeof (int)) ;
+
+	if (fabs (in) < 1e-30)
+		return ;
+
+	if (in < 0.0)
+	{	in *= -1.0 ;
+		negative = 1 ;
+		} ;
+
+	in = frexp (in, &exponent) ;
+
+	exponent += 126 ;
+
+	in *= (float) 0x1000000 ;
+	mantissa = (((int) in) & 0x7FFFFF) ;
+
+	if (negative)
+		out [3] |= 0x80 ;
+
+	if (exponent & 0x01)
+		out [2] |= 0x80 ;
+
+	out [0] = mantissa & 0xFF ;
+	out [1] = (mantissa >> 8) & 0xFF ;
+	out [2] |= (mantissa >> 16) & 0x7F ;
+	out [3] |= (exponent >> 1) & 0x7F ;
+
+	return ;
+} /* float32_le_write */
+
+void
+float32_be_write (float in, unsigned char *out)
+{	int		exponent, mantissa, negative = 0 ;
+
+	memset (out, 0, sizeof (int)) ;
+
+	if (fabs (in) < 1e-30)
+		return ;
+
+	if (in < 0.0)
+	{	in *= -1.0 ;
+		negative = 1 ;
+		} ;
+
+	in = frexp (in, &exponent) ;
+
+	exponent += 126 ;
+
+	in *= (float) 0x1000000 ;
+	mantissa = (((int) in) & 0x7FFFFF) ;
+
+	if (negative)
+		out [0] |= 0x80 ;
+
+	if (exponent & 0x01)
+		out [1] |= 0x80 ;
+
+	out [3] = mantissa & 0xFF ;
+	out [2] = (mantissa >> 8) & 0xFF ;
+	out [1] |= (mantissa >> 16) & 0x7F ;
+	out [0] |= (exponent >> 1) & 0x7F ;
+
+	return ;
+} /* float32_be_write */
+
+/*==============================================================================================
+**	Private functions.
+*/
+
+static void
+float32_peak_update	(SF_PRIVATE *psf, const float *buffer, int count, sf_count_t indx)
+{	int 	chan ;
+	int		k, position ;
+	float	fmaxval ;
+
+	for (chan = 0 ; chan < psf->sf.channels ; chan++)
+	{	fmaxval = fabs (buffer [chan]) ;
+		position = 0 ;
+		for (k = chan ; k < count ; k += psf->sf.channels)
+			if (fmaxval < fabs (buffer [k]))
+			{	fmaxval = fabs (buffer [k]) ;
+				position = k ;
+				} ;
+
+		if (fmaxval > psf->peak_info->peaks [chan].value)
+		{	psf->peak_info->peaks [chan].value = fmaxval ;
+			psf->peak_info->peaks [chan].position = psf->write_current + indx + (position / psf->sf.channels) ;
+			} ;
+		} ;
+
+	return ;
+} /* float32_peak_update */
+
+static int
+float32_get_capability	(SF_PRIVATE *psf)
+{	union
+	{	float			f ;
+		int				i ;
+		unsigned char	c [4] ;
+	} data ;
+
+	data.f = (float) 1.23456789 ; /* Some abitrary value. */
+
+	if (! psf->ieee_replace)
+	{	/* If this test is true ints and floats are compatible and little endian. */
+		if (data.c [0] == 0x52 && data.c [1] == 0x06 && data.c [2] == 0x9e && data.c [3] == 0x3f)
+			return FLOAT_CAN_RW_LE ;
+
+		/* If this test is true ints and floats are compatible and big endian. */
+		if (data.c [3] == 0x52 && data.c [2] == 0x06 && data.c [1] == 0x9e && data.c [0] == 0x3f)
+			return FLOAT_CAN_RW_BE ;
+		} ;
+
+	/* Floats are broken. Don't expect reading or writing to be fast. */
+	psf_log_printf (psf, "Using IEEE replacement code for float.\n") ;
+
+	return (CPU_IS_LITTLE_ENDIAN) ? FLOAT_BROKEN_LE : FLOAT_BROKEN_BE ;
+} /* float32_get_capability */
+
+/*=======================================================================================
+*/
+
+static void
+f2s_array (const float *src, int count, short *dest, float scale)
+{
+	for (int i = 0 ; i < count ; i++)
+	{	dest [i] = psf_lrintf (scale * src [i]) ;
+		} ;
+} /* f2s_array */
+
+static void
+f2s_clip_array (const float *src, int count, short *dest, float scale)
+{	for (int i = 0 ; i < count ; i++)
+	{	float tmp = scale * src [i] ;
+
+		if (tmp > 32767.0)
+			dest [i] = SHRT_MAX ;
+		else if (tmp < -32768.0)
+			dest [i] = SHRT_MIN ;
+		else
+			dest [i] = psf_lrintf (tmp) ;
+		} ;
+} /* f2s_clip_array */
+
+static inline void
+f2i_array (const float *src, int count, int *dest, float scale)
+{	for (int i = 0 ; i < count ; i++)
+	{	dest [i] = psf_lrintf (scale * src [i]) ;
+		} ;
+} /* f2i_array */
+
+static inline void
+f2i_clip_array (const float *src, int count, int *dest, float scale)
+{	for (int i = 0 ; i < count ; i++)
+	{	float tmp = scale * src [i] ;
+
+		if (CPU_CLIPS_POSITIVE == 0 && tmp > (1.0 * INT_MAX))
+			dest [i] = INT_MAX ;
+		else if (CPU_CLIPS_NEGATIVE == 0 && tmp < (-1.0 * INT_MAX))
+			dest [i] = INT_MIN ;
+		else
+			dest [i] = psf_lrintf (tmp) ;
+		} ;
+} /* f2i_clip_array */
+
+static inline void
+f2d_array (const float *src, int count, double *dest)
+{	for (int i = 0 ; i < count ; i++)
+	{	dest [i] = src [i] ;
+		} ;
+} /* f2d_array */
+
+static inline void
+s2f_array (const short *src, float *dest, int count, float scale)
+{	for (int i = 0 ; i < count ; i++)
+	{	dest [i] = scale * src [i] ;
+		} ;
+} /* s2f_array */
+
+static inline void
+i2f_array (const int *src, float *dest, int count, float scale)
+{	for (int i = 0 ; i < count ; i++)
+	{	dest [i] = scale * src [i] ;
+		} ;
+} /* i2f_array */
+
+static inline void
+d2f_array (const double *src, float *dest, int count)
+{	for (int i = 0 ; i < count ; i++)
+	{	dest [i] = src [i] ;
+		} ;
+} /* d2f_array */
+
+/*----------------------------------------------------------------------------------------------
+*/
+
+static sf_count_t
+host_read_f2s	(SF_PRIVATE *psf, short *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	void		(*convert) (const float *, int, short *, float) ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+	float		scale ;
+
+	convert = (psf->add_clipping) ? f2s_clip_array : f2s_array ;
+	bufferlen = ARRAY_LEN (ubuf.fbuf) ;
+	scale = (psf->float_int_mult == 0) ? 1.0 : 0x7FFF / psf->float_max ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.fbuf, sizeof (float), bufferlen, psf) ;
+
+/* Fix me : Need lef2s_array */
+		if (psf->data_endswap == SF_TRUE)
+			endswap_int_array (ubuf.ibuf, readcount) ;
+
+		convert (ubuf.fbuf, readcount, ptr + total, scale) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* host_read_f2s */
+
+static sf_count_t
+host_read_f2i	(SF_PRIVATE *psf, int *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	void		(*convert) (const float *, int, int *, float) ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+	float		scale ;
+
+	convert = (psf->add_clipping) ? f2i_clip_array : f2i_array ;
+	bufferlen = ARRAY_LEN (ubuf.fbuf) ;
+	scale = (psf->float_int_mult == 0) ? 1.0 : 2147483648.0f / psf->float_max ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.fbuf, sizeof (float), bufferlen, psf) ;
+
+		if (psf->data_endswap == SF_TRUE)
+			endswap_int_array (ubuf.ibuf, bufferlen) ;
+
+		convert (ubuf.fbuf, readcount, ptr + total, scale) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* host_read_f2i */
+
+static sf_count_t
+host_read_f	(SF_PRIVATE *psf, float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+
+	if (psf->data_endswap != SF_TRUE)
+		return psf_fread (ptr, sizeof (float), len, psf) ;
+
+	bufferlen = ARRAY_LEN (ubuf.fbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.fbuf, sizeof (float), bufferlen, psf) ;
+
+		endswap_int_copy ((int*) (ptr + total), ubuf.ibuf, readcount) ;
+
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* host_read_f */
+
+static sf_count_t
+host_read_f2d	(SF_PRIVATE *psf, double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.fbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.fbuf, sizeof (float), bufferlen, psf) ;
+
+		if (psf->data_endswap == SF_TRUE)
+			endswap_int_array (ubuf.ibuf, bufferlen) ;
+
+/* Fix me : Need lef2d_array */
+		f2d_array (ubuf.fbuf, readcount, ptr + total) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* host_read_f2d */
+
+static sf_count_t
+host_write_s2f	(SF_PRIVATE *psf, const short *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+	float		scale ;
+
+/* Erik */
+	scale = (psf->scale_int_float == 0) ? 1.0 : 1.0 / 0x8000 ;
+	bufferlen = ARRAY_LEN (ubuf.fbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		s2f_array (ptr + total, ubuf.fbuf, bufferlen, scale) ;
+
+		if (psf->peak_info)
+			float32_peak_update (psf, ubuf.fbuf, bufferlen, total / psf->sf.channels) ;
+
+		if (psf->data_endswap == SF_TRUE)
+			endswap_int_array (ubuf.ibuf, bufferlen) ;
+
+		writecount = (int) psf_fwrite (ubuf.fbuf, sizeof (float), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* host_write_s2f */
+
+static sf_count_t
+host_write_i2f	(SF_PRIVATE *psf, const int *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+	float		scale ;
+
+	scale = (psf->scale_int_float == 0) ? 1.0 : 1.0 / (8.0 * 0x10000000) ;
+	bufferlen = ARRAY_LEN (ubuf.fbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		i2f_array (ptr + total, ubuf.fbuf, bufferlen, scale) ;
+
+		if (psf->peak_info)
+			float32_peak_update (psf, ubuf.fbuf, bufferlen, total / psf->sf.channels) ;
+
+		if (psf->data_endswap == SF_TRUE)
+			endswap_int_array (ubuf.ibuf, bufferlen) ;
+
+		writecount = (int) psf_fwrite (ubuf.fbuf, sizeof (float) , bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* host_write_i2f */
+
+static sf_count_t
+host_write_f	(SF_PRIVATE *psf, const float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	if (psf->peak_info)
+		float32_peak_update (psf, ptr, len, 0) ;
+
+	if (psf->data_endswap != SF_TRUE)
+		return psf_fwrite (ptr, sizeof (float), len, psf) ;
+
+	bufferlen = ARRAY_LEN (ubuf.fbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+
+		endswap_int_copy (ubuf.ibuf, (const int*) (ptr + total), bufferlen) ;
+
+		writecount = (int) psf_fwrite (ubuf.fbuf, sizeof (float), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* host_write_f */
+
+static sf_count_t
+host_write_d2f	(SF_PRIVATE *psf, const double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.fbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+
+		d2f_array (ptr + total, ubuf.fbuf, bufferlen) ;
+
+		if (psf->peak_info)
+			float32_peak_update (psf, ubuf.fbuf, bufferlen, total / psf->sf.channels) ;
+
+		if (psf->data_endswap == SF_TRUE)
+			endswap_int_array (ubuf.ibuf, bufferlen) ;
+
+		writecount = (int) psf_fwrite (ubuf.fbuf, sizeof (float), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* host_write_d2f */
+
+/*=======================================================================================
+*/
+
+static sf_count_t
+replace_read_f2s	(SF_PRIVATE *psf, short *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+	float		scale ;
+
+	bufferlen = ARRAY_LEN (ubuf.fbuf) ;
+	scale = (psf->float_int_mult == 0) ? 1.0 : 0x7FFF / psf->float_max ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.fbuf, sizeof (float), bufferlen, psf) ;
+
+		if (psf->data_endswap == SF_TRUE)
+			endswap_int_array (ubuf.ibuf, bufferlen) ;
+
+		bf2f_array (ubuf.fbuf, bufferlen) ;
+
+		f2s_array (ubuf.fbuf, readcount, ptr + total, scale) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* replace_read_f2s */
+
+static sf_count_t
+replace_read_f2i	(SF_PRIVATE *psf, int *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+	float		scale ;
+
+	bufferlen = ARRAY_LEN (ubuf.fbuf) ;
+	scale = (psf->float_int_mult == 0) ? 1.0 : 2147483648.0f / psf->float_max ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.fbuf, sizeof (float), bufferlen, psf) ;
+
+		if (psf->data_endswap == SF_TRUE)
+			endswap_int_array (ubuf.ibuf, bufferlen) ;
+
+		bf2f_array (ubuf.fbuf, bufferlen) ;
+
+		f2i_array (ubuf.fbuf, readcount, ptr + total, scale) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* replace_read_f2i */
+
+static sf_count_t
+replace_read_f	(SF_PRIVATE *psf, float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+
+	/* FIX THIS */
+
+	bufferlen = ARRAY_LEN (ubuf.fbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.fbuf, sizeof (float), bufferlen, psf) ;
+
+		if (psf->data_endswap == SF_TRUE)
+			endswap_int_array (ubuf.ibuf, bufferlen) ;
+
+		bf2f_array (ubuf.fbuf, bufferlen) ;
+
+		memcpy (ptr + total, ubuf.fbuf, bufferlen * sizeof (float)) ;
+
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* replace_read_f */
+
+static sf_count_t
+replace_read_f2d	(SF_PRIVATE *psf, double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.fbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.fbuf, sizeof (float), bufferlen, psf) ;
+
+		if (psf->data_endswap == SF_TRUE)
+			endswap_int_array (ubuf.ibuf, bufferlen) ;
+
+		bf2f_array (ubuf.fbuf, bufferlen) ;
+
+		f2d_array (ubuf.fbuf, readcount, ptr + total) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* replace_read_f2d */
+
+static sf_count_t
+replace_write_s2f	(SF_PRIVATE *psf, const short *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+	float		scale ;
+
+	scale = (psf->scale_int_float == 0) ? 1.0 : 1.0 / 0x8000 ;
+	bufferlen = ARRAY_LEN (ubuf.fbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		s2f_array (ptr + total, ubuf.fbuf, bufferlen, scale) ;
+
+		if (psf->peak_info)
+			float32_peak_update (psf, ubuf.fbuf, bufferlen, total / psf->sf.channels) ;
+
+		f2bf_array (ubuf.fbuf, bufferlen) ;
+
+		if (psf->data_endswap == SF_TRUE)
+			endswap_int_array (ubuf.ibuf, bufferlen) ;
+
+		writecount = (int) psf_fwrite (ubuf.fbuf, sizeof (float), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* replace_write_s2f */
+
+static sf_count_t
+replace_write_i2f	(SF_PRIVATE *psf, const int *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+	float		scale ;
+
+	scale = (psf->scale_int_float == 0) ? 1.0 : 1.0 / (8.0 * 0x10000000) ;
+	bufferlen = ARRAY_LEN (ubuf.fbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		i2f_array (ptr + total, ubuf.fbuf, bufferlen, scale) ;
+
+		if (psf->peak_info)
+			float32_peak_update (psf, ubuf.fbuf, bufferlen, total / psf->sf.channels) ;
+
+		f2bf_array (ubuf.fbuf, bufferlen) ;
+
+		if (psf->data_endswap == SF_TRUE)
+			endswap_int_array (ubuf.ibuf, bufferlen) ;
+
+		writecount = (int) psf_fwrite (ubuf.fbuf, sizeof (float), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* replace_write_i2f */
+
+static sf_count_t
+replace_write_f	(SF_PRIVATE *psf, const float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	/* FIX THIS */
+	if (psf->peak_info)
+		float32_peak_update (psf, ptr, len, 0) ;
+
+	bufferlen = ARRAY_LEN (ubuf.fbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+
+		memcpy (ubuf.fbuf, ptr + total, bufferlen * sizeof (float)) ;
+
+		f2bf_array (ubuf.fbuf, bufferlen) ;
+
+		if (psf->data_endswap == SF_TRUE)
+			endswap_int_array (ubuf.ibuf, bufferlen) ;
+
+		writecount = (int) psf_fwrite (ubuf.fbuf, sizeof (float) , bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* replace_write_f */
+
+static sf_count_t
+replace_write_d2f	(SF_PRIVATE *psf, const double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.fbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		d2f_array (ptr + total, ubuf.fbuf, bufferlen) ;
+
+		if (psf->peak_info)
+			float32_peak_update (psf, ubuf.fbuf, bufferlen, total / psf->sf.channels) ;
+
+		f2bf_array (ubuf.fbuf, bufferlen) ;
+
+		if (psf->data_endswap == SF_TRUE)
+			endswap_int_array (ubuf.ibuf, bufferlen) ;
+
+		writecount = (int) psf_fwrite (ubuf.fbuf, sizeof (float), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* replace_write_d2f */
+
+/*----------------------------------------------------------------------------------------------
+*/
+
+static void
+bf2f_array (float *buffer, int count)
+{	for (int i = 0 ; i < count ; i++)
+	{	buffer [i] = FLOAT32_READ ((unsigned char *) &buffer [i]) ;
+		} ;
+} /* bf2f_array */
+
+static void
+f2bf_array (float *buffer, int count)
+{	for (int i = 0 ; i < count ; i++)
+	{	FLOAT32_WRITE (buffer [i], (unsigned char*) &buffer [i]) ;
+		} ;
+} /* f2bf_array */
+
diff --git a/src/g72x.c b/src/g72x.c
new file mode 100644
index 0000000..62e4005
--- /dev/null
+++ b/src/g72x.c
@@ -0,0 +1,608 @@
+/*
+** Copyright (C) 1999-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "sndfile.h"
+#include "sfendian.h"
+#include "common.h"
+#include "G72x/g72x.h"
+
+/* This struct is private to the G72x code. */
+struct g72x_state ;
+typedef struct g72x_state G72x_STATE ;
+
+typedef struct
+{	/* Private data. Don't mess with it. */
+	struct g72x_state * private ;
+
+	/* Public data. Read only. */
+	int				blocksize, samplesperblock, bytesperblock ;
+
+	/* Public data. Read and write. */
+	int				blocks_total, block_curr, sample_curr ;
+	unsigned char	block	[G72x_BLOCK_SIZE] ;
+	short			samples	[G72x_BLOCK_SIZE] ;
+} G72x_PRIVATE ;
+
+static	int	psf_g72x_decode_block (SF_PRIVATE *psf, G72x_PRIVATE *pg72x) ;
+static	int	psf_g72x_encode_block (SF_PRIVATE *psf, G72x_PRIVATE *pg72x) ;
+
+static	sf_count_t	g72x_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
+static	sf_count_t	g72x_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
+static	sf_count_t	g72x_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
+static	sf_count_t	g72x_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
+
+static	sf_count_t	g72x_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
+static	sf_count_t	g72x_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
+static	sf_count_t	g72x_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
+static	sf_count_t	g72x_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
+
+static	sf_count_t g72x_seek (SF_PRIVATE *psf, int mode, sf_count_t offset) ;
+
+static	int	g72x_close (SF_PRIVATE *psf) ;
+
+
+/*============================================================================================
+** WAV G721 Reader initialisation function.
+*/
+
+int
+g72x_init (SF_PRIVATE * psf)
+{	G72x_PRIVATE	*pg72x ;
+	int	bitspersample, bytesperblock, codec ;
+
+	if (psf->codec_data != NULL)
+	{	psf_log_printf (psf, "*** psf->codec_data is not NULL.\n") ;
+		return SFE_INTERNAL ;
+		} ;
+
+	psf->sf.seekable = SF_FALSE ;
+
+	if (psf->sf.channels != 1)
+		return SFE_G72X_NOT_MONO ;
+
+	if ((pg72x = calloc (1, sizeof (G72x_PRIVATE))) == NULL)
+		return SFE_MALLOC_FAILED ;
+
+	psf->codec_data = (void*) pg72x ;
+
+	pg72x->block_curr = 0 ;
+	pg72x->sample_curr = 0 ;
+
+	switch (SF_CODEC (psf->sf.format))
+	{	case SF_FORMAT_G721_32 :
+				codec = G721_32_BITS_PER_SAMPLE ;
+				bytesperblock = G721_32_BYTES_PER_BLOCK ;
+				bitspersample = G721_32_BITS_PER_SAMPLE ;
+				break ;
+
+		case SF_FORMAT_G723_24:
+				codec = G723_24_BITS_PER_SAMPLE ;
+				bytesperblock = G723_24_BYTES_PER_BLOCK ;
+				bitspersample = G723_24_BITS_PER_SAMPLE ;
+				break ;
+
+		case SF_FORMAT_G723_40:
+				codec = G723_40_BITS_PER_SAMPLE ;
+				bytesperblock = G723_40_BYTES_PER_BLOCK ;
+				bitspersample = G723_40_BITS_PER_SAMPLE ;
+				break ;
+
+		default : return SFE_UNIMPLEMENTED ;
+		} ;
+
+	psf->filelength = psf_get_filelen (psf) ;
+	if (psf->filelength < psf->dataoffset)
+		psf->filelength = psf->dataoffset ;
+
+	psf->datalength = psf->filelength - psf->dataoffset ;
+	if (psf->dataend > 0)
+		psf->datalength -= psf->filelength - psf->dataend ;
+
+	if (psf->file.mode == SFM_READ)
+	{	pg72x->private = g72x_reader_init (codec, &(pg72x->blocksize), &(pg72x->samplesperblock)) ;
+		if (pg72x->private == NULL)
+			return SFE_MALLOC_FAILED ;
+
+		pg72x->bytesperblock = bytesperblock ;
+
+		psf->read_short		= g72x_read_s ;
+		psf->read_int		= g72x_read_i ;
+		psf->read_float		= g72x_read_f ;
+		psf->read_double	= g72x_read_d ;
+
+		psf->seek = g72x_seek ;
+
+		if (psf->datalength % pg72x->blocksize)
+		{	psf_log_printf (psf, "*** Odd psf->datalength (%D) should be a multiple of %d\n", psf->datalength, pg72x->blocksize) ;
+			pg72x->blocks_total = (psf->datalength / pg72x->blocksize) + 1 ;
+			}
+		else
+			pg72x->blocks_total = psf->datalength / pg72x->blocksize ;
+
+		psf->sf.frames = (sf_count_t) pg72x->blocks_total * pg72x->samplesperblock ;
+
+		psf_g72x_decode_block (psf, pg72x) ;
+		}
+	else if (psf->file.mode == SFM_WRITE)
+	{	pg72x->private = g72x_writer_init (codec, &(pg72x->blocksize), &(pg72x->samplesperblock)) ;
+		if (pg72x->private == NULL)
+			return SFE_MALLOC_FAILED ;
+
+		pg72x->bytesperblock = bytesperblock ;
+
+		psf->write_short	= g72x_write_s ;
+		psf->write_int		= g72x_write_i ;
+		psf->write_float	= g72x_write_f ;
+		psf->write_double	= g72x_write_d ;
+
+		if (psf->datalength % pg72x->blocksize)
+			pg72x->blocks_total = (psf->datalength / pg72x->blocksize) + 1 ;
+		else
+			pg72x->blocks_total = psf->datalength / pg72x->blocksize ;
+
+		if (psf->datalength > 0)
+			psf->sf.frames = (8 * psf->datalength) / bitspersample ;
+
+		if ((psf->sf.frames * bitspersample) / 8 != psf->datalength)
+			psf_log_printf (psf, "*** Warning : weird psf->datalength.\n") ;
+		} ;
+
+	psf->codec_close	= g72x_close ;
+
+	return 0 ;
+} /* g72x_init */
+
+/*============================================================================================
+** G721 Read Functions.
+*/
+
+static int
+psf_g72x_decode_block (SF_PRIVATE *psf, G72x_PRIVATE *pg72x)
+{	int	k ;
+
+	pg72x->block_curr ++ ;
+	pg72x->sample_curr = 0 ;
+
+	if (pg72x->block_curr > pg72x->blocks_total)
+	{	memset (pg72x->samples, 0, G72x_BLOCK_SIZE * sizeof (short)) ;
+		return 1 ;
+		} ;
+
+	if ((k = psf_fread (pg72x->block, 1, pg72x->bytesperblock, psf)) != pg72x->bytesperblock)
+		psf_log_printf (psf, "*** Warning : short read (%d != %d).\n", k, pg72x->bytesperblock) ;
+
+	pg72x->blocksize = k ;
+	g72x_decode_block (pg72x->private, pg72x->block, pg72x->samples) ;
+
+	return 1 ;
+} /* psf_g72x_decode_block */
+
+static int
+g72x_read_block (SF_PRIVATE *psf, G72x_PRIVATE *pg72x, short *ptr, int len)
+{	int	count, total = 0, indx = 0 ;
+
+	while (indx < len)
+	{	if (pg72x->block_curr > pg72x->blocks_total)
+		{	memset (&(ptr [indx]), 0, (len - indx) * sizeof (short)) ;
+			return total ;
+			} ;
+
+		if (pg72x->sample_curr >= pg72x->samplesperblock)
+			psf_g72x_decode_block (psf, pg72x) ;
+
+		count = pg72x->samplesperblock - pg72x->sample_curr ;
+		count = (len - indx > count) ? count : len - indx ;
+
+		memcpy (&(ptr [indx]), &(pg72x->samples [pg72x->sample_curr]), count * sizeof (short)) ;
+		indx += count ;
+		pg72x->sample_curr += count ;
+		total = indx ;
+		} ;
+
+	return total ;
+} /* g72x_read_block */
+
+static sf_count_t
+g72x_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len)
+{	G72x_PRIVATE 	*pg72x ;
+	int			readcount, count ;
+	sf_count_t	total = 0 ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	pg72x = (G72x_PRIVATE*) psf->codec_data ;
+
+	while (len > 0)
+	{	readcount = (len > 0x10000000) ? 0x10000000 : (int) len ;
+
+		count = g72x_read_block (psf, pg72x, ptr, readcount) ;
+
+		total += count ;
+		len -= count ;
+
+		if (count != readcount)
+			break ;
+		} ;
+
+	return total ;
+} /* g72x_read_s */
+
+static sf_count_t
+g72x_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	G72x_PRIVATE *pg72x ;
+	short		*sptr ;
+	int			k, bufferlen, readcount = 0, count ;
+	sf_count_t	total = 0 ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	pg72x = (G72x_PRIVATE*) psf->codec_data ;
+
+	sptr = ubuf.sbuf ;
+	bufferlen = SF_BUFFER_LEN / sizeof (short) ;
+	while (len > 0)
+	{	readcount = (len >= bufferlen) ? bufferlen : (int) len ;
+		count = g72x_read_block (psf, pg72x, sptr, readcount) ;
+
+		for (k = 0 ; k < readcount ; k++)
+			ptr [total + k] = arith_shift_left (sptr [k], 16) ;
+
+		total += count ;
+		len -= readcount ;
+		if (count != readcount)
+			break ;
+		} ;
+
+	return total ;
+} /* g72x_read_i */
+
+static sf_count_t
+g72x_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	G72x_PRIVATE *pg72x ;
+	short		*sptr ;
+	int			k, bufferlen, readcount = 0, count ;
+	sf_count_t	total = 0 ;
+	float 		normfact ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	pg72x = (G72x_PRIVATE*) psf->codec_data ;
+
+	normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x8000) : 1.0 ;
+
+	sptr = ubuf.sbuf ;
+	bufferlen = SF_BUFFER_LEN / sizeof (short) ;
+	while (len > 0)
+	{	readcount = (len >= bufferlen) ? bufferlen : (int) len ;
+		count = g72x_read_block (psf, pg72x, sptr, readcount) ;
+		for (k = 0 ; k < readcount ; k++)
+			ptr [total + k] = normfact * sptr [k] ;
+
+		total += count ;
+		len -= readcount ;
+		if (count != readcount)
+			break ;
+		} ;
+
+	return total ;
+} /* g72x_read_f */
+
+static sf_count_t
+g72x_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	G72x_PRIVATE *pg72x ;
+	short		*sptr ;
+	int			k, bufferlen, readcount = 0, count ;
+	sf_count_t	total = 0 ;
+	double		normfact ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	pg72x = (G72x_PRIVATE*) psf->codec_data ;
+
+	normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((double) 0x8000) : 1.0 ;
+
+	sptr = ubuf.sbuf ;
+	bufferlen = SF_BUFFER_LEN / sizeof (short) ;
+	while (len > 0)
+	{	readcount = (len >= bufferlen) ? bufferlen : (int) len ;
+		count = g72x_read_block (psf, pg72x, sptr, readcount) ;
+		for (k = 0 ; k < readcount ; k++)
+			ptr [total + k] = normfact * (double) (sptr [k]) ;
+
+		total += count ;
+		len -= readcount ;
+		if (count != readcount)
+			break ;
+		} ;
+
+	return total ;
+} /* g72x_read_d */
+
+static sf_count_t
+g72x_seek (SF_PRIVATE *psf, int UNUSED (mode), sf_count_t UNUSED (offset))
+{
+	psf_log_printf (psf, "seek unsupported\n") ;
+
+	/*	No simple solution. To do properly, would need to seek
+	**	to start of file and decode everything up to seek position.
+	**	Maybe implement SEEK_SET to 0 only?
+	*/
+	return 0 ;
+
+/*
+**		G72x_PRIVATE	*pg72x ;
+**		int			newblock, newsample, sample_curr ;
+**
+**		if (psf->codec_data == NULL)
+**			return 0 ;
+**		pg72x = (G72x_PRIVATE*) psf->codec_data ;
+**
+**		if (! (psf->datalength && psf->dataoffset))
+**		{	psf->error = SFE_BAD_SEEK ;
+**			return	PSF_SEEK_ERROR ;
+**			} ;
+**
+**		sample_curr = (8 * psf->datalength) / G721_32_BITS_PER_SAMPLE ;
+**
+**		switch (whence)
+**		{	case SEEK_SET :
+**					if (offset < 0 || offset > sample_curr)
+**					{	psf->error = SFE_BAD_SEEK ;
+**						return	PSF_SEEK_ERROR ;
+**						} ;
+**					newblock  = offset / pg72x->samplesperblock ;
+**					newsample = offset % pg72x->samplesperblock ;
+**					break ;
+**
+**			case SEEK_CUR :
+**					if (psf->current + offset < 0 || psf->current + offset > sample_curr)
+**					{	psf->error = SFE_BAD_SEEK ;
+**						return	PSF_SEEK_ERROR ;
+**						} ;
+**					newblock  = (8 * (psf->current + offset)) / pg72x->samplesperblock ;
+**					newsample = (8 * (psf->current + offset)) % pg72x->samplesperblock ;
+**					break ;
+**
+**			case SEEK_END :
+**					if (offset > 0 || sample_curr + offset < 0)
+**					{	psf->error = SFE_BAD_SEEK ;
+**						return	PSF_SEEK_ERROR ;
+**						} ;
+**					newblock  = (sample_curr + offset) / pg72x->samplesperblock ;
+**					newsample = (sample_curr + offset) % pg72x->samplesperblock ;
+**					break ;
+**
+**			default :
+**					psf->error = SFE_BAD_SEEK ;
+**					return	PSF_SEEK_ERROR ;
+**			} ;
+**
+**		if (psf->file.mode == SFM_READ)
+**		{	psf_fseek (psf, psf->dataoffset + newblock * pg72x->blocksize, SEEK_SET) ;
+**			pg72x->block_curr  = newblock ;
+**			psf_g72x_decode_block (psf, pg72x) ;
+**			pg72x->sample_curr = newsample ;
+**			}
+**		else
+**		{	/+* What to do about write??? *+/
+**			psf->error = SFE_BAD_SEEK ;
+**			return	PSF_SEEK_ERROR ;
+**			} ;
+**
+**		psf->current = newblock * pg72x->samplesperblock + newsample ;
+**		return psf->current ;
+**
+*/
+} /* g72x_seek */
+
+/*==========================================================================================
+** G72x Write Functions.
+*/
+
+static int
+psf_g72x_encode_block (SF_PRIVATE *psf, G72x_PRIVATE *pg72x)
+{	int k ;
+
+	/* Encode the samples. */
+	g72x_encode_block (pg72x->private, pg72x->samples, pg72x->block) ;
+
+	/* Write the block to disk. */
+	if ((k = psf_fwrite (pg72x->block, 1, pg72x->blocksize, psf)) != pg72x->blocksize)
+		psf_log_printf (psf, "*** Warning : short write (%d != %d).\n", k, pg72x->blocksize) ;
+
+	pg72x->sample_curr = 0 ;
+	pg72x->block_curr ++ ;
+
+	/* Set samples to zero for next block. */
+	memset (pg72x->samples, 0, G72x_BLOCK_SIZE * sizeof (short)) ;
+
+	return 1 ;
+} /* psf_g72x_encode_block */
+
+static int
+g72x_write_block (SF_PRIVATE *psf, G72x_PRIVATE *pg72x, const short *ptr, int len)
+{	int	count, total = 0, indx = 0 ;
+
+	while (indx < len)
+	{	count = pg72x->samplesperblock - pg72x->sample_curr ;
+
+		if (count > len - indx)
+			count = len - indx ;
+
+		memcpy (&(pg72x->samples [pg72x->sample_curr]), &(ptr [indx]), count * sizeof (short)) ;
+		indx += count ;
+		pg72x->sample_curr += count ;
+		total = indx ;
+
+		if (pg72x->sample_curr >= pg72x->samplesperblock)
+			psf_g72x_encode_block (psf, pg72x) ;
+		} ;
+
+	return total ;
+} /* g72x_write_block */
+
+static sf_count_t
+g72x_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len)
+{	G72x_PRIVATE 	*pg72x ;
+	int			writecount, count ;
+	sf_count_t	total = 0 ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	pg72x = (G72x_PRIVATE*) psf->codec_data ;
+
+	while (len > 0)
+	{	writecount = (len > 0x10000000) ? 0x10000000 : (int) len ;
+
+		count = g72x_write_block (psf, pg72x, ptr, writecount) ;
+
+		total += count ;
+		len -= count ;
+		if (count != writecount)
+			break ;
+		} ;
+
+	return total ;
+} /* g72x_write_s */
+
+static sf_count_t
+g72x_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	G72x_PRIVATE *pg72x ;
+	short		*sptr ;
+	int			k, bufferlen, writecount = 0, count ;
+	sf_count_t	total = 0 ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	pg72x = (G72x_PRIVATE*) psf->codec_data ;
+
+	sptr = ubuf.sbuf ;
+	bufferlen = SF_BUFFER_LEN / sizeof (short) ;
+	while (len > 0)
+	{	writecount = (len >= bufferlen) ? bufferlen : (int) len ;
+		for (k = 0 ; k < writecount ; k++)
+			sptr [k] = ptr [total + k] >> 16 ;
+		count = g72x_write_block (psf, pg72x, sptr, writecount) ;
+
+		total += count ;
+		len -= writecount ;
+		if (count != writecount)
+			break ;
+		} ;
+	return total ;
+} /* g72x_write_i */
+
+static sf_count_t
+g72x_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	G72x_PRIVATE *pg72x ;
+	short		*sptr ;
+	int			k, bufferlen, writecount = 0, count ;
+	sf_count_t	total = 0 ;
+	float		normfact ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	pg72x = (G72x_PRIVATE*) psf->codec_data ;
+
+	normfact = (psf->norm_float == SF_TRUE) ? (1.0 * 0x8000) : 1.0 ;
+
+	sptr = ubuf.sbuf ;
+	bufferlen = SF_BUFFER_LEN / sizeof (short) ;
+	while (len > 0)
+	{	writecount = (len >= bufferlen) ? bufferlen : (int) len ;
+		for (k = 0 ; k < writecount ; k++)
+			sptr [k] = psf_lrintf (normfact * ptr [total + k]) ;
+		count = g72x_write_block (psf, pg72x, sptr, writecount) ;
+
+		total += count ;
+		len -= writecount ;
+		if (count != writecount)
+			break ;
+		} ;
+
+	return total ;
+} /* g72x_write_f */
+
+static sf_count_t
+g72x_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	G72x_PRIVATE *pg72x ;
+	short		*sptr ;
+	int			k, bufferlen, writecount = 0, count ;
+	sf_count_t	total = 0 ;
+	double		normfact ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	pg72x = (G72x_PRIVATE*) psf->codec_data ;
+
+	normfact = (psf->norm_double == SF_TRUE) ? (1.0 * 0x8000) : 1.0 ;
+
+	sptr = ubuf.sbuf ;
+	bufferlen = SF_BUFFER_LEN / sizeof (short) ;
+	while (len > 0)
+	{	writecount = (len >= bufferlen) ? bufferlen : (int) len ;
+		for (k = 0 ; k < writecount ; k++)
+			sptr [k] = psf_lrint (normfact * ptr [total + k]) ;
+		count = g72x_write_block (psf, pg72x, sptr, writecount) ;
+
+		total += count ;
+		len -= writecount ;
+		if (count != writecount)
+			break ;
+		} ;
+
+	return total ;
+} /* g72x_write_d */
+
+static int
+g72x_close (SF_PRIVATE *psf)
+{	G72x_PRIVATE *pg72x ;
+
+	pg72x = (G72x_PRIVATE*) psf->codec_data ;
+
+	if (psf->file.mode == SFM_WRITE)
+	{	/*	If a block has been partially assembled, write it out
+		**	as the final block.
+		*/
+
+		if (pg72x->sample_curr && pg72x->sample_curr < G72x_BLOCK_SIZE)
+			psf_g72x_encode_block (psf, pg72x) ;
+
+		if (psf->write_header)
+			psf->write_header (psf, SF_FALSE) ;
+		} ;
+
+	/* Only free the pointer allocated by g72x_(reader|writer)_init. */
+	free (pg72x->private) ;
+
+	return 0 ;
+} /* g72x_close */
+
diff --git a/src/gsm610.c b/src/gsm610.c
new file mode 100644
index 0000000..933f03f
--- /dev/null
+++ b/src/gsm610.c
@@ -0,0 +1,629 @@
+/*
+** Copyright (C) 1999-2019 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "sndfile.h"
+#include "sfendian.h"
+#include "common.h"
+#include "wavlike.h"
+#include "GSM610/gsm.h"
+
+#define	GSM610_BLOCKSIZE		33
+#define	GSM610_SAMPLES			160
+
+typedef struct gsm610_tag
+{	int				blocks ;
+	int				blockcount, samplecount ;
+	int				samplesperblock, blocksize ;
+
+	int				(*decode_block)	(SF_PRIVATE *psf, struct gsm610_tag *pgsm610) ;
+	int				(*encode_block)	(SF_PRIVATE *psf, struct gsm610_tag *pgsm610) ;
+
+	short			samples [WAVLIKE_GSM610_SAMPLES] ;
+	unsigned char	block [WAVLIKE_GSM610_BLOCKSIZE] ;
+
+	/* Damn I hate typedef-ed pointers; yes, gsm is a pointer type. */
+	gsm				gsm_data ;
+} GSM610_PRIVATE ;
+
+static sf_count_t	gsm610_read_s	(SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
+static sf_count_t	gsm610_read_i	(SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
+static sf_count_t	gsm610_read_f	(SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
+static sf_count_t	gsm610_read_d	(SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
+
+static sf_count_t	gsm610_write_s	(SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
+static sf_count_t	gsm610_write_i	(SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
+static sf_count_t	gsm610_write_f	(SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
+static sf_count_t	gsm610_write_d	(SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
+
+static	int gsm610_read_block	(SF_PRIVATE *psf, GSM610_PRIVATE *pgsm610, short *ptr, int len) ;
+static	int gsm610_write_block	(SF_PRIVATE *psf, GSM610_PRIVATE *pgsm610, const short *ptr, int len) ;
+
+static	int	gsm610_decode_block	(SF_PRIVATE *psf, GSM610_PRIVATE *pgsm610) ;
+static	int	gsm610_encode_block	(SF_PRIVATE *psf, GSM610_PRIVATE *pgsm610) ;
+
+static	int	gsm610_wav_decode_block	(SF_PRIVATE *psf, GSM610_PRIVATE *pgsm610) ;
+static	int	gsm610_wav_encode_block	(SF_PRIVATE *psf, GSM610_PRIVATE *pgsm610) ;
+
+static sf_count_t	gsm610_seek	(SF_PRIVATE *psf, int mode, sf_count_t offset) ;
+
+static int	gsm610_close	(SF_PRIVATE *psf) ;
+
+/*============================================================================================
+** WAV GSM610 initialisation function.
+*/
+
+int
+gsm610_init	(SF_PRIVATE *psf)
+{	GSM610_PRIVATE	*pgsm610 ;
+	int		true_flag = 1 ;
+
+	if (psf->codec_data != NULL)
+	{	psf_log_printf (psf, "*** psf->codec_data is not NULL.\n") ;
+		return SFE_INTERNAL ;
+		} ;
+
+	if (psf->file.mode == SFM_RDWR)
+		return SFE_BAD_MODE_RW ;
+
+	psf->sf.seekable = SF_FALSE ;
+
+	if ((pgsm610 = calloc (1, sizeof (GSM610_PRIVATE))) == NULL)
+		return SFE_MALLOC_FAILED ;
+
+	psf->codec_data = pgsm610 ;
+
+	memset (pgsm610, 0, sizeof (GSM610_PRIVATE)) ;
+
+/*============================================================
+
+Need separate gsm_data structs for encode and decode.
+
+============================================================*/
+
+	if ((pgsm610->gsm_data = gsm_create ()) == NULL)
+		return SFE_MALLOC_FAILED ;
+
+	switch (SF_CONTAINER (psf->sf.format))
+	{	case SF_FORMAT_WAV :
+		case SF_FORMAT_WAVEX :
+		case SF_FORMAT_W64 :
+			gsm_option (pgsm610->gsm_data, GSM_OPT_WAV49, &true_flag) ;
+
+			pgsm610->encode_block = gsm610_wav_encode_block ;
+			pgsm610->decode_block = gsm610_wav_decode_block ;
+
+			pgsm610->samplesperblock = WAVLIKE_GSM610_SAMPLES ;
+			pgsm610->blocksize = WAVLIKE_GSM610_BLOCKSIZE ;
+			break ;
+
+		case SF_FORMAT_AIFF :
+		case SF_FORMAT_RAW :
+			pgsm610->encode_block = gsm610_encode_block ;
+			pgsm610->decode_block = gsm610_decode_block ;
+
+			pgsm610->samplesperblock = GSM610_SAMPLES ;
+			pgsm610->blocksize = GSM610_BLOCKSIZE ;
+			break ;
+
+		default :
+			return SFE_INTERNAL ;
+			break ;
+		} ;
+
+	if (psf->file.mode == SFM_READ)
+	{	if (psf->datalength % pgsm610->blocksize == 0)
+			pgsm610->blocks = psf->datalength / pgsm610->blocksize ;
+		else if (psf->datalength % pgsm610->blocksize == 1 && pgsm610->blocksize == GSM610_BLOCKSIZE)
+		{	/*
+			**	Weird AIFF specific case.
+			**	AIFF chunks must be at an even offset from the start of file and
+			**	GSM610_BLOCKSIZE is odd which can result in an odd length SSND
+			**	chunk. The SSND chunk then gets padded on write which means that
+			**	when it is read the datalength is too big by 1.
+			*/
+			pgsm610->blocks = psf->datalength / pgsm610->blocksize ;
+			}
+		else
+		{	psf_log_printf (psf, "*** Warning : data chunk seems to be truncated.\n") ;
+			pgsm610->blocks = psf->datalength / pgsm610->blocksize + 1 ;
+			} ;
+
+		psf->sf.frames = (sf_count_t) pgsm610->samplesperblock * pgsm610->blocks ;
+
+		psf_fseek (psf, psf->dataoffset, SEEK_SET) ;
+
+		pgsm610->decode_block (psf, pgsm610) ;	/* Read first block. */
+
+		psf->read_short		= gsm610_read_s ;
+		psf->read_int		= gsm610_read_i ;
+		psf->read_float		= gsm610_read_f ;
+		psf->read_double	= gsm610_read_d ;
+		} ;
+
+	if (psf->file.mode == SFM_WRITE)
+	{	pgsm610->blockcount = 0 ;
+		pgsm610->samplecount = 0 ;
+
+		psf->write_short	= gsm610_write_s ;
+		psf->write_int		= gsm610_write_i ;
+		psf->write_float	= gsm610_write_f ;
+		psf->write_double	= gsm610_write_d ;
+		} ;
+
+	psf->codec_close = gsm610_close ;
+
+	psf->seek = gsm610_seek ;
+
+	psf->filelength = psf_get_filelen (psf) ;
+	psf->datalength = psf->filelength - psf->dataoffset ;
+
+	return 0 ;
+} /* gsm610_init */
+
+/*============================================================================================
+** GSM 6.10 Read Functions.
+*/
+
+static int
+gsm610_wav_decode_block	(SF_PRIVATE *psf, GSM610_PRIVATE *pgsm610)
+{	int	k ;
+
+	pgsm610->blockcount ++ ;
+	pgsm610->samplecount = 0 ;
+
+	if (pgsm610->blockcount > pgsm610->blocks)
+	{	memset (pgsm610->samples, 0, sizeof (pgsm610->samples)) ;
+		return 1 ;
+		} ;
+
+	if ((k = (int) psf_fread (pgsm610->block, 1, WAVLIKE_GSM610_BLOCKSIZE, psf)) != WAVLIKE_GSM610_BLOCKSIZE)
+		psf_log_printf (psf, "*** Warning : short read (%d != %d).\n", k, WAVLIKE_GSM610_BLOCKSIZE) ;
+
+	if (gsm_decode (pgsm610->gsm_data, pgsm610->block, pgsm610->samples) < 0)
+	{	psf_log_printf (psf, "Error from WAV gsm_decode() on frame : %d\n", pgsm610->blockcount) ;
+		return 0 ;
+		} ;
+
+	if (gsm_decode (pgsm610->gsm_data, pgsm610->block + (WAVLIKE_GSM610_BLOCKSIZE + 1) / 2, pgsm610->samples + WAVLIKE_GSM610_SAMPLES / 2) < 0)
+	{	psf_log_printf (psf, "Error from WAV gsm_decode() on frame : %d.5\n", pgsm610->blockcount) ;
+		return 0 ;
+		} ;
+
+	return 1 ;
+} /* gsm610_wav_decode_block */
+
+static int
+gsm610_decode_block	(SF_PRIVATE *psf, GSM610_PRIVATE *pgsm610)
+{	int	k ;
+
+	pgsm610->blockcount ++ ;
+	pgsm610->samplecount = 0 ;
+
+	if (pgsm610->blockcount > pgsm610->blocks)
+	{	memset (pgsm610->samples, 0, sizeof (pgsm610->samples)) ;
+		return 1 ;
+		} ;
+
+	if ((k = (int) psf_fread (pgsm610->block, 1, GSM610_BLOCKSIZE, psf)) != GSM610_BLOCKSIZE)
+		psf_log_printf (psf, "*** Warning : short read (%d != %d).\n", k, GSM610_BLOCKSIZE) ;
+
+	if (gsm_decode (pgsm610->gsm_data, pgsm610->block, pgsm610->samples) < 0)
+	{	psf_log_printf (psf, "Error from standard gsm_decode() on frame : %d\n", pgsm610->blockcount) ;
+		return 0 ;
+		} ;
+
+	return 1 ;
+} /* gsm610_decode_block */
+
+static int
+gsm610_read_block	(SF_PRIVATE *psf, GSM610_PRIVATE *pgsm610, short *ptr, int len)
+{	int	count, total = 0, indx = 0 ;
+
+	while (indx < len)
+	{	if (pgsm610->blockcount >= pgsm610->blocks && pgsm610->samplecount >= pgsm610->samplesperblock)
+		{	memset (ptr + indx, 0, (len - indx) * sizeof (short)) ;
+			return total ;
+			} ;
+
+		if (pgsm610->samplecount >= pgsm610->samplesperblock)
+			pgsm610->decode_block (psf, pgsm610) ;
+
+		count = pgsm610->samplesperblock - pgsm610->samplecount ;
+		count = (len - indx > count) ? count : len - indx ;
+
+		memcpy (&(ptr [indx]), &(pgsm610->samples [pgsm610->samplecount]), count * sizeof (short)) ;
+		indx += count ;
+		pgsm610->samplecount += count ;
+		total = indx ;
+		} ;
+
+	return total ;
+} /* gsm610_read_block */
+
+static sf_count_t
+gsm610_read_s	(SF_PRIVATE *psf, short *ptr, sf_count_t len)
+{	GSM610_PRIVATE 	*pgsm610 ;
+	int			readcount, count ;
+	sf_count_t	total = 0 ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	pgsm610 = (GSM610_PRIVATE*) psf->codec_data ;
+
+	while (len > 0)
+	{	readcount = (len > 0x10000000) ? 0x1000000 : (int) len ;
+
+		count = gsm610_read_block (psf, pgsm610, ptr, readcount) ;
+
+		total += count ;
+		len -= count ;
+
+		if (count != readcount)
+			break ;
+		} ;
+
+	return total ;
+} /* gsm610_read_s */
+
+static sf_count_t
+gsm610_read_i	(SF_PRIVATE *psf, int *ptr, sf_count_t len)
+{	GSM610_PRIVATE *pgsm610 ;
+	BUF_UNION	ubuf ;
+	short		*sptr ;
+	int			k, bufferlen, readcount = 0, count ;
+	sf_count_t	total = 0 ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	pgsm610 = (GSM610_PRIVATE*) psf->codec_data ;
+
+	sptr = ubuf.sbuf ;
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+	while (len > 0)
+	{	readcount = (len >= bufferlen) ? bufferlen : (int) len ;
+		count = gsm610_read_block (psf, pgsm610, sptr, readcount) ;
+		for (k = 0 ; k < readcount ; k++)
+			ptr [total + k] = arith_shift_left (sptr [k], 16) ;
+
+		total += count ;
+		len -= readcount ;
+		} ;
+	return total ;
+} /* gsm610_read_i */
+
+static sf_count_t
+gsm610_read_f	(SF_PRIVATE *psf, float *ptr, sf_count_t len)
+{	GSM610_PRIVATE *pgsm610 ;
+	BUF_UNION	ubuf ;
+	short		*sptr ;
+	int			k, bufferlen, readcount = 0, count ;
+	sf_count_t	total = 0 ;
+	float		normfact ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	pgsm610 = (GSM610_PRIVATE*) psf->codec_data ;
+
+	normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x8000) : 1.0 ;
+
+	sptr = ubuf.sbuf ;
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+	while (len > 0)
+	{	readcount = (len >= bufferlen) ? bufferlen : (int) len ;
+		count = gsm610_read_block (psf, pgsm610, sptr, readcount) ;
+		for (k = 0 ; k < readcount ; k++)
+			ptr [total + k] = normfact * sptr [k] ;
+
+		total += count ;
+		len -= readcount ;
+		} ;
+	return total ;
+} /* gsm610_read_f */
+
+static sf_count_t
+gsm610_read_d	(SF_PRIVATE *psf, double *ptr, sf_count_t len)
+{	GSM610_PRIVATE *pgsm610 ;
+	BUF_UNION	ubuf ;
+	short		*sptr ;
+	int			k, bufferlen, readcount = 0, count ;
+	sf_count_t	total = 0 ;
+	double		normfact ;
+
+	normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((double) 0x8000) : 1.0 ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	pgsm610 = (GSM610_PRIVATE*) psf->codec_data ;
+
+	sptr = ubuf.sbuf ;
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+	while (len > 0)
+	{	readcount = (len >= bufferlen) ? bufferlen : (int) len ;
+		count = gsm610_read_block (psf, pgsm610, sptr, readcount) ;
+		for (k = 0 ; k < readcount ; k++)
+			ptr [total + k] = normfact * sptr [k] ;
+
+		total += count ;
+		len -= readcount ;
+		} ;
+	return total ;
+} /* gsm610_read_d */
+
+static sf_count_t
+gsm610_seek	(SF_PRIVATE *psf, int UNUSED (mode), sf_count_t offset)
+{	GSM610_PRIVATE *pgsm610 ;
+	int			newblock, newsample ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	pgsm610 = (GSM610_PRIVATE*) psf->codec_data ;
+
+	if (psf->dataoffset < 0)
+	{	psf->error = SFE_BAD_SEEK ;
+		return	PSF_SEEK_ERROR ;
+		} ;
+
+	if (offset == 0)
+	{	int true_flag = 1 ;
+
+		psf_fseek (psf, psf->dataoffset, SEEK_SET) ;
+		pgsm610->blockcount = 0 ;
+
+		gsm_init (pgsm610->gsm_data) ;
+		if ((SF_CONTAINER (psf->sf.format)) == SF_FORMAT_WAV ||
+				(SF_CONTAINER (psf->sf.format)) == SF_FORMAT_W64)
+			gsm_option (pgsm610->gsm_data, GSM_OPT_WAV49, &true_flag) ;
+
+		pgsm610->decode_block (psf, pgsm610) ;
+		pgsm610->samplecount = 0 ;
+		return 0 ;
+		} ;
+
+	if (offset < 0 || offset > pgsm610->blocks * pgsm610->samplesperblock)
+	{	psf->error = SFE_BAD_SEEK ;
+		return	PSF_SEEK_ERROR ;
+		} ;
+
+	newblock	= offset / pgsm610->samplesperblock ;
+	newsample	= offset % pgsm610->samplesperblock ;
+
+	if (psf->file.mode == SFM_READ)
+	{	if (psf->read_current != newblock * pgsm610->samplesperblock + newsample)
+		{	psf_fseek (psf, psf->dataoffset + newblock * pgsm610->samplesperblock, SEEK_SET) ;
+			pgsm610->blockcount = newblock ;
+			pgsm610->decode_block (psf, pgsm610) ;
+			pgsm610->samplecount = newsample ;
+			} ;
+
+		return newblock * pgsm610->samplesperblock + newsample ;
+		} ;
+
+	/* What to do about write??? */
+	psf->error = SFE_BAD_SEEK ;
+	return	PSF_SEEK_ERROR ;
+} /* gsm610_seek */
+
+/*==========================================================================================
+** GSM 6.10 Write Functions.
+*/
+
+static int
+gsm610_encode_block	(SF_PRIVATE *psf, GSM610_PRIVATE *pgsm610)
+{	int k ;
+
+	/* Encode the samples. */
+	gsm_encode (pgsm610->gsm_data, pgsm610->samples, pgsm610->block) ;
+
+	/* Write the block to disk. */
+	if ((k = (int) psf_fwrite (pgsm610->block, 1, GSM610_BLOCKSIZE, psf)) != GSM610_BLOCKSIZE)
+		psf_log_printf (psf, "*** Warning : short write (%d != %d).\n", k, GSM610_BLOCKSIZE) ;
+
+	pgsm610->samplecount = 0 ;
+	pgsm610->blockcount ++ ;
+
+	/* Set samples to zero for next block. */
+	memset (pgsm610->samples, 0, sizeof (pgsm610->samples)) ;
+
+	return 1 ;
+} /* gsm610_encode_block */
+
+static int
+gsm610_wav_encode_block	(SF_PRIVATE *psf, GSM610_PRIVATE *pgsm610)
+{	int k ;
+
+	/* Encode the samples. */
+	gsm_encode (pgsm610->gsm_data, pgsm610->samples, pgsm610->block) ;
+	gsm_encode (pgsm610->gsm_data, pgsm610->samples+WAVLIKE_GSM610_SAMPLES / 2, pgsm610->block+WAVLIKE_GSM610_BLOCKSIZE / 2) ;
+
+	/* Write the block to disk. */
+	if ((k = (int) psf_fwrite (pgsm610->block, 1, WAVLIKE_GSM610_BLOCKSIZE, psf)) != WAVLIKE_GSM610_BLOCKSIZE)
+		psf_log_printf (psf, "*** Warning : short write (%d != %d).\n", k, WAVLIKE_GSM610_BLOCKSIZE) ;
+
+	pgsm610->samplecount = 0 ;
+	pgsm610->blockcount ++ ;
+
+	/* Set samples to zero for next block. */
+	memset (pgsm610->samples, 0, sizeof (pgsm610->samples)) ;
+
+	return 1 ;
+} /* gsm610_wav_encode_block */
+
+static int
+gsm610_write_block	(SF_PRIVATE *psf, GSM610_PRIVATE *pgsm610, const short *ptr, int len)
+{	int		count, total = 0, indx = 0 ;
+
+	while (indx < len)
+	{	count = pgsm610->samplesperblock - pgsm610->samplecount ;
+
+		if (count > len - indx)
+			count = len - indx ;
+
+		memcpy (&(pgsm610->samples [pgsm610->samplecount]), &(ptr [indx]), count * sizeof (short)) ;
+		indx += count ;
+		pgsm610->samplecount += count ;
+		total = indx ;
+
+		if (pgsm610->samplecount >= pgsm610->samplesperblock)
+			pgsm610->encode_block (psf, pgsm610) ;
+		} ;
+
+	return total ;
+} /* gsm610_write_block */
+
+static sf_count_t
+gsm610_write_s	(SF_PRIVATE *psf, const short *ptr, sf_count_t len)
+{	GSM610_PRIVATE 	*pgsm610 ;
+	int			writecount, count ;
+	sf_count_t	total = 0 ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	pgsm610 = (GSM610_PRIVATE*) psf->codec_data ;
+
+	while (len > 0)
+	{	writecount = (len > 0x10000000) ? 0x10000000 : (int) len ;
+
+		count = gsm610_write_block (psf, pgsm610, ptr, writecount) ;
+
+		total += count ;
+		len -= count ;
+
+		if (count != writecount)
+			break ;
+		} ;
+
+	return total ;
+} /* gsm610_write_s */
+
+static sf_count_t
+gsm610_write_i	(SF_PRIVATE *psf, const int *ptr, sf_count_t len)
+{	GSM610_PRIVATE *pgsm610 ;
+	BUF_UNION	ubuf ;
+	short		*sptr ;
+	int			k, bufferlen, writecount = 0, count ;
+	sf_count_t	total = 0 ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	pgsm610 = (GSM610_PRIVATE*) psf->codec_data ;
+
+	sptr = ubuf.sbuf ;
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+	while (len > 0)
+	{	writecount = (len >= bufferlen) ? bufferlen : (int) len ;
+		for (k = 0 ; k < writecount ; k++)
+			sptr [k] = ptr [total + k] >> 16 ;
+		count = gsm610_write_block (psf, pgsm610, sptr, writecount) ;
+
+		total += count ;
+		len -= writecount ;
+		} ;
+	return total ;
+} /* gsm610_write_i */
+
+static sf_count_t
+gsm610_write_f	(SF_PRIVATE *psf, const float *ptr, sf_count_t len)
+{	GSM610_PRIVATE *pgsm610 ;
+	BUF_UNION	ubuf ;
+	short		*sptr ;
+	int			k, bufferlen, writecount = 0, count ;
+	sf_count_t	total = 0 ;
+	float		normfact ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	pgsm610 = (GSM610_PRIVATE*) psf->codec_data ;
+
+	normfact = (psf->norm_float == SF_TRUE) ? (1.0 * 0x7FFF) : 1.0 ;
+
+	sptr = ubuf.sbuf ;
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+	while (len > 0)
+	{	writecount = (len >= bufferlen) ? bufferlen : (int) len ;
+		for (k = 0 ; k < writecount ; k++)
+			sptr [k] = psf_lrintf (normfact * ptr [total + k]) ;
+		count = gsm610_write_block (psf, pgsm610, sptr, writecount) ;
+
+		total += count ;
+		len -= writecount ;
+		if (count != writecount)
+			break ;
+		} ;
+	return total ;
+} /* gsm610_write_f */
+
+static sf_count_t
+gsm610_write_d	(SF_PRIVATE *psf, const double *ptr, sf_count_t len)
+{	GSM610_PRIVATE *pgsm610 ;
+	BUF_UNION	ubuf ;
+	short		*sptr ;
+	int			k, bufferlen, writecount = 0, count ;
+	sf_count_t	total = 0 ;
+	double		normfact ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	pgsm610 = (GSM610_PRIVATE*) psf->codec_data ;
+
+	normfact = (psf->norm_double == SF_TRUE) ? (1.0 * 0x7FFF) : 1.0 ;
+
+	sptr = ubuf.sbuf ;
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+	while (len > 0)
+	{	writecount = (len >= bufferlen) ? bufferlen : (int) len ;
+		for (k = 0 ; k < writecount ; k++)
+			sptr [k] = psf_lrint (normfact * ptr [total + k]) ;
+		count = gsm610_write_block (psf, pgsm610, sptr, writecount) ;
+
+		total += count ;
+		len -= writecount ;
+		} ;
+	return total ;
+} /* gsm610_write_d */
+
+static int
+gsm610_close	(SF_PRIVATE *psf)
+{	GSM610_PRIVATE *pgsm610 ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+
+	pgsm610 = (GSM610_PRIVATE*) psf->codec_data ;
+
+	if (psf->file.mode == SFM_WRITE)
+	{	/*	If a block has been partially assembled, write it out
+		**	as the final block.
+		*/
+
+		if (pgsm610->samplecount && pgsm610->samplecount < pgsm610->samplesperblock)
+			pgsm610->encode_block (psf, pgsm610) ;
+		} ;
+
+	if (pgsm610->gsm_data)
+		gsm_destroy (pgsm610->gsm_data) ;
+
+	return 0 ;
+} /* gsm610_close */
+
diff --git a/src/htk.c b/src/htk.c
new file mode 100644
index 0000000..1041a84
--- /dev/null
+++ b/src/htk.c
@@ -0,0 +1,226 @@
+/*
+** Copyright (C) 2002-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<fcntl.h>
+#include	<string.h>
+#include	<ctype.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+/*------------------------------------------------------------------------------
+** Macros to handle big/little endian issues.
+*/
+
+#define	SFE_HTK_BAD_FILE_LEN 	1666
+#define	SFE_HTK_NOT_WAVEFORM	1667
+
+/*------------------------------------------------------------------------------
+** Private static functions.
+*/
+
+static	int		htk_close		(SF_PRIVATE *psf) ;
+
+static int		htk_write_header (SF_PRIVATE *psf, int calc_length) ;
+static int		htk_read_header (SF_PRIVATE *psf) ;
+
+/*------------------------------------------------------------------------------
+** Public function.
+*/
+
+int
+htk_open	(SF_PRIVATE *psf)
+{	int		subformat ;
+	int		error = 0 ;
+
+	if (psf->is_pipe)
+		return SFE_HTK_NO_PIPE ;
+
+	if (psf->file.mode == SFM_READ || (psf->file.mode == SFM_RDWR && psf->filelength > 0))
+	{	if ((error = htk_read_header (psf)))
+			return error ;
+		} ;
+
+	subformat = SF_CODEC (psf->sf.format) ;
+
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	if ((SF_CONTAINER (psf->sf.format)) != SF_FORMAT_HTK)
+			return	SFE_BAD_OPEN_FORMAT ;
+
+		psf->endian = SF_ENDIAN_BIG ;
+
+		if (htk_write_header (psf, SF_FALSE))
+			return psf->error ;
+
+		psf->write_header = htk_write_header ;
+		} ;
+
+	psf->container_close = htk_close ;
+
+	psf->blockwidth = psf->bytewidth * psf->sf.channels ;
+
+	switch (subformat)
+	{	case SF_FORMAT_PCM_16 :	/* 16-bit linear PCM. */
+				error = pcm_init (psf) ;
+				break ;
+
+		default : break ;
+		} ;
+
+	return error ;
+} /* htk_open */
+
+/*------------------------------------------------------------------------------
+*/
+
+static int
+htk_close	(SF_PRIVATE *psf)
+{
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+		htk_write_header (psf, SF_TRUE) ;
+
+	return 0 ;
+} /* htk_close */
+
+static int
+htk_write_header (SF_PRIVATE *psf, int calc_length)
+{	sf_count_t	current ;
+	int			sample_count, sample_period ;
+
+	current = psf_ftell (psf) ;
+
+	if (calc_length)
+		psf->filelength = psf_get_filelen (psf) ;
+
+	/* Reset the current header length to zero. */
+	psf->header.ptr [0] = 0 ;
+	psf->header.indx = 0 ;
+	psf_fseek (psf, 0, SEEK_SET) ;
+
+	if (psf->filelength > 12)
+		sample_count = (psf->filelength - 12) / 2 ;
+	else
+		sample_count = 0 ;
+
+	sample_period = 10000000 / psf->sf.samplerate ;
+
+	psf_binheader_writef (psf, "E444", BHW4 (sample_count), BHW4 (sample_period), BHW4 (0x20000)) ;
+
+	/* Header construction complete so write it out. */
+	psf_fwrite (psf->header.ptr, psf->header.indx, 1, psf) ;
+
+	if (psf->error)
+		return psf->error ;
+
+	psf->dataoffset = psf->header.indx ;
+
+	if (current > 0)
+		psf_fseek (psf, current, SEEK_SET) ;
+
+	return psf->error ;
+} /* htk_write_header */
+
+/*
+** Found the following info in a comment block within Bill Schottstaedt's
+** sndlib library.
+**
+** HTK format files consist of a contiguous sequence of samples preceded by a
+** header. Each sample is a vector of either 2-byte integers or 4-byte floats.
+** 2-byte integers are used for compressed forms as described below and for
+** vector quantised data as described later in section 5.11. HTK format data
+** files can also be used to store speech waveforms as described in section 5.8.
+**
+** The HTK file format header is 12 bytes long and contains the following data
+**   nSamples   -- number of samples in file (4-byte integer)
+**   sampPeriod -- sample period in 100ns units (4-byte integer)
+**   sampSize   -- number of bytes per sample (2-byte integer)
+**   parmKind   -- a code indicating the sample kind (2-byte integer)
+**
+** The parameter kind  consists of a 6 bit code representing the basic
+** parameter kind plus additional bits for each of the possible qualifiers.
+** The basic parameter kind codes are
+**
+**  0    WAVEFORM    sampled waveform
+**  1    LPC         linear prediction filter coefficients
+**  2    LPREFC      linear prediction reflection coefficients
+**  3    LPCEPSTRA   LPC cepstral coefficients
+**  4    LPDELCEP    LPC cepstra plus delta coefficients
+**  5    IREFC       LPC reflection coef in 16 bit integer format
+**  6    MFCC        mel-frequency cepstral coefficients
+**  7    FBANK       log mel-filter bank channel outputs
+**  8    MELSPEC     linear mel-filter bank channel outputs
+**  9    USER        user defined sample kind
+**  10   DISCRETE    vector quantised data
+**
+** and the bit-encoding for the qualifiers (in octal) is
+**   _E   000100      has energy
+**   _N   000200      absolute energy suppressed
+**   _D   000400      has delta coefficients
+**   _A   001000      has acceleration coefficients
+**   _C   002000      is compressed
+**   _Z   004000      has zero mean static coef.
+**   _K   010000      has CRC checksum
+**   _O   020000      has 0'th cepstral coef.
+*/
+
+static int
+htk_read_header (SF_PRIVATE *psf)
+{	int		sample_count, sample_period, marker ;
+
+	psf_binheader_readf (psf, "pE444", 0, &sample_count, &sample_period, &marker) ;
+
+	if (2 * sample_count + 12 != psf->filelength)
+		return SFE_HTK_BAD_FILE_LEN ;
+
+	if (marker != 0x20000)
+		return SFE_HTK_NOT_WAVEFORM ;
+
+	psf->sf.channels = 1 ;
+
+	if (sample_period > 0)
+	{	psf->sf.samplerate = 10000000 / sample_period ;
+		psf_log_printf (psf, "HTK Waveform file\n  Sample Count  : %d\n  Sample Period : %d => %d Hz\n",
+					sample_count, sample_period, psf->sf.samplerate) ;
+		}
+	else
+	{	psf->sf.samplerate = 16000 ;
+		psf_log_printf (psf, "HTK Waveform file\n  Sample Count  : %d\n  Sample Period : %d (should be > 0) => Guessed sample rate %d Hz\n",
+					sample_count, sample_period, psf->sf.samplerate) ;
+		} ;
+
+	psf->sf.format = SF_FORMAT_HTK | SF_FORMAT_PCM_16 ;
+	psf->bytewidth = 2 ;
+
+	/* HTK always has a 12 byte header. */
+	psf->dataoffset = 12 ;
+	psf->endian = SF_ENDIAN_BIG ;
+
+	psf->datalength = psf->filelength - psf->dataoffset ;
+
+	psf->blockwidth = psf->sf.channels * psf->bytewidth ;
+
+	if (! psf->sf.frames && psf->blockwidth)
+		psf->sf.frames = (psf->filelength - psf->dataoffset) / psf->blockwidth ;
+
+	return 0 ;
+} /* htk_read_header */
+
diff --git a/src/id3.c b/src/id3.c
new file mode 100644
index 0000000..1516ee3
--- /dev/null
+++ b/src/id3.c
@@ -0,0 +1,132 @@
+/*
+** Copyright (C) 2010-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<fcntl.h>
+#include	<string.h>
+#include	<ctype.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+#include	"id3.h"
+
+#if HAVE_MPEG
+#include	<lame/lame.h>
+
+struct id3v1_genre_handler_userdata
+{	int number ;
+	const char *ret ;
+} ;
+
+static void
+id3v1_genre_handler (int number, const char *description, void *userdata)
+{	struct id3v1_genre_handler_userdata *data = (struct id3v1_genre_handler_userdata *) userdata ;
+	if (data->number == number)
+		data->ret = description ;
+}
+
+const char *
+id3_lookup_v1_genre (int number)
+{	struct id3v1_genre_handler_userdata data ;
+
+	data.number = number ;
+	data.ret = NULL ;
+	id3tag_genre_list (id3v1_genre_handler, &data) ;
+
+	return data.ret ;
+}
+
+#else /* HAVE_MPEG */
+
+const char *
+id3_lookup_v1_genre (int UNUSED (number))
+{	return NULL ;
+	}
+
+#endif
+
+int
+id3_skip (SF_PRIVATE * psf)
+{	unsigned char	buf [10] ;
+	int	offset ;
+
+	memset (buf, 0, sizeof (buf)) ;
+	psf_binheader_readf (psf, "pb", 0, buf, 10) ;
+
+	if (buf [0] == 'I' && buf [1] == 'D' && buf [2] == '3')
+	{	psf->id3_header.minor_version = buf [3] ;
+		offset = buf [6] & 0x7f ;
+		offset = (offset << 7) | (buf [7] & 0x7f) ;
+		offset = (offset << 7) | (buf [8] & 0x7f) ;
+		offset = (offset << 7) | (buf [9] & 0x7f) ;
+
+		/*
+		** ID3 count field is how many bytes of ID3v2 header FOLLOW the ten
+		** bytes of header magic and offset, NOT the total ID3v2 header len.
+		*/
+		psf->id3_header.len = offset + 10 ;
+		psf->id3_header.offset = psf->fileoffset ;
+
+		psf_log_printf (psf, "  ID3v2.%d header length :	%d\n----------------------------------------\n",
+			psf->id3_header.minor_version, psf->id3_header.len) ;
+
+		/* Never want to jump backwards in a file. */
+		if (offset < 0)
+			return 0 ;
+
+		/* Position ourselves at the new file offset. */
+		if (psf->fileoffset + psf->id3_header.len < psf->filelength)
+		{	psf_binheader_readf (psf, "p!", psf->id3_header.len) ;
+			psf->fileoffset += psf->id3_header.len ;
+			return 1 ;
+			} ;
+		} ;
+
+	return 0 ;
+} /* id3_skip */
+
+const char *
+id3_process_v2_genre (const char *genre)
+{	int num = 0 ;
+	char c ;
+	const char *ptr ;
+
+	if (!genre)
+		return NULL ;
+
+	/*
+	** Genre may require more processing.
+	**
+	** It is allowed to have numeric references to the genre table from ID3v1.
+	** We'll just convert the simple case here, strings of the format "(nnn)".
+	*/
+	ptr = genre ;
+	if (ptr [0] == '(' && (c = *++ ptr) && isdigit (c))
+	{	num = c - '0' ;
+		while ((c == *++ ptr) && isdigit (c))
+			num = num * 10 + (c - '0') ;
+		if (c == ')' && (c = *++ ptr) == '\0' && num < 256)
+			if ((ptr = id3_lookup_v1_genre (num)))
+				return ptr ;
+		} ;
+
+	return genre ;
+} /* id3_process_v2_genre */
diff --git a/src/id3.h b/src/id3.h
new file mode 100644
index 0000000..4f54c4f
--- /dev/null
+++ b/src/id3.h
@@ -0,0 +1,29 @@
+/*
+** Copyright (C) 2008-2019 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2019 Arthur Taylor <art@ified.ca>
+**
+** This program is free software ; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation ; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program ; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef SF_SRC_ID3_H
+#define SF_SRC_ID3_H
+
+int id3_skip (SF_PRIVATE * psf) ;
+
+const char *id3_lookup_v1_genre (int number) ;
+
+const char *id3_process_v2_genre (const char *genre) ;
+
+#endif /* SF_SRC_ID3_H */
diff --git a/src/ima_adpcm.c b/src/ima_adpcm.c
new file mode 100644
index 0000000..bc61f4e
--- /dev/null
+++ b/src/ima_adpcm.c
@@ -0,0 +1,1008 @@
+/*
+** Copyright (C) 1999-2020 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<stdlib.h>
+#include	<string.h>
+#include	<math.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+typedef struct IMA_ADPCM_PRIVATE_tag
+{	int 			(*decode_block) (SF_PRIVATE *psf, struct IMA_ADPCM_PRIVATE_tag *pima) ;
+	int 			(*encode_block) (SF_PRIVATE *psf, struct IMA_ADPCM_PRIVATE_tag *pima) ;
+
+	int				channels, blocksize, samplesperblock, blocks ;
+	int				blockcount, samplecount ;
+	int				previous [2] ;
+	int				stepindx [2] ;
+	unsigned char	*block ;
+	short			*samples ;
+	short			data	[] ; /* ISO C99 struct flexible array. */
+} IMA_ADPCM_PRIVATE ;
+
+/*============================================================================================
+** Predefined IMA ADPCM data.
+*/
+
+static int ima_indx_adjust [16] =
+{	-1, -1, -1, -1,		/* +0 - +3, decrease the step size */
+	+2, +4, +6, +8,		/* +4 - +7, increase the step size */
+	-1, -1, -1, -1,		/* -0 - -3, decrease the step size */
+	+2, +4, +6, +8,		/* -4 - -7, increase the step size */
+} ;
+
+static int ima_step_size [89] =
+{	7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
+	50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230,
+	253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963,
+	1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327,
+	3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442,
+	11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
+	32767
+} ;
+
+static int ima_reader_init (SF_PRIVATE *psf, int blockalign, int samplesperblock) ;
+static int ima_writer_init (SF_PRIVATE *psf, int blockalign) ;
+
+static int ima_read_block (SF_PRIVATE *psf, IMA_ADPCM_PRIVATE *pima, short *ptr, int len) ;
+static int ima_write_block (SF_PRIVATE *psf, IMA_ADPCM_PRIVATE *pima, const short *ptr, int len) ;
+
+static sf_count_t ima_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
+static sf_count_t ima_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
+static sf_count_t ima_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
+static sf_count_t ima_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
+
+static sf_count_t ima_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
+static sf_count_t ima_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
+static sf_count_t ima_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
+static sf_count_t ima_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
+
+static sf_count_t aiff_ima_seek		(SF_PRIVATE *psf, int mode, sf_count_t offset) ;
+static sf_count_t wavlike_ima_seek	(SF_PRIVATE *psf, int mode, sf_count_t offset) ;
+
+static int	ima_close	(SF_PRIVATE *psf) ;
+
+static int wavlike_ima_decode_block (SF_PRIVATE *psf, IMA_ADPCM_PRIVATE *pima) ;
+static int wavlike_ima_encode_block (SF_PRIVATE *psf, IMA_ADPCM_PRIVATE *pima) ;
+
+/*-static int aiff_ima_reader_init (SF_PRIVATE *psf, int blockalign, int samplesperblock) ;-*/
+static int aiff_ima_decode_block (SF_PRIVATE *psf, IMA_ADPCM_PRIVATE *pima) ;
+static int aiff_ima_encode_block (SF_PRIVATE *psf, IMA_ADPCM_PRIVATE *pima) ;
+
+
+static inline int
+clamp_ima_step_index (int indx)
+{	if (indx < 0)
+		return 0 ;
+	if (indx >= ARRAY_LEN (ima_step_size))
+		return ARRAY_LEN (ima_step_size) - 1 ;
+
+	return indx ;
+} /* clamp_ima_step_index */
+
+/*============================================================================================
+** IMA ADPCM Reader initialisation function.
+*/
+
+int
+wavlike_ima_init (SF_PRIVATE *psf, int blockalign, int samplesperblock)
+{	int error ;
+
+	if (psf->codec_data != NULL)
+	{	psf_log_printf (psf, "*** psf->codec_data is not NULL.\n") ;
+		return SFE_INTERNAL ;
+		} ;
+
+	if (psf->file.mode == SFM_RDWR)
+		return SFE_BAD_MODE_RW ;
+
+	if (psf->file.mode == SFM_READ)
+		if ((error = ima_reader_init (psf, blockalign, samplesperblock)))
+			return error ;
+
+	if (psf->file.mode == SFM_WRITE)
+		if ((error = ima_writer_init (psf, blockalign)))
+			return error ;
+
+	psf->codec_close = ima_close ;
+	psf->seek = wavlike_ima_seek ;
+
+	return 0 ;
+} /* wavlike_ima_init */
+
+int
+aiff_ima_init (SF_PRIVATE *psf, int blockalign, int samplesperblock)
+{	int error ;
+
+	if (psf->file.mode == SFM_RDWR)
+		return SFE_BAD_MODE_RW ;
+
+	if (psf->file.mode == SFM_READ)
+		if ((error = ima_reader_init (psf, blockalign, samplesperblock)))
+			return error ;
+
+	if (psf->file.mode == SFM_WRITE)
+		if ((error = ima_writer_init (psf, blockalign)))
+			return error ;
+
+	psf->codec_close = ima_close ;
+	psf->seek = aiff_ima_seek ;
+
+	return 0 ;
+} /* aiff_ima_init */
+
+static int
+ima_close	(SF_PRIVATE *psf)
+{	IMA_ADPCM_PRIVATE *pima ;
+
+	pima = (IMA_ADPCM_PRIVATE*) psf->codec_data ;
+
+	if (psf->file.mode == SFM_WRITE)
+	{	/*	If a block has been partially assembled, write it out
+		**	as the final block.
+		*/
+		if (pima->samplecount && pima->samplecount < pima->samplesperblock)
+			pima->encode_block (psf, pima) ;
+
+		psf->sf.frames = pima->samplesperblock * pima->blockcount / psf->sf.channels ;
+		} ;
+
+	return 0 ;
+} /* ima_close */
+
+/*============================================================================================
+** IMA ADPCM Read Functions.
+*/
+
+static int
+ima_reader_init (SF_PRIVATE *psf, int blockalign, int samplesperblock)
+{	IMA_ADPCM_PRIVATE	*pima ;
+	int		pimasize, count ;
+
+	if (psf->file.mode != SFM_READ)
+		return SFE_BAD_MODE_RW ;
+
+	/*
+	**	Allocate enough space for 1 more than a multiple of 8 samples
+	**	to avoid having to branch when pulling apart the nibbles.
+	*/
+	count = ((samplesperblock - 2) | 7) + 2 ;
+	pimasize = sizeof (IMA_ADPCM_PRIVATE) + psf->sf.channels * (blockalign + samplesperblock + sizeof(short) * count) ;
+
+	if (! (pima = calloc (1, pimasize)))
+		return SFE_MALLOC_FAILED ;
+
+	psf->codec_data = (void*) pima ;
+
+	pima->samples	= pima->data ;
+	pima->block		= (unsigned char*) (pima->data + samplesperblock * psf->sf.channels) ;
+
+	pima->channels			= psf->sf.channels ;
+	pima->blocksize			= blockalign ;
+	pima->samplesperblock	= samplesperblock ;
+
+	psf->filelength = psf_get_filelen (psf) ;
+	psf->datalength = (psf->dataend) ? psf->dataend - psf->dataoffset :
+							psf->filelength - psf->dataoffset ;
+
+	if (pima->blocksize <= 0)
+	{	psf_log_printf (psf, "*** Error : pima->blocksize should be > 0.\n") ;
+		return SFE_INTERNAL ;
+		} ;
+
+	if (pima->samplesperblock <= 0)
+	{	psf_log_printf (psf, "*** Error : pima->samplesperblock should be > 0.\n") ;
+		return SFE_INTERNAL ;
+		} ;
+
+	if (psf->datalength % pima->blocksize)
+		pima->blocks = psf->datalength / pima->blocksize + 1 ;
+	else
+		pima->blocks = psf->datalength / pima->blocksize ;
+
+	switch (SF_CONTAINER (psf->sf.format))
+	{	case SF_FORMAT_WAV :
+		case SF_FORMAT_W64 :
+				count = 2 * (pima->blocksize - 4 * pima->channels) / pima->channels + 1 ;
+
+				if (pima->samplesperblock != count)
+				{	psf_log_printf (psf, "*** Error : samplesperblock should be %d.\n", count) ;
+					return SFE_INTERNAL ;
+					} ;
+
+				pima->decode_block = wavlike_ima_decode_block ;
+
+				psf->sf.frames = pima->samplesperblock * pima->blocks ;
+				break ;
+
+		case SF_FORMAT_AIFF :
+				psf_log_printf (psf, "still need to check block count\n") ;
+				pima->decode_block = aiff_ima_decode_block ;
+				psf->sf.frames = pima->samplesperblock * pima->blocks / pima->channels ;
+				break ;
+
+		default :
+				psf_log_printf (psf, "ima_reader_init: bad psf->sf.format\n") ;
+				return SFE_INTERNAL ;
+		} ;
+
+	pima->decode_block (psf, pima) ;	/* Read first block. */
+
+	psf->read_short		= ima_read_s ;
+	psf->read_int		= ima_read_i ;
+	psf->read_float		= ima_read_f ;
+	psf->read_double	= ima_read_d ;
+
+	return 0 ;
+} /* ima_reader_init */
+
+static int
+aiff_ima_decode_block (SF_PRIVATE *psf, IMA_ADPCM_PRIVATE *pima)
+{	unsigned char *blockdata ;
+	int		chan, k, diff, bytecode, predictor ;
+	short	step, stepindx, *sampledata ;
+
+static int count = 0 ;
+count ++ ;
+
+	pima->blockcount += pima->channels ;
+	pima->samplecount = 0 ;
+
+	if (pima->blockcount > pima->blocks)
+	{	memset (pima->samples, 0, pima->samplesperblock * pima->channels * sizeof (short)) ;
+		return 1 ;
+		} ;
+
+	if ((k = (int) psf_fread (pima->block, 1, pima->blocksize * pima->channels, psf)) != pima->blocksize * pima->channels)
+		psf_log_printf (psf, "*** Warning : short read (%d != %d).\n", k, pima->blocksize) ;
+
+	/* Read and check the block header. */
+	for (chan = 0 ; chan < pima->channels ; chan++)
+	{	blockdata = pima->block + chan * 34 ;
+		sampledata = pima->samples + chan ;
+
+		/* Sign-extend from 16 bits to 32. */
+		predictor = (int) ((short) ((blockdata [0] << 8) | (blockdata [1] & 0x80))) ;
+
+		stepindx = blockdata [1] & 0x7F ;
+		stepindx = clamp_ima_step_index (stepindx) ;
+
+		/*
+		**	Pull apart the packed 4 bit samples and store them in their
+		**	correct sample positions.
+		*/
+		for (k = 0 ; k < pima->blocksize - 2 ; k++)
+		{	bytecode = blockdata [k + 2] ;
+			sampledata [pima->channels * (2 * k + 0)] = bytecode & 0xF ;
+			sampledata [pima->channels * (2 * k + 1)] = (bytecode >> 4) & 0xF ;
+			} ;
+
+		/* Decode the encoded 4 bit samples. */
+		for (k = 0 ; k < pima->samplesperblock ; k ++)
+		{	step = ima_step_size [stepindx] ;
+
+			bytecode = pima->samples [pima->channels * k + chan] ;
+
+			stepindx += ima_indx_adjust [bytecode] ;
+			stepindx = clamp_ima_step_index (stepindx) ;
+
+			diff = step >> 3 ;
+			if (bytecode & 1)	diff += step >> 2 ;
+			if (bytecode & 2)	diff += step >> 1 ;
+			if (bytecode & 4)	diff += step ;
+			if (bytecode & 8)	diff = -diff ;
+
+			predictor += diff ;
+			if (predictor < -32768)
+				predictor = -32768 ;
+			else if (predictor > 32767)
+				predictor = 32767 ;
+
+			pima->samples [pima->channels * k + chan] = predictor ;
+			} ;
+		} ;
+
+	return 1 ;
+} /* aiff_ima_decode_block */
+
+static int
+aiff_ima_encode_block (SF_PRIVATE *psf, IMA_ADPCM_PRIVATE *pima)
+{	int		chan, k, step, diff, vpdiff, blockindx, indx ;
+	short	bytecode, mask ;
+
+	k = 0 ;
+	for (chan = 0 ; chan < pima->channels ; chan ++)
+	{	blockindx = chan * pima->blocksize ;
+		/* Encode the block header. */
+		pima->block [blockindx++] = (pima->previous [chan] >> 8) & 0xFF ;
+		pima->block [blockindx++] = (pima->previous [chan] & 0x80) + (pima->stepindx [chan] & 0x7F) ;
+
+		/* Encode the samples as 4 bit. */
+		for (indx = chan ; indx < pima->samplesperblock * pima->channels ; indx += pima->channels)
+		{	diff = pima->samples [indx] - pima->previous [chan] ;
+
+			bytecode = 0 ;
+			step = ima_step_size [pima->stepindx [chan]] ;
+			vpdiff = step >> 3 ;
+			if (diff < 0)
+			{	bytecode = 8 ;
+				diff = -diff ;
+				} ;
+			mask = 4 ;
+			while (mask)
+			{	if (diff >= step)
+				{	bytecode |= mask ;
+					diff -= step ;
+					vpdiff += step ;
+					} ;
+				step >>= 1 ;
+				mask >>= 1 ;
+				} ;
+
+			if (bytecode & 8)
+				vpdiff = -vpdiff ;
+			pima->previous [chan] += vpdiff ;
+
+			if (pima->previous [chan] > 32767)
+				pima->previous [chan] = 32767 ;
+			else if (pima->previous [chan] < -32768)
+				pima->previous [chan] = -32768 ;
+
+			pima->stepindx [chan] += ima_indx_adjust [bytecode] ;
+
+			pima->stepindx [chan] = clamp_ima_step_index (pima->stepindx [chan]) ;
+			pima->block [blockindx] = (bytecode << (4 * k)) | pima->block [blockindx] ;
+			blockindx += k ;
+			k = 1 - k ;
+			} ;
+		} ;
+
+	/* Write the block to disk. */
+	if ((k = (int) psf_fwrite (pima->block, 1, pima->channels * pima->blocksize, psf)) != pima->channels * pima->blocksize)
+		psf_log_printf (psf, "*** Warning : short write (%d != %d).\n", k, pima->channels * pima->blocksize) ;
+
+	memset (pima->block, 0, pima->channels * pima->blocksize) ;
+	pima->samplecount = 0 ;
+	pima->blockcount ++ ;
+
+	return 1 ;
+} /* aiff_ima_encode_block */
+
+static int
+wavlike_ima_decode_block (SF_PRIVATE *psf, IMA_ADPCM_PRIVATE *pima)
+{	int		chan, k, predictor, blockindx, indx, indxstart, diff ;
+	short	step, bytecode, stepindx [2] = { 0 };
+
+	pima->blockcount ++ ;
+	pima->samplecount = 0 ;
+
+	if (pima->blockcount > pima->blocks)
+	{	memset (pima->samples, 0, pima->samplesperblock * pima->channels * sizeof (short)) ;
+		return 1 ;
+		} ;
+
+	if ((k = (int) psf_fread (pima->block, 1, pima->blocksize, psf)) != pima->blocksize)
+		psf_log_printf (psf, "*** Warning : short read (%d != %d).\n", k, pima->blocksize) ;
+
+	/* Read and check the block header. */
+
+	for (chan = 0 ; chan < pima->channels ; chan++)
+	{	predictor = pima->block [chan*4] | (pima->block [chan*4+1] << 8) ;
+		if (predictor & 0x8000)
+			predictor -= 0x10000 ;
+
+		stepindx [chan] = pima->block [chan*4+2] ;
+		stepindx [chan] = clamp_ima_step_index (stepindx [chan]) ;
+
+
+		if (pima->block [chan*4+3] != 0)
+			psf_log_printf (psf, "IMA ADPCM synchronisation error.\n") ;
+
+		pima->samples [chan] = predictor ;
+		} ;
+
+	/*
+	**	Pull apart the packed 4 bit samples and store them in their
+	**	correct sample positions.
+	*/
+
+	blockindx = 4 * pima->channels ;
+
+	indxstart = pima->channels ;
+	while (blockindx < pima->blocksize)
+	{	for (chan = 0 ; chan < pima->channels ; chan++)
+		{	indx = indxstart + chan ;
+			for (k = 0 ; k < 4 ; k++)
+			{	bytecode = pima->block [blockindx++] ;
+				pima->samples [indx] = bytecode & 0x0F ;
+				indx += pima->channels ;
+				pima->samples [indx] = (bytecode >> 4) & 0x0F ;
+				indx += pima->channels ;
+				} ;
+			} ;
+		indxstart += 8 * pima->channels ;
+		} ;
+
+	/* Decode the encoded 4 bit samples. */
+
+	for (k = pima->channels ; k < (pima->samplesperblock * pima->channels) ; k ++)
+	{	chan = (pima->channels > 1) ? (k % 2) : 0 ;
+
+		bytecode = pima->samples [k] & 0xF ;
+
+		step = ima_step_size [stepindx [chan]] ;
+		predictor = pima->samples [k - pima->channels] ;
+
+		diff = step >> 3 ;
+		if (bytecode & 1)
+			diff += step >> 2 ;
+		if (bytecode & 2)
+			diff += step >> 1 ;
+		if (bytecode & 4)
+			diff += step ;
+		if (bytecode & 8)
+			diff = -diff ;
+
+		predictor += diff ;
+
+		if (predictor > 32767)
+			predictor = 32767 ;
+		else if (predictor < -32768)
+			predictor = -32768 ;
+
+		stepindx [chan] += ima_indx_adjust [bytecode] ;
+		stepindx [chan] = clamp_ima_step_index (stepindx [chan]) ;
+
+		pima->samples [k] = predictor ;
+		} ;
+
+	return 1 ;
+} /* wavlike_ima_decode_block */
+
+static int
+wavlike_ima_encode_block (SF_PRIVATE *psf, IMA_ADPCM_PRIVATE *pima)
+{	int		chan, k, step, diff, vpdiff, blockindx, indx, indxstart ;
+	short	bytecode, mask ;
+
+	/* Encode the block header. */
+	for (chan = 0 ; chan < pima->channels ; chan++)
+	{	pima->block [chan*4]	= pima->samples [chan] & 0xFF ;
+		pima->block [chan*4+1]	= (pima->samples [chan] >> 8) & 0xFF ;
+
+		pima->block [chan*4+2] = pima->stepindx [chan] ;
+		pima->block [chan*4+3] = 0 ;
+
+		pima->previous [chan] = pima->samples [chan] ;
+		} ;
+
+	/* Encode the samples as 4 bit. */
+
+	for (k = pima->channels ; k < (pima->samplesperblock * pima->channels) ; k ++)
+	{	chan = (pima->channels > 1) ? (k % 2) : 0 ;
+
+		diff = pima->samples [k] - pima->previous [chan] ;
+
+		bytecode = 0 ;
+		step = ima_step_size [pima->stepindx [chan]] ;
+		vpdiff = step >> 3 ;
+		if (diff < 0)
+		{	bytecode = 8 ;
+			diff = -diff ;
+			} ;
+		mask = 4 ;
+		while (mask)
+		{	if (diff >= step)
+			{	bytecode |= mask ;
+				diff -= step ;
+				vpdiff += step ;
+				} ;
+			step >>= 1 ;
+			mask >>= 1 ;
+			} ;
+
+		if (bytecode & 8)
+			pima->previous [chan] -= vpdiff ;
+		else
+			pima->previous [chan] += vpdiff ;
+
+		if (pima->previous [chan] > 32767)
+			pima->previous [chan] = 32767 ;
+		else if (pima->previous [chan] < -32768)
+			pima->previous [chan] = -32768 ;
+
+		pima->stepindx [chan] += ima_indx_adjust [bytecode] ;
+		pima->stepindx [chan] = clamp_ima_step_index (pima->stepindx [chan]) ;
+
+		pima->samples [k] = bytecode ;
+		} ;
+
+	/* Pack the 4 bit encoded samples. */
+
+	blockindx = 4 * pima->channels ;
+
+	indxstart = pima->channels ;
+	while (blockindx < pima->blocksize)
+	{	for (chan = 0 ; chan < pima->channels ; chan++)
+		{	indx = indxstart + chan ;
+			for (k = 0 ; k < 4 ; k++)
+			{	pima->block [blockindx] = pima->samples [indx] & 0x0F ;
+				indx += pima->channels ;
+				pima->block [blockindx] |= (pima->samples [indx] << 4) & 0xF0 ;
+				indx += pima->channels ;
+				blockindx ++ ;
+				} ;
+			} ;
+		indxstart += 8 * pima->channels ;
+		} ;
+
+	/* Write the block to disk. */
+
+	if ((k = (int) psf_fwrite (pima->block, 1, pima->blocksize, psf)) != pima->blocksize)
+		psf_log_printf (psf, "*** Warning : short write (%d != %d).\n", k, pima->blocksize) ;
+
+	memset (pima->samples, 0, pima->samplesperblock * sizeof (short)) ;
+	pima->samplecount = 0 ;
+	pima->blockcount ++ ;
+
+	return 1 ;
+} /* wavlike_ima_encode_block */
+
+static int
+ima_read_block (SF_PRIVATE *psf, IMA_ADPCM_PRIVATE *pima, short *ptr, int len)
+{	int		count, total = 0, indx = 0 ;
+
+	while (indx < len)
+	{	if (pima->blockcount >= pima->blocks && pima->samplecount >= pima->samplesperblock)
+		{	memset (&(ptr [indx]), 0, (size_t) ((len - indx) * sizeof (short))) ;
+			return total ;
+			} ;
+
+		if (pima->samplecount >= pima->samplesperblock)
+			pima->decode_block (psf, pima) ;
+
+		count = (pima->samplesperblock - pima->samplecount) * pima->channels ;
+		count = (len - indx > count) ? count : len - indx ;
+
+		memcpy (&(ptr [indx]), &(pima->samples [pima->samplecount * pima->channels]), count * sizeof (short)) ;
+		indx += count ;
+		pima->samplecount += count / pima->channels ;
+		total = indx ;
+		} ;
+
+	return total ;
+} /* ima_read_block */
+
+static sf_count_t
+ima_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len)
+{	IMA_ADPCM_PRIVATE 	*pima ;
+	int			readcount, count ;
+	sf_count_t	total = 0 ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	pima = (IMA_ADPCM_PRIVATE*) psf->codec_data ;
+
+	while (len > 0)
+	{	readcount = (len > 0x10000000) ? 0x10000000 : (int) len ;
+
+		count = ima_read_block (psf, pima, ptr, readcount) ;
+
+		total += count ;
+		len -= count ;
+		if (count != readcount)
+			break ;
+		} ;
+
+	return total ;
+} /* ima_read_s */
+
+static sf_count_t
+ima_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len)
+{	IMA_ADPCM_PRIVATE *pima ;
+	BUF_UNION	ubuf ;
+	short		*sptr ;
+	int			k, bufferlen, readcount, count ;
+	sf_count_t	total = 0 ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	pima = (IMA_ADPCM_PRIVATE*) psf->codec_data ;
+
+	sptr = ubuf.sbuf ;
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+	while (len > 0)
+	{	readcount = (len >= bufferlen) ? bufferlen : (int) len ;
+		count = ima_read_block (psf, pima, sptr, readcount) ;
+		for (k = 0 ; k < readcount ; k++)
+			ptr [total + k] = arith_shift_left (sptr [k], 16) ;
+		total += count ;
+		len -= readcount ;
+		if (count != readcount)
+			break ;
+		} ;
+
+	return total ;
+} /* ima_read_i */
+
+static sf_count_t
+ima_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len)
+{	IMA_ADPCM_PRIVATE *pima ;
+	BUF_UNION	ubuf ;
+	short		*sptr ;
+	int			k, bufferlen, readcount, count ;
+	sf_count_t	total = 0 ;
+	float		normfact ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	pima = (IMA_ADPCM_PRIVATE*) psf->codec_data ;
+
+	normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x8000) : 1.0 ;
+
+	sptr = ubuf.sbuf ;
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+	while (len > 0)
+	{	readcount = (len >= bufferlen) ? bufferlen : (int) len ;
+		count = ima_read_block (psf, pima, sptr, readcount) ;
+		for (k = 0 ; k < readcount ; k++)
+			ptr [total + k] = normfact * (float) (sptr [k]) ;
+		total += count ;
+		len -= readcount ;
+		if (count != readcount)
+			break ;
+		} ;
+
+	return total ;
+} /* ima_read_f */
+
+static sf_count_t
+ima_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len)
+{	IMA_ADPCM_PRIVATE *pima ;
+	BUF_UNION	ubuf ;
+	short		*sptr ;
+	int			k, bufferlen, readcount, count ;
+	sf_count_t	total = 0 ;
+	double 		normfact ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	pima = (IMA_ADPCM_PRIVATE*) psf->codec_data ;
+
+	normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((double) 0x8000) : 1.0 ;
+
+	sptr = ubuf.sbuf ;
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+	while (len > 0)
+	{	readcount = (len >= bufferlen) ? bufferlen : (int) len ;
+		count = ima_read_block (psf, pima, sptr, readcount) ;
+		for (k = 0 ; k < readcount ; k++)
+			ptr [total + k] = normfact * (double) (sptr [k]) ;
+		total += count ;
+		len -= readcount ;
+		if (count != readcount)
+			break ;
+		} ;
+
+	return total ;
+} /* ima_read_d */
+
+static sf_count_t
+aiff_ima_seek (SF_PRIVATE *psf, int mode, sf_count_t offset)
+{	IMA_ADPCM_PRIVATE *pima ;
+	int			newblock, newsample, newblockaiff ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	pima = (IMA_ADPCM_PRIVATE*) psf->codec_data ;
+
+	if (psf->datalength < 0 || psf->dataoffset < 0)
+	{	psf->error = SFE_BAD_SEEK ;
+		return PSF_SEEK_ERROR ;
+		} ;
+
+	if (offset == 0)
+	{	psf_fseek (psf, psf->dataoffset, SEEK_SET) ;
+		pima->blockcount = 0 ;
+		pima->decode_block (psf, pima) ;
+		pima->samplecount = 0 ;
+		return 0 ;
+		} ;
+
+	if (offset < 0 || offset > pima->blocks * pima->samplesperblock)
+	{	psf->error = SFE_BAD_SEEK ;
+		return	PSF_SEEK_ERROR ;
+		} ;
+
+	newblock		= offset / pima->samplesperblock ;
+	newsample		= offset % pima->samplesperblock ;
+	newblockaiff	= newblock * psf->sf.channels ;
+
+	if (mode == SFM_READ)
+	{	psf_fseek (psf, psf->dataoffset + newblockaiff * pima->blocksize, SEEK_SET) ;
+		pima->blockcount = newblockaiff ;
+		pima->decode_block (psf, pima) ;
+		pima->samplecount = newsample ;
+		}
+	else
+	{	/* What to do about write??? */
+		psf->error = SFE_BAD_SEEK ;
+		return	PSF_SEEK_ERROR ;
+		} ;
+
+	return newblock * pima->samplesperblock + newsample ;
+} /* aiff_ima_seek */
+
+static sf_count_t
+wavlike_ima_seek (SF_PRIVATE *psf, int mode, sf_count_t offset)
+{	IMA_ADPCM_PRIVATE *pima ;
+	int			newblock, newsample ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	pima = (IMA_ADPCM_PRIVATE*) psf->codec_data ;
+
+	if (psf->datalength < 0 || psf->dataoffset < 0)
+	{	psf->error = SFE_BAD_SEEK ;
+		return PSF_SEEK_ERROR ;
+		} ;
+
+	if (offset == 0)
+	{	psf_fseek (psf, psf->dataoffset, SEEK_SET) ;
+		pima->blockcount = 0 ;
+		if (!pima->decode_block)
+			return PSF_SEEK_ERROR ;
+
+		pima->decode_block (psf, pima) ;
+		pima->samplecount = 0 ;
+		return 0 ;
+		} ;
+
+	if (offset < 0 || offset > pima->blocks * pima->samplesperblock)
+	{	psf->error = SFE_BAD_SEEK ;
+		return	PSF_SEEK_ERROR ;
+		} ;
+
+	newblock	= offset / pima->samplesperblock ;
+	newsample	= offset % pima->samplesperblock ;
+
+	if (mode == SFM_READ)
+	{	psf_fseek (psf, psf->dataoffset + newblock * pima->blocksize, SEEK_SET) ;
+		pima->blockcount = newblock ;
+		pima->decode_block (psf, pima) ;
+		pima->samplecount = newsample ;
+		}
+	else
+	{	/* What to do about write??? */
+		psf->error = SFE_BAD_SEEK ;
+		return	PSF_SEEK_ERROR ;
+		} ;
+
+	return newblock * pima->samplesperblock + newsample ;
+} /* wavlike_ima_seek */
+
+/*==========================================================================================
+** IMA ADPCM Write Functions.
+*/
+
+static int
+ima_writer_init (SF_PRIVATE *psf, int blockalign)
+{	IMA_ADPCM_PRIVATE	*pima ;
+	int					samplesperblock ;
+	unsigned int 		pimasize ;
+
+	if (psf->file.mode != SFM_WRITE)
+		return SFE_BAD_MODE_RW ;
+
+	switch (SF_CONTAINER (psf->sf.format))
+	{	case SF_FORMAT_WAV :
+		case SF_FORMAT_W64 :
+				samplesperblock = 2 * (blockalign - 4 * psf->sf.channels) / psf->sf.channels + 1 ;
+				break ;
+
+		case SF_FORMAT_AIFF :
+				samplesperblock = 2 * ((blockalign - 2) * psf->sf.channels) / psf->sf.channels ;
+				break ;
+
+		default :
+				psf_log_printf (psf, "ima_reader_init: bad psf->sf.format\n") ;
+				return SFE_INTERNAL ;
+		} ;
+
+	pimasize = sizeof (IMA_ADPCM_PRIVATE) + blockalign + 3 * psf->sf.channels * samplesperblock ;
+
+	if ((pima = calloc (1, pimasize)) == NULL)
+		return SFE_MALLOC_FAILED ;
+
+	psf->codec_data = (void*) pima ;
+
+	pima->channels			= psf->sf.channels ;
+	pima->blocksize			= blockalign ;
+	pima->samplesperblock	= samplesperblock ;
+
+	pima->block		= (unsigned char*) pima->data ;
+	pima->samples	= (short*) (pima->data + blockalign) ;
+
+	pima->samplecount = 0 ;
+
+	switch (SF_CONTAINER (psf->sf.format))
+	{	case SF_FORMAT_WAV :
+		case SF_FORMAT_W64 :
+				pima->encode_block = wavlike_ima_encode_block ;
+				break ;
+
+		case SF_FORMAT_AIFF :
+				pima->encode_block = aiff_ima_encode_block ;
+				break ;
+
+		default :
+				psf_log_printf (psf, "ima_reader_init: bad psf->sf.format\n") ;
+				return SFE_INTERNAL ;
+		} ;
+
+	psf->write_short	= ima_write_s ;
+	psf->write_int		= ima_write_i ;
+	psf->write_float	= ima_write_f ;
+	psf->write_double	= ima_write_d ;
+
+	return 0 ;
+} /* ima_writer_init */
+
+/*==========================================================================================
+*/
+
+static int
+ima_write_block (SF_PRIVATE *psf, IMA_ADPCM_PRIVATE *pima, const short *ptr, int len)
+{	int		count, total = 0, indx = 0 ;
+
+	while (indx < len)
+	{	count = (pima->samplesperblock - pima->samplecount) * pima->channels ;
+
+		if (count > len - indx)
+			count = len - indx ;
+
+		memcpy (&(pima->samples [pima->samplecount * pima->channels]), &(ptr [total]), count * sizeof (short)) ;
+		indx += count ;
+		pima->samplecount += count / pima->channels ;
+		total = indx ;
+
+		if (pima->samplecount >= pima->samplesperblock)
+			pima->encode_block (psf, pima) ;
+		} ;
+
+	return total ;
+} /* ima_write_block */
+
+static sf_count_t
+ima_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len)
+{	IMA_ADPCM_PRIVATE 	*pima ;
+	int			writecount, count ;
+	sf_count_t	total = 0 ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	pima = (IMA_ADPCM_PRIVATE*) psf->codec_data ;
+
+	while (len)
+	{	writecount = (len > 0x10000000) ? 0x10000000 : (int) len ;
+
+		count = ima_write_block (psf, pima, ptr, writecount) ;
+
+		total += count ;
+		len -= count ;
+		if (count != writecount)
+			break ;
+		} ;
+
+	return total ;
+} /* ima_write_s */
+
+static sf_count_t
+ima_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len)
+{	IMA_ADPCM_PRIVATE *pima ;
+	BUF_UNION	ubuf ;
+	short		*sptr ;
+	int			k, bufferlen, writecount, count ;
+	sf_count_t	total = 0 ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	pima = (IMA_ADPCM_PRIVATE*) psf->codec_data ;
+
+	sptr = ubuf.sbuf ;
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+	while (len > 0)
+	{	writecount = (len >= bufferlen) ? bufferlen : (int) len ;
+		for (k = 0 ; k < writecount ; k++)
+			sptr [k] = ptr [total + k] >> 16 ;
+		count = ima_write_block (psf, pima, sptr, writecount) ;
+		total += count ;
+		len -= writecount ;
+		if (count != writecount)
+			break ;
+		} ;
+
+	return total ;
+} /* ima_write_i */
+
+static sf_count_t
+ima_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len)
+{	IMA_ADPCM_PRIVATE *pima ;
+	BUF_UNION	ubuf ;
+	short		*sptr ;
+	int			k, bufferlen, writecount, count ;
+	sf_count_t	total = 0 ;
+	float		normfact ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	pima = (IMA_ADPCM_PRIVATE*) psf->codec_data ;
+
+	normfact = (psf->norm_float == SF_TRUE) ? (1.0 * 0x7FFF) : 1.0 ;
+
+	sptr = ubuf.sbuf ;
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+	while (len > 0)
+	{	writecount = (len >= bufferlen) ? bufferlen : (int) len ;
+		for (k = 0 ; k < writecount ; k++)
+			sptr [k] = psf_lrintf (normfact * ptr [total + k]) ;
+		count = ima_write_block (psf, pima, sptr, writecount) ;
+		total += count ;
+		len -= writecount ;
+		if (count != writecount)
+			break ;
+		} ;
+
+	return total ;
+} /* ima_write_f */
+
+static sf_count_t
+ima_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len)
+{	IMA_ADPCM_PRIVATE *pima ;
+	BUF_UNION	ubuf ;
+	short		*sptr ;
+	int			k, bufferlen, writecount, count ;
+	sf_count_t	total = 0 ;
+	double 		normfact ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	pima = (IMA_ADPCM_PRIVATE*) psf->codec_data ;
+
+	normfact = (psf->norm_double == SF_TRUE) ? (1.0 * 0x7FFF) : 1.0 ;
+
+	sptr = ubuf.sbuf ;
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+	while (len > 0)
+	{	writecount = (len >= bufferlen) ? bufferlen : (int) len ;
+		for (k = 0 ; k < writecount ; k++)
+			sptr [k] = psf_lrint (normfact * ptr [total + k]) ;
+		count = ima_write_block (psf, pima, sptr, writecount) ;
+		total += count ;
+		len -= writecount ;
+		if (count != writecount)
+			break ;
+		} ;
+
+	return total ;
+} /* ima_write_d */
+
diff --git a/src/ima_oki_adpcm.c b/src/ima_oki_adpcm.c
new file mode 100644
index 0000000..2532160
--- /dev/null
+++ b/src/ima_oki_adpcm.c
@@ -0,0 +1,297 @@
+/*
+** Copyright (C) 2007-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (c) 2007 <robs@users.sourceforge.net>
+**
+** This library is free software; you can redistribute it and/or modify it
+** under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or (at
+** your option) any later version.
+**
+** This library 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 Lesser
+** General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this library.  If not, write to the Free Software Foundation,
+** Fifth Floor, 51 Franklin Street, Boston, MA 02111-1301, USA.
+*/
+
+/* ADPCM: IMA, OKI <==> 16-bit PCM. */
+
+#include "sfconfig.h"
+
+#include <string.h>
+
+/* Set up for libsndfile environment: */
+#include "common.h"
+
+#include "ima_oki_adpcm.h"
+
+#define MIN_SAMPLE	-0x8000
+#define MAX_SAMPLE	0x7fff
+
+static int const ima_steps [] =	/* ~16-bit precision */
+{	7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
+	50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230,
+	253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963,
+	1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327,
+	3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442,
+	11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
+	32767
+} ;
+
+static int const oki_steps [] =	/* ~12-bit precision */
+{	256, 272, 304, 336, 368, 400, 448, 496, 544, 592, 656, 720, 800, 880, 960,
+	1056, 1168, 1280, 1408, 1552, 1712, 1888, 2080, 2288, 2512, 2768, 3040, 3344,
+	3680, 4048, 4464, 4912, 5392, 5936, 6528, 7184, 7904, 8704, 9568, 10528,
+	11584, 12736, 14016, 15408, 16960, 18656, 20512, 22576, 24832
+} ;
+
+static int const step_changes [] = { -1, -1, -1, -1, 2, 4, 6, 8 } ;
+
+void
+ima_oki_adpcm_init (IMA_OKI_ADPCM * state, IMA_OKI_ADPCM_TYPE type)
+{
+	memset (state, 0, sizeof (*state)) ;
+
+	if (type == IMA_OKI_ADPCM_TYPE_IMA)
+	{	state->max_step_index = ARRAY_LEN (ima_steps) - 1 ;
+		state->steps = ima_steps ;
+		state->mask = (~0) ;
+		}
+	else
+	{	state->max_step_index = ARRAY_LEN (oki_steps) - 1 ;
+		state->steps = oki_steps ;
+		state->mask = arith_shift_left (~0, 4) ;
+		} ;
+
+} /* ima_oki_adpcm_init */
+
+
+int
+adpcm_decode (IMA_OKI_ADPCM * state, int code)
+{	int s ;
+
+	s = ((code & 7) << 1) | 1 ;
+	s = ((state->steps [state->step_index] * s) >> 3) & state->mask ;
+
+	if (code & 8)
+		s = -s ;
+	s += state->last_output ;
+
+	if (s < MIN_SAMPLE || s > MAX_SAMPLE)
+	{	int grace ;
+
+		grace = (state->steps [state->step_index] >> 3) & state->mask ;
+
+		if (s < MIN_SAMPLE - grace || s > MAX_SAMPLE + grace)
+			state->errors ++ ;
+
+		s = s < MIN_SAMPLE ? MIN_SAMPLE : MAX_SAMPLE ;
+		} ;
+
+	state->step_index += step_changes [code & 7] ;
+	state->step_index = SF_MIN (SF_MAX (state->step_index, 0), state->max_step_index) ;
+	state->last_output = s ;
+
+	return s ;
+} /* adpcm_decode */
+
+int
+adpcm_encode (IMA_OKI_ADPCM * state, int sample)
+{	int delta, sign = 0, code ;
+
+	delta = sample - state->last_output ;
+
+	if (delta < 0)
+	{	sign = 8 ;
+		delta = -delta ;
+		} ;
+
+	code = 4 * delta / state->steps [state->step_index] ;
+	code = sign | SF_MIN (code, 7) ;
+	adpcm_decode (state, code) ; /* Update encoder state */
+
+	return code ;
+} /* adpcm_encode */
+
+
+void
+ima_oki_adpcm_decode_block	(IMA_OKI_ADPCM * state)
+{	unsigned char code ;
+	int k ;
+
+	for (k = 0 ; k < state->code_count ; k++)
+	{	code = state->codes [k] ;
+		state->pcm [2 * k] = adpcm_decode (state, code >> 4) ;
+		state->pcm [2 * k + 1] = adpcm_decode (state, code) ;
+		} ;
+
+	state->pcm_count = 2 * k ;
+} /* ima_oki_adpcm_decode_block */
+
+
+void
+ima_oki_adpcm_encode_block (IMA_OKI_ADPCM * state)
+{	unsigned char code ;
+	int k ;
+
+	/*
+	**	The codec expects an even number of input samples.
+	**
+	**	Samples should always be passed in even length blocks. If the last block to
+	**	be encoded is odd length, extend that block by one zero valued sample.
+	*/
+	if (state->pcm_count % 2 == 1)
+		state->pcm [state->pcm_count ++] = 0 ;
+
+	for (k = 0 ; k < state->pcm_count / 2 ; k++)
+	{	code = adpcm_encode (state, state->pcm [2 * k]) << 4 ;
+		code |= adpcm_encode (state, state->pcm [2 * k + 1]) ;
+		state->codes [k] = code ;
+		} ;
+
+	state->code_count = k ;
+} /* ima_oki_adpcm_encode_block */
+
+
+#ifdef TEST
+
+static const unsigned char test_codes [] =
+{	0x08, 0x08, 0x04, 0x7f, 0x72, 0xf7, 0x9f, 0x7c, 0xd7, 0xbc, 0x7a, 0xa7, 0xb8,
+	0x4b, 0x0b, 0x38, 0xf6, 0x9d, 0x7a, 0xd7, 0xbc, 0x7a, 0xd7, 0xa8, 0x6c, 0x81,
+	0x98, 0xe4, 0x0e, 0x7a, 0xd7, 0x9e, 0x7b, 0xc7, 0xab, 0x7a, 0x85, 0xc0, 0xb3,
+	0x8f, 0x58, 0xd7, 0xad, 0x7a, 0xd7, 0xad, 0x7a, 0x87, 0xd0, 0x2b, 0x0e, 0x48,
+	0xd7, 0xad, 0x78, 0xf7, 0xbc, 0x7a, 0xb7, 0xa8, 0x4b, 0x88, 0x18, 0xd5, 0x8d,
+	0x6a, 0xa4, 0x98, 0x08, 0x00, 0x80, 0x88,
+} ;
+
+static const short test_pcm [] =
+{	32, 0, 32, 0, 32, 320, 880, -336, 2304, 4192, -992, 10128, 5360, -16352,
+	30208, 2272, -31872, 14688, -7040, -32432, 14128, -1392, -15488, 22960,
+	1232, -1584, 21488, -240, 2576, -15360, 960, -1152, -30032, 10320, 1008,
+	-30032, 16528, 1008, -30032, 16528, -5200, -30592, 15968, 448, -30592,
+	15968, 448, -2368, 30960, 3024, -80, 8384, 704, -1616, -29168, -1232, 1872,
+	-32768, 13792, -1728, -32768, 13792, 4480, -32192, 14368, -7360, -32752,
+	13808, -1712, -21456, 16992, 1472, -1344, 26848, -1088, 2016, -17728, 208,
+	-2112, -32768, 1376, -1728, -32768, 13792, -1728, -32768, 13792, -1728,
+	-32768, 13792, -1728, -32768, 13792, -1728, -4544, 32767, -1377, 1727,
+	15823, -2113, 207, -27345, 591, -2513, -32768, 13792, -1728, -32768, 13792,
+	10688, -31632, 14928, -6800, -32192, 14368, -1152, -20896, 17552, 2032,
+	-784, 22288, 560, -2256, -4816, 2176, 64, -21120, 9920, 6816, -24224, 16128,
+	608, -13488, 9584, 272, -2544, 16, -2304, -192, 1728, -16, 1568, 128, -1184,
+} ;
+
+
+static void
+test_oki_adpcm (void)
+{
+	IMA_OKI_ADPCM adpcm ;
+	unsigned char code ;
+	int i, j ;
+
+	printf ("    Testing encoder          : ") ;
+	fflush (stdout) ;
+
+	ima_oki_adpcm_init (&adpcm, IMA_OKI_ADPCM_TYPE_OKI) ;
+	for (i = 0 ; i < ARRAY_LEN (test_codes) ; i++)
+		for (j = 0, code = test_codes [i] ; j < 2 ; j++, code <<= 4)
+			if (adpcm_decode (&adpcm, code >> 4) != test_pcm [2 * i + j])
+			{	printf ("\n\nFail at i = %d, j = %d.\n\n", i, j) ;
+				exit (1) ;
+				} ;
+
+	puts ("ok") ;
+
+	printf ("    Testing decoder          : ") ;
+	fflush (stdout) ;
+
+	ima_oki_adpcm_init (&adpcm, IMA_OKI_ADPCM_TYPE_OKI) ;
+	for (i = 0 ; i < ARRAY_LEN (test_pcm) ; i += j)
+	{	code = adpcm_encode (&adpcm, test_pcm [i]) ;
+		code = (code << 4) | adpcm_encode (&adpcm, test_pcm [i + 1]) ;
+		if (code != test_codes [i / 2])
+			{	printf ("\n\nFail at i = %d, %d should be %d\n\n", i, code, test_codes [i / 2]) ;
+				exit (1) ;
+				} ;
+		} ;
+
+	puts ("ok") ;
+} /* test_oki_adpcm */
+
+static void
+test_oki_adpcm_block (void)
+{
+	IMA_OKI_ADPCM adpcm ;
+	int k ;
+
+	if (ARRAY_LEN (adpcm.pcm) < ARRAY_LEN (test_pcm))
+	{	printf ("\n\nLine %d : ARRAY_LEN (adpcm->pcm) > ARRAY_LEN (test_pcm) (%d > %d).\n\n", __LINE__, ARRAY_LEN (adpcm.pcm), ARRAY_LEN (test_pcm)) ;
+		exit (1) ;
+		} ;
+
+	if (ARRAY_LEN (adpcm.codes) < ARRAY_LEN (test_codes))
+	{	printf ("\n\nLine %d : ARRAY_LEN (adcodes->codes) > ARRAY_LEN (test_codes).n", __LINE__) ;
+		exit (1) ;
+		} ;
+
+	printf ("    Testing block encoder    : ") ;
+	fflush (stdout) ;
+
+	ima_oki_adpcm_init (&adpcm, IMA_OKI_ADPCM_TYPE_OKI) ;
+
+	memcpy (adpcm.pcm, test_pcm, sizeof (adpcm.pcm [0]) * ARRAY_LEN (test_pcm)) ;
+	adpcm.pcm_count = ARRAY_LEN (test_pcm) ;
+	adpcm.code_count = 13 ;
+
+	ima_oki_adpcm_encode_block (&adpcm) ;
+
+	if (adpcm.code_count * 2 != ARRAY_LEN (test_pcm))
+	{	printf ("\n\nLine %d : %d * 2 != %d\n\n", __LINE__, adpcm.code_count * 2, ARRAY_LEN (test_pcm)) ;
+		exit (1) ;
+		} ;
+
+	for (k = 0 ; k < ARRAY_LEN (test_codes) ; k++)
+		if (adpcm.codes [k] != test_codes [k])
+		{	printf ("\n\nLine %d : Fail at k = %d, %d should be %d\n\n", __LINE__, k, adpcm.codes [k], test_codes [k]) ;
+			exit (1) ;
+			} ;
+
+	puts ("ok") ;
+
+	printf ("    Testing block decoder    : ") ;
+	fflush (stdout) ;
+
+	ima_oki_adpcm_init (&adpcm, IMA_OKI_ADPCM_TYPE_OKI) ;
+
+	memcpy (adpcm.codes, test_codes, sizeof (adpcm.codes [0]) * ARRAY_LEN (test_codes)) ;
+	adpcm.code_count = ARRAY_LEN (test_codes) ;
+	adpcm.pcm_count = 13 ;
+
+	ima_oki_adpcm_decode_block (&adpcm) ;
+
+	if (adpcm.pcm_count != 2 * ARRAY_LEN (test_codes))
+	{	printf ("\n\nLine %d : %d * 2 != %d\n\n", __LINE__, adpcm.pcm_count, 2 * ARRAY_LEN (test_codes)) ;
+		exit (1) ;
+		} ;
+
+	for (k = 0 ; k < ARRAY_LEN (test_pcm) ; k++)
+		if (adpcm.pcm [k] != test_pcm [k])
+		{	printf ("\n\nLine %d : Fail at i = %d, %d should be %d.\n\n", __LINE__, k, adpcm.pcm [k], test_pcm [k]) ;
+			exit (1) ;
+			} ;
+
+	puts ("ok") ;
+} /* test_oki_adpcm_block */
+
+int
+main (void)
+{
+	test_oki_adpcm () ;
+	test_oki_adpcm_block () ;
+
+	return 0 ;
+} /* main */
+
+#endif
diff --git a/src/ima_oki_adpcm.h b/src/ima_oki_adpcm.h
new file mode 100644
index 0000000..86241e7
--- /dev/null
+++ b/src/ima_oki_adpcm.h
@@ -0,0 +1,54 @@
+/*
+** Copyright (C) 2007-2011 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (c) 2007 <robs@users.sourceforge.net>
+**
+** This library is free software; you can redistribute it and/or modify it
+** under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or (at
+** your option) any later version.
+**
+** This library 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 Lesser
+** General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this library.  If not, write to the Free Software Foundation,
+** Fifth Floor, 51 Franklin Street, Boston, MA 02111-1301, USA.
+*/
+
+/* ADPCM: IMA, OKI <==> 16-bit PCM. */
+
+
+#define		IMA_OKI_ADPCM_CODE_LEN	256
+#define		IMA_OKI_ADPCM_PCM_LEN	(IMA_OKI_ADPCM_CODE_LEN *2)
+
+typedef struct
+{
+	/* private: */
+	int mask ;
+	int last_output ;
+	int step_index ;
+	int max_step_index ;
+	int const * steps ;
+
+	/* public: */
+	int errors ;
+	int	code_count, pcm_count ;
+
+	unsigned char	codes [IMA_OKI_ADPCM_CODE_LEN] ;
+	short 			pcm [IMA_OKI_ADPCM_PCM_LEN] ;
+} IMA_OKI_ADPCM ;
+
+typedef enum
+{	IMA_OKI_ADPCM_TYPE_IMA,
+	IMA_OKI_ADPCM_TYPE_OKI
+} IMA_OKI_ADPCM_TYPE ;
+
+void ima_oki_adpcm_init		(IMA_OKI_ADPCM * state, IMA_OKI_ADPCM_TYPE type) ;
+
+int	adpcm_decode	(IMA_OKI_ADPCM * state, int /* 0..15 */ code) ;
+int	adpcm_encode	(IMA_OKI_ADPCM * state, int /* -32768..32767 */ sample) ;
+
+void	ima_oki_adpcm_decode_block	(IMA_OKI_ADPCM * state) ;
+void	ima_oki_adpcm_encode_block	(IMA_OKI_ADPCM * state) ;
diff --git a/src/interleave.c b/src/interleave.c
new file mode 100644
index 0000000..5f79944
--- /dev/null
+++ b/src/interleave.c
@@ -0,0 +1,299 @@
+/*
+** Copyright (C) 2002-2013 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfendian.h"
+
+#include	<stdlib.h>
+
+#include	"sndfile.h"
+#include	"common.h"
+
+#define		INTERLEAVE_CHANNELS		6
+
+typedef struct
+{	double	buffer [SF_BUFFER_LEN / sizeof (double)] ;
+
+	sf_count_t		channel_len ;
+
+	sf_count_t		(*read_short)	(SF_PRIVATE*, short *ptr, sf_count_t len) ;
+	sf_count_t		(*read_int)	(SF_PRIVATE*, int *ptr, sf_count_t len) ;
+	sf_count_t		(*read_float)	(SF_PRIVATE*, float *ptr, sf_count_t len) ;
+	sf_count_t		(*read_double)	(SF_PRIVATE*, double *ptr, sf_count_t len) ;
+
+} INTERLEAVE_DATA ;
+
+
+
+static sf_count_t	interleave_read_short	(SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
+static sf_count_t	interleave_read_int	(SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
+static sf_count_t	interleave_read_float	(SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
+static sf_count_t	interleave_read_double	(SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
+
+static sf_count_t	interleave_seek	(SF_PRIVATE*, int mode, sf_count_t samples_from_start) ;
+
+
+
+
+int
+interleave_init	(SF_PRIVATE *psf)
+{	INTERLEAVE_DATA *pdata ;
+
+	if (psf->file.mode != SFM_READ)
+		return SFE_INTERLEAVE_MODE ;
+
+	if (psf->interleave)
+	{	psf_log_printf (psf, "*** Weird, already have interleave.\n") ;
+		return 666 ;
+		} ;
+
+	/* Free this in sf_close() function. */
+	if (! (pdata = malloc (sizeof (INTERLEAVE_DATA))))
+		return SFE_MALLOC_FAILED ;
+
+puts ("interleave_init") ;
+
+	psf->interleave = pdata ;
+
+	/* Save the existing methods. */
+	pdata->read_short	= psf->read_short ;
+	pdata->read_int		= psf->read_int ;
+	pdata->read_float	= psf->read_float ;
+	pdata->read_double	= psf->read_double ;
+
+	pdata->channel_len = psf->sf.frames * psf->bytewidth ;
+
+	/* Insert our new methods. */
+	psf->read_short		= interleave_read_short ;
+	psf->read_int		= interleave_read_int ;
+	psf->read_float		= interleave_read_float ;
+	psf->read_double	= interleave_read_double ;
+
+	psf->seek = interleave_seek ;
+
+	return 0 ;
+} /* pcm_interleave_init */
+
+/*------------------------------------------------------------------------------
+*/
+
+static sf_count_t
+interleave_read_short	(SF_PRIVATE *psf, short *ptr, sf_count_t len)
+{	INTERLEAVE_DATA *pdata ;
+	sf_count_t	offset, templen ;
+	int			chan, count, k ;
+	short		*inptr, *outptr ;
+
+	if (! (pdata = psf->interleave))
+		return 0 ;
+
+	inptr = (short*) pdata->buffer ;
+
+	for (chan = 0 ; chan < psf->sf.channels ; chan++)
+	{	outptr = ptr + chan ;
+
+		offset = psf->dataoffset + chan * psf->bytewidth * psf->read_current ;
+
+		if (psf_fseek (psf, offset, SEEK_SET) != offset)
+		{	psf->error = SFE_INTERLEAVE_SEEK ;
+			return 0 ;
+			} ;
+
+		templen = len / psf->sf.channels ;
+
+		while (templen > 0)
+		{	if (templen > SIGNED_SIZEOF (pdata->buffer) / SIGNED_SIZEOF (short))
+				count = SIGNED_SIZEOF (pdata->buffer) / SIGNED_SIZEOF (short) ;
+			else
+				count = (int) templen ;
+
+			if (pdata->read_short (psf, inptr, count) != count)
+			{	psf->error = SFE_INTERLEAVE_READ ;
+				return 0 ;
+				} ;
+
+			for (k = 0 ; k < count ; k++)
+			{	*outptr = inptr [k] ;
+				outptr += psf->sf.channels ;
+				} ;
+
+			templen -= count ;
+			} ;
+		} ;
+
+	return len ;
+} /* interleave_read_short */
+
+static sf_count_t
+interleave_read_int	(SF_PRIVATE *psf, int *ptr, sf_count_t len)
+{	INTERLEAVE_DATA *pdata ;
+	sf_count_t	offset, templen ;
+	int			chan, count, k ;
+	int		*inptr, *outptr ;
+
+	if (! (pdata = psf->interleave))
+		return 0 ;
+
+	inptr = (int*) pdata->buffer ;
+
+	for (chan = 0 ; chan < psf->sf.channels ; chan++)
+	{	outptr = ptr + chan ;
+
+		offset = psf->dataoffset + chan * psf->bytewidth * psf->read_current ;
+
+		if (psf_fseek (psf, offset, SEEK_SET) != offset)
+		{	psf->error = SFE_INTERLEAVE_SEEK ;
+			return 0 ;
+			} ;
+
+		templen = len / psf->sf.channels ;
+
+		while (templen > 0)
+		{	if (templen > SIGNED_SIZEOF (pdata->buffer) / SIGNED_SIZEOF (int))
+				count = SIGNED_SIZEOF (pdata->buffer) / SIGNED_SIZEOF (int) ;
+			else
+				count = (int) templen ;
+
+			if (pdata->read_int (psf, inptr, count) != count)
+			{	psf->error = SFE_INTERLEAVE_READ ;
+				return 0 ;
+				} ;
+
+			for (k = 0 ; k < count ; k++)
+			{	*outptr = inptr [k] ;
+				outptr += psf->sf.channels ;
+				} ;
+
+			templen -= count ;
+			} ;
+		} ;
+
+	return len ;
+} /* interleave_read_int */
+
+static sf_count_t
+interleave_read_float	(SF_PRIVATE *psf, float *ptr, sf_count_t len)
+{	INTERLEAVE_DATA *pdata ;
+	sf_count_t	offset, templen ;
+	int			chan, count, k ;
+	float		*inptr, *outptr ;
+
+	if (! (pdata = psf->interleave))
+		return 0 ;
+
+	inptr = (float*) pdata->buffer ;
+
+	for (chan = 0 ; chan < psf->sf.channels ; chan++)
+	{	outptr = ptr + chan ;
+
+		offset = psf->dataoffset + pdata->channel_len * chan + psf->read_current * psf->bytewidth ;
+
+/*-printf ("chan : %d     read_current : %6lld    offset : %6lld\n", chan, psf->read_current, offset) ;-*/
+
+		if (psf_fseek (psf, offset, SEEK_SET) != offset)
+		{	psf->error = SFE_INTERLEAVE_SEEK ;
+/*-puts ("interleave_seek error") ; exit (1) ;-*/
+			return 0 ;
+			} ;
+
+		templen = len / psf->sf.channels ;
+
+		while (templen > 0)
+		{	if (templen > SIGNED_SIZEOF (pdata->buffer) / SIGNED_SIZEOF (float))
+				count = SIGNED_SIZEOF (pdata->buffer) / SIGNED_SIZEOF (float) ;
+			else
+				count = (int) templen ;
+
+			if (pdata->read_float (psf, inptr, count) != count)
+			{	psf->error = SFE_INTERLEAVE_READ ;
+/*-puts ("interleave_read error") ; exit (1) ;-*/
+				return 0 ;
+				} ;
+
+			for (k = 0 ; k < count ; k++)
+			{	*outptr = inptr [k] ;
+				outptr += psf->sf.channels ;
+				} ;
+
+			templen -= count ;
+			} ;
+		} ;
+
+	return len ;
+} /* interleave_read_float */
+
+static sf_count_t
+interleave_read_double	(SF_PRIVATE *psf, double *ptr, sf_count_t len)
+{	INTERLEAVE_DATA *pdata ;
+	sf_count_t	offset, templen ;
+	int			chan, count, k ;
+	double		*inptr, *outptr ;
+
+	if (! (pdata = psf->interleave))
+		return 0 ;
+
+	inptr = (double*) pdata->buffer ;
+
+	for (chan = 0 ; chan < psf->sf.channels ; chan++)
+	{	outptr = ptr + chan ;
+
+		offset = psf->dataoffset + chan * psf->bytewidth * psf->read_current ;
+
+		if (psf_fseek (psf, offset, SEEK_SET) != offset)
+		{	psf->error = SFE_INTERLEAVE_SEEK ;
+			return 0 ;
+			} ;
+
+		templen = len / psf->sf.channels ;
+
+		while (templen > 0)
+		{	if (templen > SIGNED_SIZEOF (pdata->buffer) / SIGNED_SIZEOF (double))
+				count = SIGNED_SIZEOF (pdata->buffer) / SIGNED_SIZEOF (double) ;
+			else
+				count = (int) templen ;
+
+			if (pdata->read_double (psf, inptr, count) != count)
+			{	psf->error = SFE_INTERLEAVE_READ ;
+				return 0 ;
+				} ;
+
+			for (k = 0 ; k < count ; k++)
+			{	*outptr = inptr [k] ;
+				outptr += psf->sf.channels ;
+				} ;
+
+			templen -= count ;
+			} ;
+		} ;
+
+	return len ;
+} /* interleave_read_double */
+
+/*------------------------------------------------------------------------------
+*/
+
+static sf_count_t
+interleave_seek	(SF_PRIVATE * UNUSED (psf), int UNUSED (mode), sf_count_t samples_from_start)
+{
+	/*
+	** Do nothing here. This is a place holder to prevent the default
+	** seek function from being called.
+	*/
+
+	return samples_from_start ;
+} /* interleave_seek */
+
diff --git a/src/ircam.c b/src/ircam.c
new file mode 100644
index 0000000..8e7cdba
--- /dev/null
+++ b/src/ircam.c
@@ -0,0 +1,323 @@
+/*
+** Copyright (C) 2001-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<fcntl.h>
+#include	<string.h>
+#include	<ctype.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+/*------------------------------------------------------------------------------
+** Macros to handle big/little endian issues.
+*/
+
+/* The IRCAM magic number is weird in that one byte in the number can have
+** values of 0x1, 0x2, 0x03 or 0x04. Hence the need for a marker and a mask.
+*/
+
+#define IRCAM_BE_MASK		(MAKE_MARKER (0xFF, 0xFF, 0x00, 0xFF))
+#define IRCAM_BE_MARKER		(MAKE_MARKER (0x64, 0xA3, 0x00, 0x00))
+
+#define IRCAM_LE_MASK		(MAKE_MARKER (0xFF, 0x00, 0xFF, 0xFF))
+#define IRCAM_LE_MARKER		(MAKE_MARKER (0x00, 0x00, 0xA3, 0x64))
+
+#define IRCAM_02B_MARKER	(MAKE_MARKER (0x64, 0xA3, 0x02, 0x00))
+#define IRCAM_03L_MARKER	(MAKE_MARKER (0x64, 0xA3, 0x03, 0x00))
+
+#define IRCAM_DATA_OFFSET	(1024)
+
+/*------------------------------------------------------------------------------
+** Typedefs.
+*/
+
+enum
+{	IRCAM_PCM_16	= 0x00002,
+	IRCAM_FLOAT		= 0x00004,
+	IRCAM_ALAW		= 0x10001,
+	IRCAM_ULAW		= 0x20001,
+	IRCAM_PCM_32	= 0x40004
+} ;
+
+
+/*------------------------------------------------------------------------------
+** Private static functions.
+*/
+
+static	int		ircam_close			(SF_PRIVATE *psf) ;
+static	int		ircam_write_header	(SF_PRIVATE *psf, int calc_length) ;
+static	int		ircam_read_header	(SF_PRIVATE *psf) ;
+
+static	int		get_encoding (int subformat) ;
+
+static	const char*	get_encoding_str (int encoding) ;
+
+/*------------------------------------------------------------------------------
+** Public function.
+*/
+
+int
+ircam_open	(SF_PRIVATE *psf)
+{	int		subformat ;
+	int		error = SFE_NO_ERROR ;
+
+	if (psf->file.mode == SFM_READ || (psf->file.mode == SFM_RDWR && psf->filelength > 0))
+	{	if ((error = ircam_read_header (psf)))
+			return error ;
+		} ;
+
+	subformat = SF_CODEC (psf->sf.format) ;
+
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	if ((SF_CONTAINER (psf->sf.format)) != SF_FORMAT_IRCAM)
+			return	SFE_BAD_OPEN_FORMAT ;
+
+		psf->endian = SF_ENDIAN (psf->sf.format) ;
+		if (psf->endian == 0 || psf->endian == SF_ENDIAN_CPU)
+			psf->endian = (CPU_IS_BIG_ENDIAN) ? SF_ENDIAN_BIG : SF_ENDIAN_LITTLE ;
+
+		psf->dataoffset = IRCAM_DATA_OFFSET ;
+
+		if ((error = ircam_write_header (psf, SF_FALSE)))
+			return error ;
+
+		psf->write_header = ircam_write_header ;
+		} ;
+
+	psf->container_close = ircam_close ;
+
+	switch (subformat)
+	{	case SF_FORMAT_ULAW :		/* 8-bit Ulaw encoding. */
+				error = ulaw_init (psf) ;
+				break ;
+
+		case SF_FORMAT_ALAW :		/* 8-bit Alaw encoding. */
+				error = alaw_init (psf) ;
+				break ;
+
+		case SF_FORMAT_PCM_16 :	/* 16-bit linear PCM. */
+		case SF_FORMAT_PCM_32 :	/* 32-bit linear PCM. */
+				error = pcm_init (psf) ;
+				break ;
+
+		case SF_FORMAT_FLOAT :	/* 32-bit linear PCM. */
+				error = float32_init (psf) ;
+				break ;
+
+		default : break ;
+		} ;
+
+	return error ;
+} /* ircam_open */
+
+/*------------------------------------------------------------------------------
+*/
+
+static int
+ircam_read_header	(SF_PRIVATE *psf)
+{	unsigned int	marker, encoding ;
+	float			samplerate ;
+	int				error = SFE_NO_ERROR ;
+
+	psf_binheader_readf (psf, "epmf44", 0, &marker, &samplerate, &(psf->sf.channels), &encoding) ;
+
+	if (((marker & IRCAM_BE_MASK) != IRCAM_BE_MARKER) && ((marker & IRCAM_LE_MASK) != IRCAM_LE_MARKER))
+	{	psf_log_printf (psf, "marker: 0x%X\n", marker) ;
+		return SFE_IRCAM_NO_MARKER ;
+		} ;
+
+	psf->endian = SF_ENDIAN_LITTLE ;
+
+	if (psf->sf.channels > SF_MAX_CHANNELS)
+	{	psf_binheader_readf (psf, "Epmf44", 0, &marker, &samplerate, &(psf->sf.channels), &encoding) ;
+
+		/* Sanity checking for endian-ness detection. */
+		if (psf->sf.channels > SF_MAX_CHANNELS)
+		{	psf_log_printf (psf, "marker: 0x%X\n", marker) ;
+			return SFE_IRCAM_BAD_CHANNELS ;
+			} ;
+
+		psf->endian = SF_ENDIAN_BIG ;
+		} ;
+
+	psf_log_printf (psf, "marker: 0x%X\n", marker) ;
+
+	psf->sf.samplerate = (int) samplerate ;
+
+	psf_log_printf (psf,	"  Sample Rate : %d\n"
+							"  Channels    : %d\n"
+							"  Encoding    : %X => %s\n",
+						psf->sf.samplerate, psf->sf.channels, encoding, get_encoding_str (encoding)) ;
+
+	switch (encoding)
+	{	case IRCAM_PCM_16 :
+				psf->bytewidth = 2 ;
+				psf->blockwidth = psf->sf.channels * psf->bytewidth ;
+
+				psf->sf.format = SF_FORMAT_IRCAM | SF_FORMAT_PCM_16 ;
+				break ;
+
+		case IRCAM_PCM_32 :
+				psf->bytewidth = 4 ;
+				psf->blockwidth = psf->sf.channels * psf->bytewidth ;
+
+				psf->sf.format = SF_FORMAT_IRCAM | SF_FORMAT_PCM_32 ;
+				break ;
+
+		case IRCAM_FLOAT :
+				psf->bytewidth = 4 ;
+				psf->blockwidth = psf->sf.channels * psf->bytewidth ;
+
+				psf->sf.format = SF_FORMAT_IRCAM | SF_FORMAT_FLOAT ;
+				break ;
+
+		case IRCAM_ALAW :
+				psf->bytewidth = 1 ;
+				psf->blockwidth = psf->sf.channels * psf->bytewidth ;
+
+				psf->sf.format = SF_FORMAT_IRCAM | SF_FORMAT_ALAW ;
+				break ;
+
+		case IRCAM_ULAW :
+				psf->bytewidth = 1 ;
+				psf->blockwidth = psf->sf.channels * psf->bytewidth ;
+
+				psf->sf.format = SF_FORMAT_IRCAM | SF_FORMAT_ULAW ;
+				break ;
+
+		default :
+				error = SFE_IRCAM_UNKNOWN_FORMAT ;
+				break ;
+		} ;
+
+	if (psf->endian == SF_ENDIAN_BIG)
+		psf->sf.format |= SF_ENDIAN_BIG ;
+	else
+		psf->sf.format |= SF_ENDIAN_LITTLE ;
+
+	if (error)
+		return error ;
+
+	psf->dataoffset = IRCAM_DATA_OFFSET ;
+	psf->datalength = psf->filelength - psf->dataoffset ;
+
+	if (psf->sf.frames == 0 && psf->blockwidth)
+		psf->sf.frames = psf->datalength / psf->blockwidth ;
+
+	psf_log_printf (psf, "  Samples     : %d\n", psf->sf.frames) ;
+
+	psf_binheader_readf (psf, "p", IRCAM_DATA_OFFSET) ;
+
+	return 0 ;
+} /* ircam_read_header */
+
+static int
+ircam_close	(SF_PRIVATE *psf)
+{
+	psf_log_printf (psf, "close\n") ;
+
+	return 0 ;
+} /* ircam_close */
+
+static int
+ircam_write_header (SF_PRIVATE *psf, int UNUSED (calc_length))
+{	int			encoding ;
+	float		samplerate ;
+	sf_count_t	current ;
+
+	if (psf->pipeoffset > 0)
+		return 0 ;
+
+	current = psf_ftell (psf) ;
+
+	/* This also sets psf->endian. */
+	encoding = get_encoding (SF_CODEC (psf->sf.format)) ;
+
+	if (encoding == 0)
+		return SFE_BAD_OPEN_FORMAT ;
+
+	/* Reset the current header length to zero. */
+	psf->header.ptr [0] = 0 ;
+	psf->header.indx = 0 ;
+
+	if (psf->is_pipe == SF_FALSE)
+		psf_fseek (psf, 0, SEEK_SET) ;
+
+	samplerate = psf->sf.samplerate ;
+
+	switch (psf->endian)
+	{	case SF_ENDIAN_BIG :
+			psf_binheader_writef (psf, "Emf", BHWm (IRCAM_02B_MARKER), BHWf (samplerate)) ;
+			psf_binheader_writef (psf, "E44", BHW4 (psf->sf.channels), BHW4 (encoding)) ;
+			break ;
+
+		case SF_ENDIAN_LITTLE :
+			psf_binheader_writef (psf, "emf", BHWm (IRCAM_03L_MARKER), BHWf (samplerate)) ;
+			psf_binheader_writef (psf, "e44", BHW4 (psf->sf.channels), BHW4 (encoding)) ;
+			break ;
+
+		default : return SFE_BAD_OPEN_FORMAT ;
+		} ;
+
+	psf_binheader_writef (psf, "z", BHWz ((size_t) (IRCAM_DATA_OFFSET - psf->header.indx))) ;
+
+	/* Header construction complete so write it out. */
+	psf_fwrite (psf->header.ptr, psf->header.indx, 1, psf) ;
+
+	if (psf->error)
+		return psf->error ;
+
+	if (current > 0)
+		psf_fseek (psf, current, SEEK_SET) ;
+
+	return psf->error ;
+} /* ircam_write_header */
+
+static int
+get_encoding (int subformat)
+{	switch (subformat)
+	{	case SF_FORMAT_PCM_16 :	return IRCAM_PCM_16 ;
+		case SF_FORMAT_PCM_32 :	return IRCAM_PCM_32 ;
+
+		case SF_FORMAT_FLOAT :	return IRCAM_FLOAT ;
+
+		case SF_FORMAT_ULAW :	return IRCAM_ULAW ;
+		case SF_FORMAT_ALAW :	return IRCAM_ALAW ;
+
+		default : break ;
+		} ;
+
+	return 0 ;
+} /* get_encoding */
+
+static const char*
+get_encoding_str (int encoding)
+{	switch (encoding)
+	{	case IRCAM_PCM_16	: return "16 bit PCM" ;
+		case IRCAM_FLOAT	: return "32 bit float" ;
+		case IRCAM_ALAW		: return "A law" ;
+		case IRCAM_ULAW		: return "u law" ;
+		case IRCAM_PCM_32	: return "32 bit PCM" ;
+		} ;
+	return "Unknown encoding" ;
+} /* get_encoding_str */
+
diff --git a/src/macos.c b/src/macos.c
new file mode 100644
index 0000000..5f6c531
--- /dev/null
+++ b/src/macos.c
@@ -0,0 +1,51 @@
+/*
+** Copyright (C) 2003-2011 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdlib.h>
+#include	<string.h>
+#include	<sys/stat.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+#define	STR_MARKER	MAKE_MARKER ('S', 'T', 'R', ' ')
+
+int
+macos_guess_file_type (SF_PRIVATE * psf, const char *filename)
+{	static char rsrc_name [1024] ;
+	struct stat statbuf ;
+
+	snprintf (rsrc_name, sizeof (rsrc_name), "%s/rsrc", filename) ;
+
+	/* If there is no resource fork, just return. */
+	if (stat (rsrc_name, &statbuf) != 0)
+	{	psf_log_printf (psf, "No resource fork.\n") ;
+		return 0 ;
+		} ;
+
+	if (statbuf.st_size == 0)
+	{	psf_log_printf (psf, "Have zero size resource fork.\n") ;
+		return 0 ;
+		} ;
+
+	return 0 ;
+} /* macos_guess_file_type */
+
diff --git a/src/make-static-lib-hidden-privates.sh b/src/make-static-lib-hidden-privates.sh
new file mode 100755
index 0000000..5bfd485
--- /dev/null
+++ b/src/make-static-lib-hidden-privates.sh
@@ -0,0 +1,14 @@
+#!/bin/bash -e
+
+# This script takes a static library and removes all non-public symbols.
+# Ie, it makes a static lib whose symbols are far less likely to clash with
+# the symbols of another shared or static library.
+
+grep sf_ Symbols.gnu-binutils | sed -e "s/[ ;]//g" > Symbols.static
+
+ld -r --whole-archive .libs/libsndfile.a -o libsndfile_a.o
+
+objcopy --keep-global-symbols=Symbols.static libsndfile_a.o libsndfile.o
+
+rm -f libsndfile.a
+ar cru libsndfile.a libsndfile.o
diff --git a/src/mat4.c b/src/mat4.c
new file mode 100644
index 0000000..0b1b414
--- /dev/null
+++ b/src/mat4.c
@@ -0,0 +1,391 @@
+/*
+** Copyright (C) 2002-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<fcntl.h>
+#include	<string.h>
+#include	<ctype.h>
+#include	<math.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+/*------------------------------------------------------------------------------
+** Information on how to decode and encode this file was obtained in a PDF
+** file which I found on http://www.wotsit.org/.
+** Also did a lot of testing with GNU Octave but do not have access to
+** Matlab (tm) and so could not test it there.
+*/
+
+/*------------------------------------------------------------------------------
+** Macros to handle big/little endian issues.
+*/
+
+#define MAT4_BE_DOUBLE	(MAKE_MARKER (0, 0, 0x03, 0xE8))
+#define MAT4_LE_DOUBLE	(MAKE_MARKER (0, 0, 0, 0))
+
+#define MAT4_BE_FLOAT	(MAKE_MARKER (0, 0, 0x03, 0xF2))
+#define MAT4_LE_FLOAT	(MAKE_MARKER (0x0A, 0, 0, 0))
+
+#define MAT4_BE_PCM_32	(MAKE_MARKER (0, 0, 0x03, 0xFC))
+#define MAT4_LE_PCM_32	(MAKE_MARKER (0x14, 0, 0, 0))
+
+#define MAT4_BE_PCM_16	(MAKE_MARKER (0, 0, 0x04, 0x06))
+#define MAT4_LE_PCM_16	(MAKE_MARKER (0x1E, 0, 0, 0))
+
+/* Can't see any reason to ever implement this. */
+#define MAT4_BE_PCM_U8	(MAKE_MARKER (0, 0, 0x04, 0x1A))
+#define MAT4_LE_PCM_U8	(MAKE_MARKER (0x32, 0, 0, 0))
+
+/*------------------------------------------------------------------------------
+** Private static functions.
+*/
+
+static	int		mat4_close		(SF_PRIVATE *psf) ;
+
+static	int 	mat4_format_to_encoding	(int format, int endian) ;
+
+static int		mat4_write_header (SF_PRIVATE *psf, int calc_length) ;
+static int		mat4_read_header (SF_PRIVATE *psf) ;
+
+static const char * mat4_marker_to_str (int marker) ;
+
+/*------------------------------------------------------------------------------
+** Public function.
+*/
+
+int
+mat4_open	(SF_PRIVATE *psf)
+{	int		subformat, error = 0 ;
+
+	if (psf->file.mode == SFM_READ || (psf->file.mode == SFM_RDWR && psf->filelength > 0))
+	{	if ((error = mat4_read_header (psf)))
+			return error ;
+		} ;
+
+	if ((SF_CONTAINER (psf->sf.format)) != SF_FORMAT_MAT4)
+		return	SFE_BAD_OPEN_FORMAT ;
+
+	subformat = SF_CODEC (psf->sf.format) ;
+
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	if (psf->is_pipe)
+			return SFE_NO_PIPE_WRITE ;
+
+		psf->endian = SF_ENDIAN (psf->sf.format) ;
+		if (CPU_IS_LITTLE_ENDIAN && (psf->endian == SF_ENDIAN_CPU || psf->endian == 0))
+			psf->endian = SF_ENDIAN_LITTLE ;
+		else if (CPU_IS_BIG_ENDIAN && (psf->endian == SF_ENDIAN_CPU || psf->endian == 0))
+			psf->endian = SF_ENDIAN_BIG ;
+
+		if ((error = mat4_write_header (psf, SF_FALSE)))
+			return error ;
+
+		psf->write_header = mat4_write_header ;
+		} ;
+
+	psf->container_close = mat4_close ;
+
+	psf->blockwidth = psf->bytewidth * psf->sf.channels ;
+
+	switch (subformat)
+	{	case SF_FORMAT_PCM_16 :
+		case SF_FORMAT_PCM_32 :
+				error = pcm_init (psf) ;
+				break ;
+
+		case SF_FORMAT_FLOAT :
+				error = float32_init (psf) ;
+				break ;
+
+		case SF_FORMAT_DOUBLE :
+				error = double64_init (psf) ;
+				break ;
+
+		default : break ;
+		} ;
+
+	return error ;
+} /* mat4_open */
+
+/*------------------------------------------------------------------------------
+*/
+
+static int
+mat4_close	(SF_PRIVATE *psf)
+{
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+		mat4_write_header (psf, SF_TRUE) ;
+
+	return 0 ;
+} /* mat4_close */
+
+/*------------------------------------------------------------------------------
+*/
+
+static int
+mat4_write_header (SF_PRIVATE *psf, int calc_length)
+{	sf_count_t	current ;
+	int			encoding ;
+	double		samplerate ;
+
+	current = psf_ftell (psf) ;
+
+	if (calc_length)
+	{	psf->filelength = psf_get_filelen (psf) ;
+
+		psf->datalength = psf->filelength - psf->dataoffset ;
+		if (psf->dataend)
+			psf->datalength -= psf->filelength - psf->dataend ;
+
+		psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ;
+		} ;
+
+	encoding = mat4_format_to_encoding (SF_CODEC (psf->sf.format), psf->endian) ;
+
+	if (encoding == -1)
+		return SFE_BAD_OPEN_FORMAT ;
+
+	/* Reset the current header length to zero. */
+	psf->header.ptr [0] = 0 ;
+	psf->header.indx = 0 ;
+	psf_fseek (psf, 0, SEEK_SET) ;
+
+	/* Need sample rate as a double for writing to the header. */
+	samplerate = psf->sf.samplerate ;
+
+	if (psf->endian == SF_ENDIAN_BIG)
+	{	psf_binheader_writef (psf, "Em444", BHWm (MAT4_BE_DOUBLE), BHW4 (1), BHW4 (1), BHW4 (0)) ;
+		psf_binheader_writef (psf, "E4bd", BHW4 (11), BHWv ("samplerate"), BHWz (11), BHWd (samplerate)) ;
+		psf_binheader_writef (psf, "tEm484", BHWm (encoding), BHW4 (psf->sf.channels), BHW8 (psf->sf.frames), BHW4 (0)) ;
+		psf_binheader_writef (psf, "E4b", BHW4 (9), BHWv ("wavedata"), BHWz (9)) ;
+		}
+	else if (psf->endian == SF_ENDIAN_LITTLE)
+	{	psf_binheader_writef (psf, "em444", BHWm (MAT4_LE_DOUBLE), BHW4 (1), BHW4 (1), BHW4 (0)) ;
+		psf_binheader_writef (psf, "e4bd", BHW4 (11), BHWv ("samplerate"), BHWz (11), BHWd (samplerate)) ;
+		psf_binheader_writef (psf, "tem484", BHWm (encoding), BHW4 (psf->sf.channels), BHW8 (psf->sf.frames), BHW4 (0)) ;
+		psf_binheader_writef (psf, "e4b", BHW4 (9), BHWv ("wavedata"), BHWz (9)) ;
+		}
+	else
+		return SFE_BAD_OPEN_FORMAT ;
+
+	/* Header construction complete so write it out. */
+	psf_fwrite (psf->header.ptr, psf->header.indx, 1, psf) ;
+
+	if (psf->error)
+		return psf->error ;
+
+	psf->dataoffset = psf->header.indx ;
+
+	if (current > 0)
+		psf_fseek (psf, current, SEEK_SET) ;
+
+	return psf->error ;
+} /* mat4_write_header */
+
+static int
+mat4_read_header (SF_PRIVATE *psf)
+{	char	buffer [256] ;
+	uint32_t marker, namesize ;
+	int		rows, cols, imag ;
+	double	value ;
+	const char *marker_str ;
+	char	name [64] ;
+
+	psf_binheader_readf (psf, "pm", 0, &marker) ;
+
+	/* MAT4 file must start with a double for the samplerate. */
+	if (marker == MAT4_BE_DOUBLE)
+	{	psf->endian = psf->rwf_endian = SF_ENDIAN_BIG ;
+		marker_str = "big endian double" ;
+		}
+	else if (marker == MAT4_LE_DOUBLE)
+	{	psf->endian = psf->rwf_endian = SF_ENDIAN_LITTLE ;
+		marker_str = "little endian double" ;
+		}
+	else
+		return SFE_UNIMPLEMENTED ;
+
+	psf_log_printf (psf, "GNU Octave 2.0 / MATLAB v4.2 format\nMarker : %s\n", marker_str) ;
+
+	psf_binheader_readf (psf, "444", &rows, &cols, &imag) ;
+
+	psf_log_printf (psf, " Rows  : %d\n Cols  : %d\n Imag  : %s\n", rows, cols, imag ? "True" : "False") ;
+
+	psf_binheader_readf (psf, "4", &namesize) ;
+
+	if (namesize >= SIGNED_SIZEOF (name))
+		return SFE_MAT4_BAD_NAME ;
+
+	psf_binheader_readf (psf, "b", name, namesize) ;
+	name [namesize] = 0 ;
+
+	psf_log_printf (psf, " Name  : %s\n", name) ;
+
+	psf_binheader_readf (psf, "d", &value) ;
+
+	snprintf (buffer, sizeof (buffer), " Value : %f\n", value) ;
+	psf_log_printf (psf, buffer) ;
+
+	if ((rows != 1) || (cols != 1))
+		return SFE_MAT4_NO_SAMPLERATE ;
+
+	psf->sf.samplerate = psf_lrint (value) ;
+
+	/* Now write out the audio data. */
+
+	psf_binheader_readf (psf, "m", &marker) ;
+
+	psf_log_printf (psf, "Marker : %s\n", mat4_marker_to_str (marker)) ;
+
+	psf_binheader_readf (psf, "444", &rows, &cols, &imag) ;
+
+	psf_log_printf (psf, " Rows  : %d\n Cols  : %d\n Imag  : %s\n", rows, cols, imag ? "True" : "False") ;
+
+	psf_binheader_readf (psf, "4", &namesize) ;
+
+	if (namesize >= SIGNED_SIZEOF (name))
+		return SFE_MAT4_BAD_NAME ;
+
+	psf_binheader_readf (psf, "b", name, namesize) ;
+	name [namesize] = 0 ;
+
+	psf_log_printf (psf, " Name  : %s\n", name) ;
+
+	psf->dataoffset = psf_ftell (psf) ;
+
+	if (rows == 0)
+	{	psf_log_printf (psf, "*** Error : zero channel count.\n") ;
+		return SFE_CHANNEL_COUNT_ZERO ;
+		}
+	else if (rows > SF_MAX_CHANNELS)
+	{	psf_log_printf (psf, "*** Error : channel count %d > SF_MAX_CHANNELS.\n", rows) ;
+		return SFE_CHANNEL_COUNT ;
+		} ;
+
+	psf->sf.channels	= rows ;
+	psf->sf.frames		= cols ;
+
+	psf->sf.format = psf->endian | SF_FORMAT_MAT4 ;
+	switch (marker)
+	{	case MAT4_BE_DOUBLE :
+		case MAT4_LE_DOUBLE :
+				psf->sf.format |= SF_FORMAT_DOUBLE ;
+				psf->bytewidth = 8 ;
+				break ;
+
+		case MAT4_BE_FLOAT :
+		case MAT4_LE_FLOAT :
+				psf->sf.format |= SF_FORMAT_FLOAT ;
+				psf->bytewidth = 4 ;
+				break ;
+
+		case MAT4_BE_PCM_32	:
+		case MAT4_LE_PCM_32	:
+				psf->sf.format |= SF_FORMAT_PCM_32 ;
+				psf->bytewidth = 4 ;
+				break ;
+
+		case MAT4_BE_PCM_16	:
+		case MAT4_LE_PCM_16	:
+				psf->sf.format |= SF_FORMAT_PCM_16 ;
+				psf->bytewidth = 2 ;
+				break ;
+
+		default :
+				psf_log_printf (psf, "*** Error : Bad marker %08X\n", marker) ;
+				return SFE_UNIMPLEMENTED ;
+		} ;
+
+	if ((psf->filelength - psf->dataoffset) < psf->sf.channels * psf->sf.frames * psf->bytewidth)
+	{	psf_log_printf (psf, "*** File seems to be truncated. %D <--> %D\n",
+				psf->filelength - psf->dataoffset, psf->sf.channels * psf->sf.frames * psf->bytewidth) ;
+		}
+	else if ((psf->filelength - psf->dataoffset) > psf->sf.channels * psf->sf.frames * psf->bytewidth)
+		psf->dataend = psf->dataoffset + rows * cols * psf->bytewidth ;
+
+	psf->datalength = psf->filelength - psf->dataoffset - psf->dataend ;
+
+	psf->sf.sections = 1 ;
+
+	return 0 ;
+} /* mat4_read_header */
+
+static int
+mat4_format_to_encoding (int format, int endian)
+{
+	switch (format | endian)
+	{	case (SF_FORMAT_PCM_16 | SF_ENDIAN_BIG) :
+				return MAT4_BE_PCM_16 ;
+
+		case (SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE) :
+				return MAT4_LE_PCM_16 ;
+
+		case (SF_FORMAT_PCM_32 | SF_ENDIAN_BIG) :
+				return MAT4_BE_PCM_32 ;
+
+		case (SF_FORMAT_PCM_32 | SF_ENDIAN_LITTLE) :
+				return MAT4_LE_PCM_32 ;
+
+		case (SF_FORMAT_FLOAT | SF_ENDIAN_BIG) :
+				return MAT4_BE_FLOAT ;
+
+		case (SF_FORMAT_FLOAT | SF_ENDIAN_LITTLE) :
+				return MAT4_LE_FLOAT ;
+
+		case (SF_FORMAT_DOUBLE | SF_ENDIAN_BIG) :
+				return MAT4_BE_DOUBLE ;
+
+		case (SF_FORMAT_DOUBLE | SF_ENDIAN_LITTLE) :
+				return MAT4_LE_DOUBLE ;
+
+		default : break ;
+		} ;
+
+	return -1 ;
+} /* mat4_format_to_encoding */
+
+static const char *
+mat4_marker_to_str (int marker)
+{	static char str [32] ;
+
+	switch (marker)
+	{
+		case MAT4_BE_PCM_16	:	return "big endian 16 bit PCM" ;
+		case MAT4_LE_PCM_16	:	return "little endian 16 bit PCM" ;
+
+		case MAT4_BE_PCM_32	:	return "big endian 32 bit PCM" ;
+		case MAT4_LE_PCM_32	:	return "little endian 32 bit PCM" ;
+
+
+		case MAT4_BE_FLOAT :	return "big endian float" ;
+		case MAT4_LE_FLOAT :	return "big endian float" ;
+
+		case MAT4_BE_DOUBLE	:	return "big endian double" ;
+		case MAT4_LE_DOUBLE	:	return "little endian double" ;
+		} ;
+
+	/* This is a little unsafe but is really only for debugging. */
+	str [sizeof (str) - 1] = 0 ;
+	snprintf (str, sizeof (str) - 1, "%08X", marker) ;
+	return str ;
+} /* mat4_marker_to_str */
+
diff --git a/src/mat5.c b/src/mat5.c
new file mode 100644
index 0000000..da5a6ec
--- /dev/null
+++ b/src/mat5.c
@@ -0,0 +1,509 @@
+/*
+** Copyright (C) 2002-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<fcntl.h>
+#include	<string.h>
+#include	<ctype.h>
+#include	<math.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+/*------------------------------------------------------------------------------
+** Information on how to decode and encode this file was obtained in a PDF
+** file which I found on http://www.wotsit.org/.
+** Also did a lot of testing with GNU Octave but do not have access to
+** Matlab (tm) and so could not test it there.
+*/
+
+/*------------------------------------------------------------------------------
+** Macros to handle big/little endian issues.
+*/
+
+#define MATL_MARKER	(MAKE_MARKER ('M', 'A', 'T', 'L'))
+
+#define IM_MARKER	(('I' << 8) + 'M')
+#define MI_MARKER	(('M' << 8) + 'I')
+
+/*------------------------------------------------------------------------------
+** Enums and typedefs.
+*/
+
+enum
+{	MAT5_TYPE_SCHAR			= 0x1,
+	MAT5_TYPE_UCHAR			= 0x2,
+	MAT5_TYPE_INT16			= 0x3,
+	MAT5_TYPE_UINT16		= 0x4,
+	MAT5_TYPE_INT32			= 0x5,
+	MAT5_TYPE_UINT32		= 0x6,
+	MAT5_TYPE_FLOAT			= 0x7,
+	MAT5_TYPE_DOUBLE		= 0x9,
+	MAT5_TYPE_ARRAY			= 0xE,
+
+	MAT5_TYPE_COMP_USHORT	= 0x00020004,
+	MAT5_TYPE_COMP_UINT		= 0x00040006
+} ;
+
+typedef struct
+{	sf_count_t	size ;
+	int			rows, cols ;
+	char		name [32] ;
+} MAT5_MATRIX ;
+
+/*------------------------------------------------------------------------------
+** Private static functions.
+*/
+
+static int		mat5_close		(SF_PRIVATE *psf) ;
+
+static int		mat5_write_header (SF_PRIVATE *psf, int calc_length) ;
+static int		mat5_read_header (SF_PRIVATE *psf) ;
+
+/*------------------------------------------------------------------------------
+** Public function.
+*/
+
+int
+mat5_open	(SF_PRIVATE *psf)
+{	int		subformat, error = 0 ;
+
+	if (psf->file.mode == SFM_READ || (psf->file.mode == SFM_RDWR && psf->filelength > 0))
+	{	if ((error = mat5_read_header (psf)))
+			return error ;
+		} ;
+
+	if ((SF_CONTAINER (psf->sf.format)) != SF_FORMAT_MAT5)
+		return	SFE_BAD_OPEN_FORMAT ;
+
+	subformat = SF_CODEC (psf->sf.format) ;
+
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	if (psf->is_pipe)
+			return SFE_NO_PIPE_WRITE ;
+
+		psf->endian = SF_ENDIAN (psf->sf.format) ;
+		if (CPU_IS_LITTLE_ENDIAN && (psf->endian == SF_ENDIAN_CPU || psf->endian == 0))
+			psf->endian = SF_ENDIAN_LITTLE ;
+		else if (CPU_IS_BIG_ENDIAN && (psf->endian == SF_ENDIAN_CPU || psf->endian == 0))
+			psf->endian = SF_ENDIAN_BIG ;
+
+		if ((error = mat5_write_header (psf, SF_FALSE)))
+			return error ;
+
+		psf->write_header = mat5_write_header ;
+		} ;
+
+	psf->container_close = mat5_close ;
+
+	psf->blockwidth = psf->bytewidth * psf->sf.channels ;
+
+	switch (subformat)
+	{	case SF_FORMAT_PCM_U8 :
+		case SF_FORMAT_PCM_16 :
+		case SF_FORMAT_PCM_32 :
+				error = pcm_init (psf) ;
+				break ;
+
+		case SF_FORMAT_FLOAT :
+				error = float32_init (psf) ;
+				break ;
+
+		case SF_FORMAT_DOUBLE :
+				error = double64_init (psf) ;
+				break ;
+
+		default : break ;
+		} ;
+
+	return error ;
+} /* mat5_open */
+
+/*------------------------------------------------------------------------------
+*/
+
+static int
+mat5_close	(SF_PRIVATE *psf)
+{
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+		mat5_write_header (psf, SF_TRUE) ;
+
+	return 0 ;
+} /* mat5_close */
+
+/*------------------------------------------------------------------------------
+*/
+
+static int
+mat5_write_header (SF_PRIVATE *psf, int calc_length)
+{	static const char	*filename = "MATLAB 5.0 MAT-file, written by " PACKAGE_NAME "-" PACKAGE_VERSION ", " ;
+	static const char	*sr_name = "samplerate\0\0\0\0\0\0\0\0\0\0\0" ;
+	static const char	*wd_name = "wavedata\0" ;
+	char		buffer [256] ;
+	sf_count_t	current, datasize ;
+	int			encoding ;
+
+	current = psf_ftell (psf) ;
+
+	if (calc_length)
+	{	psf_fseek (psf, 0, SEEK_END) ;
+		psf->filelength = psf_ftell (psf) ;
+		psf_fseek (psf, 0, SEEK_SET) ;
+
+		psf->datalength = psf->filelength - psf->dataoffset ;
+		if (psf->dataend)
+			psf->datalength -= psf->filelength - psf->dataend ;
+
+		psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ;
+		} ;
+
+	switch (SF_CODEC (psf->sf.format))
+	{	case SF_FORMAT_PCM_U8 :
+				encoding = MAT5_TYPE_UCHAR ;
+				break ;
+
+		case SF_FORMAT_PCM_16 :
+				encoding = MAT5_TYPE_INT16 ;
+				break ;
+
+		case SF_FORMAT_PCM_32 :
+				encoding = MAT5_TYPE_INT32 ;
+				break ;
+
+		case SF_FORMAT_FLOAT :
+				encoding = MAT5_TYPE_FLOAT ;
+				break ;
+
+		case SF_FORMAT_DOUBLE :
+				encoding = MAT5_TYPE_DOUBLE ;
+				break ;
+
+		default :
+				return SFE_BAD_OPEN_FORMAT ;
+		} ;
+
+	/* Reset the current header length to zero. */
+	psf->header.ptr [0] = 0 ;
+	psf->header.indx = 0 ;
+	psf_fseek (psf, 0, SEEK_SET) ;
+
+	psf_get_date_str (buffer, sizeof (buffer)) ;
+	psf_binheader_writef (psf, "bb", BHWv (filename), BHWz (strlen (filename)), BHWv (buffer), BHWz (strlen (buffer) + 1)) ;
+
+	memset (buffer, ' ', 124 - psf->header.indx) ;
+	psf_binheader_writef (psf, "b", BHWv (buffer), BHWz (124 - psf->header.indx)) ;
+
+	psf->rwf_endian = psf->endian ;
+
+	if (psf->rwf_endian == SF_ENDIAN_BIG)
+		psf_binheader_writef (psf, "2b", BHW2 (0x0100), BHWv ("MI"), BHWz (2)) ;
+	else
+		psf_binheader_writef (psf, "2b", BHW2 (0x0100), BHWv ("IM"), BHWz (2)) ;
+
+	psf_binheader_writef (psf, "444444", BHW4 (MAT5_TYPE_ARRAY), BHW4 (64), BHW4 (MAT5_TYPE_UINT32), BHW4 (8), BHW4 (6), BHW4 (0)) ;
+	psf_binheader_writef (psf, "4444", BHW4 (MAT5_TYPE_INT32), BHW4 (8), BHW4 (1), BHW4 (1)) ;
+	psf_binheader_writef (psf, "44b", BHW4 (MAT5_TYPE_SCHAR), BHW4 (strlen (sr_name)), BHWv (sr_name), BHWz (16)) ;
+
+	if (psf->sf.samplerate > 0xFFFF)
+		psf_binheader_writef (psf, "44", BHW4 (MAT5_TYPE_COMP_UINT), BHW4 (psf->sf.samplerate)) ;
+	else
+	{	unsigned short samplerate = psf->sf.samplerate ;
+
+		psf_binheader_writef (psf, "422", BHW4 (MAT5_TYPE_COMP_USHORT), BHW2 (samplerate), BHW2 (0)) ;
+		} ;
+
+	datasize = psf->sf.frames * psf->sf.channels * psf->bytewidth ;
+
+	psf_binheader_writef (psf, "t484444", BHW4 (MAT5_TYPE_ARRAY), BHW8 (datasize + 64), BHW4 (MAT5_TYPE_UINT32), BHW4 (8), BHW4 (6), BHW4 (0)) ;
+	psf_binheader_writef (psf, "t4448", BHW4 (MAT5_TYPE_INT32), BHW4 (8), BHW4 (psf->sf.channels), BHW8 (psf->sf.frames)) ;
+	psf_binheader_writef (psf, "44b", BHW4 (MAT5_TYPE_SCHAR), BHW4 (strlen (wd_name)), BHWv (wd_name), BHWz (strlen (wd_name))) ;
+
+	datasize = psf->sf.frames * psf->sf.channels * psf->bytewidth ;
+	if (datasize > 0x7FFFFFFF)
+		datasize = 0x7FFFFFFF ;
+
+	psf_binheader_writef (psf, "t48", BHW4 (encoding), BHW8 (datasize)) ;
+
+	/* Header construction complete so write it out. */
+	psf_fwrite (psf->header.ptr, psf->header.indx, 1, psf) ;
+
+	if (psf->error)
+		return psf->error ;
+
+	psf->dataoffset = psf->header.indx ;
+
+	if (current > 0)
+		psf_fseek (psf, current, SEEK_SET) ;
+
+	return psf->error ;
+} /* mat5_write_header */
+
+static int
+mat5_read_header (SF_PRIVATE *psf)
+{	char	buffer [256], name [32] ;
+	short	version, endian ;
+	int		type, flags1, flags2, rows, cols ;
+	unsigned size ;
+	int		have_samplerate = 1 ;
+
+	psf_binheader_readf (psf, "pb", 0, buffer, 124) ;
+
+	buffer [125] = 0 ;
+
+	if (strlen (buffer) >= 124)
+		return SFE_UNIMPLEMENTED ;
+
+	if (strstr (buffer, "MATLAB 5.0 MAT-file") == buffer)
+		psf_log_printf (psf, "%s\n", buffer) ;
+
+
+	psf_binheader_readf (psf, "E22", &version, &endian) ;
+
+	if (endian == MI_MARKER)
+	{	psf->endian = psf->rwf_endian = SF_ENDIAN_BIG ;
+		if (CPU_IS_LITTLE_ENDIAN) version = ENDSWAP_16 (version) ;
+		}
+	else if (endian == IM_MARKER)
+	{	psf->endian = psf->rwf_endian = SF_ENDIAN_LITTLE ;
+		if (CPU_IS_BIG_ENDIAN) version = ENDSWAP_16 (version) ;
+		}
+	else
+		return SFE_MAT5_BAD_ENDIAN ;
+
+	if ((CPU_IS_LITTLE_ENDIAN && endian == IM_MARKER) ||
+			(CPU_IS_BIG_ENDIAN && endian == MI_MARKER))
+		version = ENDSWAP_16 (version) ;
+
+	psf_log_printf (psf, "Version : 0x%04X\n", version) ;
+	psf_log_printf (psf, "Endian  : 0x%04X => %s\n", endian,
+				(psf->endian == SF_ENDIAN_LITTLE) ? "Little" : "Big") ;
+
+	/*========================================================*/
+	psf_binheader_readf (psf, "44", &type, &size) ;
+	psf_log_printf (psf, "Block\n Type : %X    Size : %d\n", type, size) ;
+
+	if (type != MAT5_TYPE_ARRAY)
+		return SFE_MAT5_NO_BLOCK ;
+
+	psf_binheader_readf (psf, "44", &type, &size) ;
+	psf_log_printf (psf, "    Type : %X    Size : %d\n", type, size) ;
+
+	if (type != MAT5_TYPE_UINT32)
+		return SFE_MAT5_NO_BLOCK ;
+
+	psf_binheader_readf (psf, "44", &flags1, &flags2) ;
+	psf_log_printf (psf, "    Flg1 : %X    Flg2 : %d\n", flags1, flags2) ;
+
+	psf_binheader_readf (psf, "44", &type, &size) ;
+	psf_log_printf (psf, "    Type : %X    Size : %d\n", type, size) ;
+
+	if (type != MAT5_TYPE_INT32)
+		return SFE_MAT5_NO_BLOCK ;
+
+	psf_binheader_readf (psf, "44", &rows, &cols) ;
+	psf_log_printf (psf, "    Rows : %d    Cols : %d\n", rows, cols) ;
+
+	if (rows != 1 || cols != 1)
+	{	if (psf->sf.samplerate == 0)
+			psf->sf.samplerate = 44100 ;
+		have_samplerate = 0 ;
+		}
+	psf_binheader_readf (psf, "4", &type) ;
+
+	if (type == MAT5_TYPE_SCHAR)
+	{	psf_binheader_readf (psf, "4", &size) ;
+		psf_log_printf (psf, "    Type : %X    Size : %d\n", type, size) ;
+		if (size > SIGNED_SIZEOF (name) - 1)
+		{	psf_log_printf (psf, "Error : Bad name length.\n") ;
+			return SFE_MAT5_NO_BLOCK ;
+			} ;
+
+		psf_binheader_readf (psf, "bj", name, size, (8 - (size % 8)) % 8) ;
+		name [size] = 0 ;
+		}
+	else if ((type & 0xFFFF) == MAT5_TYPE_SCHAR)
+	{	size = type >> 16 ;
+		if (size > 4)
+		{	psf_log_printf (psf, "Error : Bad name length.\n") ;
+			return SFE_MAT5_NO_BLOCK ;
+			} ;
+
+		psf_log_printf (psf, "    Type : %X\n", type) ;
+		psf_binheader_readf (psf, "4", &name) ;
+		name [size] = 0 ;
+		}
+	else
+		return SFE_MAT5_NO_BLOCK ;
+
+	psf_log_printf (psf, "    Name : %s\n", name) ;
+
+	/*-----------------------------------------*/
+
+	psf_binheader_readf (psf, "44", &type, &size) ;
+
+	if (!have_samplerate)
+		goto skip_samplerate ;
+
+	switch (type)
+	{	case MAT5_TYPE_DOUBLE :
+				{	double	samplerate ;
+
+					psf_binheader_readf (psf, "d", &samplerate) ;
+					snprintf (name, sizeof (name), "%f\n", samplerate) ;
+					psf_log_printf (psf, "    Val  : %s\n", name) ;
+
+					psf->sf.samplerate = psf_lrint (samplerate) ;
+					} ;
+				break ;
+
+		case MAT5_TYPE_COMP_USHORT :
+				{	unsigned short samplerate ;
+
+					psf_binheader_readf (psf, "j2j", -4, &samplerate, 2) ;
+					psf_log_printf (psf, "    Val  : %u\n", samplerate) ;
+					psf->sf.samplerate = samplerate ;
+					}
+				break ;
+
+		case MAT5_TYPE_COMP_UINT :
+				psf_log_printf (psf, "    Val  : %u\n", size) ;
+				psf->sf.samplerate = size ;
+				break ;
+
+		default :
+			psf_log_printf (psf, "    Type : %X    Size : %d  ***\n", type, size) ;
+			return SFE_MAT5_SAMPLE_RATE ;
+		} ;
+
+	/*-----------------------------------------*/
+
+
+	psf_binheader_readf (psf, "44", &type, &size) ;
+	psf_log_printf (psf, " Type : %X    Size : %d\n", type, size) ;
+
+	if (type != MAT5_TYPE_ARRAY)
+		return SFE_MAT5_NO_BLOCK ;
+
+	psf_binheader_readf (psf, "44", &type, &size) ;
+	psf_log_printf (psf, "    Type : %X    Size : %d\n", type, size) ;
+
+	if (type != MAT5_TYPE_UINT32)
+		return SFE_MAT5_NO_BLOCK ;
+
+	psf_binheader_readf (psf, "44", &flags1, &flags2) ;
+	psf_log_printf (psf, "    Flg1 : %X    Flg2 : %d\n", flags1, flags2) ;
+
+	psf_binheader_readf (psf, "44", &type, &size) ;
+	psf_log_printf (psf, "    Type : %X    Size : %d\n", type, size) ;
+
+	if (type != MAT5_TYPE_INT32)
+		return SFE_MAT5_NO_BLOCK ;
+
+	psf_binheader_readf (psf, "44", &rows, &cols) ;
+	psf_log_printf (psf, "    Rows : %X    Cols : %d\n", rows, cols) ;
+
+	psf_binheader_readf (psf, "4", &type) ;
+
+	if (type == MAT5_TYPE_SCHAR)
+	{	psf_binheader_readf (psf, "4", &size) ;
+		psf_log_printf (psf, "    Type : %X    Size : %d\n", type, size) ;
+		if (size > SIGNED_SIZEOF (name) - 1)
+		{	psf_log_printf (psf, "Error : Bad name length.\n") ;
+			return SFE_MAT5_NO_BLOCK ;
+			} ;
+
+		psf_binheader_readf (psf, "bj", name, size, (8 - (size % 8)) % 8) ;
+		name [size] = 0 ;
+		}
+	else if ((type & 0xFFFF) == MAT5_TYPE_SCHAR)
+	{	size = type >> 16 ;
+		if (size > 4)
+		{	psf_log_printf (psf, "Error : Bad name length.\n") ;
+			return SFE_MAT5_NO_BLOCK ;
+			} ;
+
+		psf_log_printf (psf, "    Type : %X\n", type) ;
+		psf_binheader_readf (psf, "4", &name) ;
+		name [size] = 0 ;
+		}
+	else
+		return SFE_MAT5_NO_BLOCK ;
+
+	psf_log_printf (psf, "    Name : %s\n", name) ;
+
+	psf_binheader_readf (psf, "44", &type, &size) ;
+	psf_log_printf (psf, "    Type : %X    Size : %d\n", type, size) ;
+
+skip_samplerate :
+	/*++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+	if (rows == 0 && cols == 0)
+	{	psf_log_printf (psf, "*** Error : zero channel count.\n") ;
+		return SFE_CHANNEL_COUNT_ZERO ;
+		} ;
+
+	psf->sf.channels	= rows ;
+	psf->sf.frames		= cols ;
+
+	psf->sf.format = psf->endian | SF_FORMAT_MAT5 ;
+
+	switch (type)
+	{	case MAT5_TYPE_DOUBLE :
+				psf_log_printf (psf, "Data type : double\n") ;
+				psf->sf.format |= SF_FORMAT_DOUBLE ;
+				psf->bytewidth = 8 ;
+				break ;
+
+		case MAT5_TYPE_FLOAT :
+				psf_log_printf (psf, "Data type : float\n") ;
+				psf->sf.format |= SF_FORMAT_FLOAT ;
+				psf->bytewidth = 4 ;
+				break ;
+
+		case MAT5_TYPE_INT32 :
+				psf_log_printf (psf, "Data type : 32 bit PCM\n") ;
+				psf->sf.format |= SF_FORMAT_PCM_32 ;
+				psf->bytewidth = 4 ;
+				break ;
+
+		case MAT5_TYPE_INT16 :
+				psf_log_printf (psf, "Data type : 16 bit PCM\n") ;
+				psf->sf.format |= SF_FORMAT_PCM_16 ;
+				psf->bytewidth = 2 ;
+				break ;
+
+		case MAT5_TYPE_UCHAR :
+				psf_log_printf (psf, "Data type : unsigned 8 bit PCM\n") ;
+				psf->sf.format |= SF_FORMAT_PCM_U8 ;
+				psf->bytewidth = 1 ;
+				break ;
+
+		default :
+				psf_log_printf (psf, "*** Error : Bad marker %08X\n", type) ;
+				return SFE_UNIMPLEMENTED ;
+		} ;
+
+	psf->dataoffset = psf_ftell (psf) ;
+	psf->datalength = psf->filelength - psf->dataoffset ;
+
+	return 0 ;
+} /* mat5_read_header */
+
diff --git a/src/mpc2k.c b/src/mpc2k.c
new file mode 100644
index 0000000..733f1f4
--- /dev/null
+++ b/src/mpc2k.c
@@ -0,0 +1,202 @@
+/*
+** Copyright (C) 2008-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "sndfile.h"
+#include "sfendian.h"
+#include "common.h"
+
+/*
+**	Info from Olivier Tristan <o.tristan@ultimatesoundbank.com>
+**
+**	HEADER
+**	2 magic bytes: 1 and 4.
+**	17 char for the name of the sample.
+**	3 bytes: level, tune and channels (0 for channels is mono while 1 is stereo)
+**	4 uint32: sampleStart, loopEnd, sampleFrames and loopLength
+**	1 byte: loopMode (0 no loop, 1 forward looping)
+**	1 byte: number of beat in loop
+**	1 uint16: sampleRate
+**
+**	DATA
+**	Data are always non compressed 16 bits interleaved
+*/
+
+#define HEADER_LENGTH		42	/* Sum of above data fields. */
+#define HEADER_NAME_LEN		17	/* Length of name string. */
+
+/*------------------------------------------------------------------------------
+** Private static functions.
+*/
+
+static	int		mpc2k_close		(SF_PRIVATE *psf) ;
+
+static int		mpc2k_write_header (SF_PRIVATE *psf, int calc_length) ;
+static int		mpc2k_read_header (SF_PRIVATE *psf) ;
+
+/*------------------------------------------------------------------------------
+** Public function.
+*/
+
+int
+mpc2k_open	(SF_PRIVATE *psf)
+{	int		error = 0 ;
+
+	if (psf->file.mode == SFM_READ || (psf->file.mode == SFM_RDWR && psf->filelength > 0))
+	{	if ((error = mpc2k_read_header (psf)))
+			return error ;
+		} ;
+
+	if ((SF_CONTAINER (psf->sf.format)) != SF_FORMAT_MPC2K)
+		return	SFE_BAD_OPEN_FORMAT ;
+
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	if (mpc2k_write_header (psf, SF_FALSE))
+			return psf->error ;
+
+		psf->write_header = mpc2k_write_header ;
+		} ;
+
+	psf->container_close = mpc2k_close ;
+
+	psf->blockwidth = psf->bytewidth * psf->sf.channels ;
+
+	error = pcm_init (psf) ;
+
+	return error ;
+} /* mpc2k_open */
+
+/*------------------------------------------------------------------------------
+*/
+
+static int
+mpc2k_close	(SF_PRIVATE *psf)
+{
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+		mpc2k_write_header (psf, SF_TRUE) ;
+
+	return 0 ;
+} /* mpc2k_close */
+
+static int
+mpc2k_write_header (SF_PRIVATE *psf, int calc_length)
+{	char sample_name [HEADER_NAME_LEN + 1] ;
+	sf_count_t	current ;
+
+	if (psf->pipeoffset > 0)
+		return 0 ;
+
+	current = psf_ftell (psf) ;
+
+	if (calc_length)
+	{	psf->filelength = psf_get_filelen (psf) ;
+
+		psf->dataoffset = HEADER_LENGTH ;
+		psf->datalength = psf->filelength - psf->dataoffset ;
+
+		psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ;
+		} ;
+
+	/* Reset the current header length to zero. */
+	psf->header.ptr [0] = 0 ;
+	psf->header.indx = 0 ;
+
+	/*
+	** Only attempt to seek if we are not writng to a pipe. If we are
+	** writing to a pipe we shouldn't be here anyway.
+	*/
+	if (psf->is_pipe == SF_FALSE)
+		psf_fseek (psf, 0, SEEK_SET) ;
+
+	snprintf (sample_name, sizeof (sample_name), "%-*.*s", HEADER_NAME_LEN, HEADER_NAME_LEN, psf->file.name) ;
+
+	psf_binheader_writef (psf, "e11b", BHW1 (1), BHW1 (4), BHWv (sample_name), BHWz (HEADER_NAME_LEN)) ;
+	psf_binheader_writef (psf, "e111", BHW1 (100), BHW1 (0), BHW1 ((psf->sf.channels - 1) & 1)) ;
+	psf_binheader_writef (psf, "et4888", BHW4 (0), BHW8 (psf->sf.frames), BHW8 (psf->sf.frames), BHW8 (psf->sf.frames)) ;
+	psf_binheader_writef (psf, "e112", BHW1 (0), BHW1 (1), BHW2 ((uint16_t) psf->sf.samplerate)) ;
+
+	/* Always 16 bit little endian data. */
+	psf->bytewidth = 2 ;
+	psf->endian = SF_ENDIAN_LITTLE ;
+
+	psf_fwrite (psf->header.ptr, psf->header.indx, 1, psf) ;
+
+	if (psf->error)
+		return psf->error ;
+
+	psf->dataoffset = psf->header.indx ;
+
+	if (current > 0)
+		psf_fseek (psf, current, SEEK_SET) ;
+
+	return psf->error ;
+} /* mpc2k_write_header */
+
+static int
+mpc2k_read_header (SF_PRIVATE *psf)
+{	char sample_name [HEADER_NAME_LEN + 1] ;
+	unsigned char bytes [4] ;
+	uint32_t sample_start, loop_end, sample_frames, loop_length ;
+	uint16_t sample_rate ;
+
+	psf_binheader_readf (psf, "pebb", 0, bytes, 2, sample_name, make_size_t (HEADER_NAME_LEN)) ;
+
+	if (bytes [0] != 1 || bytes [1] != 4)
+		return SFE_MPC_NO_MARKER ;
+
+	sample_name [HEADER_NAME_LEN] = 0 ;
+
+	psf_log_printf (psf, "MPC2000\n  Name         : %s\n", sample_name) ;
+
+	psf_binheader_readf (psf, "eb4444", bytes, 3, &sample_start, &loop_end, &sample_frames, &loop_length) ;
+
+	psf->sf.channels = bytes [2] ? 2 : 1 ;
+
+	psf_log_printf (psf, "  Level        : %d\n  Tune         : %d\n  Stereo       : %s\n", bytes [0], bytes [1], bytes [2] ? "Yes" : "No") ;
+
+	psf_log_printf (psf, "  Sample start : %d\n  Loop end     : %d\n  Frames       : %d\n  Length       : %d\n", sample_start, loop_end, sample_frames, loop_length) ;
+
+	psf_binheader_readf (psf, "eb2", bytes, 2, &sample_rate) ;
+
+	psf_log_printf (psf, "  Loop mode    : %s\n  Beats        : %d\n  Sample rate  : %d\nEnd\n", bytes [0] ? "None" : "Fwd", bytes [1], sample_rate) ;
+
+	psf->sf.samplerate = sample_rate ;
+
+	psf->sf.format = SF_FORMAT_MPC2K | SF_FORMAT_PCM_16 ;
+
+	psf->dataoffset = psf_ftell (psf) ;
+
+	/* Always 16 bit little endian data. */
+	psf->bytewidth = 2 ;
+	psf->endian = SF_ENDIAN_LITTLE ;
+
+	psf->datalength = psf->filelength - psf->dataoffset ;
+	psf->blockwidth = psf->sf.channels * psf->bytewidth ;
+	psf->sf.frames = psf->datalength / psf->blockwidth ;
+
+	psf->sf.frames = (psf->filelength - psf->dataoffset) / psf->blockwidth ;
+
+	return 0 ;
+} /* mpc2k_read_header */
+
diff --git a/src/mpeg.c b/src/mpeg.c
new file mode 100644
index 0000000..44db0ad
--- /dev/null
+++ b/src/mpeg.c
@@ -0,0 +1,165 @@
+/*
+** Copyright (C) 2019 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2021 Arthur Taylor <art@ified.ca>
+**
+** This program is free software ; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation ; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program ; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	"sndfile.h"
+#include	"common.h"
+
+#if HAVE_MPEG
+
+#include "mpeg.h"
+
+static int	mpeg_write_header (SF_PRIVATE *psf, int calc_length) ;
+static int	mpeg_command (SF_PRIVATE *psf, int command, void *data, int datasize) ;
+
+/*------------------------------------------------------------------------------
+ * Private functions
+ */
+
+static int
+mpeg_write_header (SF_PRIVATE *psf, int UNUSED (calc_length))
+{
+	if (psf->have_written)
+		return 0 ;
+
+	return mpeg_l3_encoder_write_id3tag (psf) ;
+}
+
+static int
+mpeg_command (SF_PRIVATE *psf, int command, void *data, int datasize)
+{	int bitrate_mode ;
+
+	switch (command)
+	{	case SFC_SET_COMPRESSION_LEVEL :
+			if (data == NULL || datasize != sizeof (double))
+			{	psf->error = SFE_BAD_COMMAND_PARAM ;
+				return SF_FALSE ;
+				} ;
+			if (psf->file.mode != SFM_WRITE)
+			{	psf->error = SFE_NOT_WRITEMODE ;
+				return SF_FALSE ;
+				} ;
+			return mpeg_l3_encoder_set_quality (psf, *(double *) data) ;
+
+		case SFC_SET_BITRATE_MODE :
+			if (psf->file.mode != SFM_WRITE)
+			{	psf->error = SFE_NOT_WRITEMODE ;
+				return SF_FALSE ;
+				} ;
+			if (data == NULL || datasize != sizeof (int))
+			{	psf->error = SFE_BAD_COMMAND_PARAM ;
+				return SF_FALSE ;
+				} ;
+			bitrate_mode = *(int *) data ;
+			return mpeg_l3_encoder_set_bitrate_mode (psf, bitrate_mode) ;
+
+		case SFC_GET_BITRATE_MODE :
+			if (psf->file.mode == SFM_READ)
+				return mpeg_decoder_get_bitrate_mode (psf) ;
+			else
+				return mpeg_l3_encoder_get_bitrate_mode (psf) ;
+
+		default :
+			return SF_FALSE ;
+		} ;
+
+	return SF_FALSE ;
+} /* mpeg_command */
+
+/*------------------------------------------------------------------------------
+ * Public functions
+ */
+
+int
+mpeg_init (SF_PRIVATE *psf, int bitrate_mode, int write_metadata)
+{	int error ;
+
+	if (psf->file.mode == SFM_RDWR)
+		return SFE_BAD_MODE_RW ;
+
+	if (psf->file.mode == SFM_WRITE)
+	{	switch (SF_CODEC (psf->sf.format))
+		{	case SF_FORMAT_MPEG_LAYER_III :
+				if ((error = mpeg_l3_encoder_init (psf, write_metadata)))
+					return error ;
+				mpeg_l3_encoder_set_bitrate_mode (psf, bitrate_mode) ;
+				if (write_metadata)
+				{	/* ID3 support */
+					psf->strings.flags = SF_STR_ALLOW_START ;
+					psf->write_header = mpeg_write_header ;
+					} ;
+				break ;
+
+			case SF_FORMAT_MPEG_LAYER_I :
+			case SF_FORMAT_MPEG_LAYER_II :
+				psf_log_printf (psf, "MPEG Layer I and II encoding is not yet supported.\n") ;
+				return SFE_UNIMPLEMENTED ;
+
+			default:
+				psf_log_printf (psf, "%s: bad psf->sf.format 0x%x.\n", __func__, psf->sf.format) ;
+				return SFE_INTERNAL ;
+			} ;
+		} ;
+
+	if (psf->file.mode == SFM_READ)
+	{	if ((error = mpeg_decoder_init (psf)))
+			return error ;
+		} ;
+
+	return 0 ;
+} /* mpeg_init */
+
+int
+mpeg_open (SF_PRIVATE *psf)
+{	int error ;
+
+	/* Choose variable bitrate mode by default for standalone files.*/
+	if ((error = mpeg_init (psf, SF_BITRATE_MODE_VARIABLE, SF_TRUE)))
+		return error ;
+
+	psf->dataoffset = 0 ;
+	psf->command = mpeg_command ;
+
+	if (psf->filelength != SF_COUNT_MAX)
+		psf->datalength = psf->filelength - psf->dataoffset ;
+	else
+		psf->datalength = SF_COUNT_MAX ;
+
+
+	return 0 ;
+} /* mpeg_open */
+
+#else /* HAVE_MPEG */
+
+int
+mpeg_init (SF_PRIVATE *psf, int UNUSED (bitrate_mode) , int UNUSED (write_metadata))
+{
+	psf_log_printf (psf, "This version of libsndfile was compiled without MPEG support.\n") ;
+	return SFE_UNIMPLEMENTED ;
+} /* mpeg_init */
+
+int
+mpeg_open (SF_PRIVATE *psf)
+{
+	psf_log_printf (psf, "This version of libsndfile was compiled without MP3 support.\n") ;
+	return SFE_UNIMPLEMENTED ;
+} /* mpeg_open */
+
+#endif
diff --git a/src/mpeg.h b/src/mpeg.h
new file mode 100644
index 0000000..f335745
--- /dev/null
+++ b/src/mpeg.h
@@ -0,0 +1,74 @@
+/*
+** Copyright (C) 2019 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2019 Arthur Taylor <art@ified.ca>
+**
+** This program is free software ; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation ; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program ; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef SNDFILE_MPEG_H
+#define SNDFILE_MPEG_H
+
+#include	"common.h"
+
+int mpeg_decoder_init (SF_PRIVATE *psf) ;
+
+/*
+** Get the file bitrate mode, returning one of the SF_BITRATE_MODE_ enum
+** values. Purely informative, 'Frankenstein' files and VBR files without an
+** Xing/LAME/Info header may not be detected properly.
+*/
+int mpeg_decoder_get_bitrate_mode (SF_PRIVATE *psf) ;
+
+
+/*
+** Initialize an encoder instance for writing. If parameter info_tag is
+** SF_TRUE, a Xing/LAME/Info header is written at the beginning of the file,
+** (unless the file cannot seek.)
+*/
+int mpeg_l3_encoder_init (SF_PRIVATE *psf, int info_tag) ;
+
+
+/*
+** Write an ID3v2 header from the sndfile string metadata. Must be called
+** before any audio data is written. Writing an ID3v2 header will also cause
+** a ID3v1 trailer to be written on close automatically.
+*/
+int mpeg_l3_encoder_write_id3tag (SF_PRIVATE *psf) ;
+
+/*
+** Set the encoder quality setting. Argument to compression should be identical
+** to that for SFC_SET_COMPRESSION_LEVEL; It should be in the range [0-1],
+** with 0 being highest quality, least compression, and 1 being the opposite.
+** Returns SF_TRUE on success, SF_FALSE otherwise.
+*/
+int mpeg_l3_encoder_set_quality (SF_PRIVATE *psf, double compression) ;
+
+/*
+** Set the encoder bitrate mode. Can only be called before any data has been
+** written. Argument mode should be one of the SF_BITRATE_MODE_ enum values.
+** Returns SF_TRUE on success, SF_FALSE otherwise. The SF_BITRATE_MODE_FILE
+** enum value should not be passed here but rather intercepted at the container
+** level and translated according to the container.
+*/
+int mpeg_l3_encoder_set_bitrate_mode (SF_PRIVATE *psf, int mode) ;
+
+/*
+** Get the encoder bitrate mode in use. Returns a SF_BITRATE_MODE_ enum value.
+** Will not return SF_BITRATE_MODE_FILE.
+*/
+int mpeg_l3_encoder_get_bitrate_mode (SF_PRIVATE *psf) ;
+
+
+#endif /* SNDFILE_MPEG_H */
diff --git a/src/mpeg_decode.c b/src/mpeg_decode.c
new file mode 100644
index 0000000..aaf12bf
--- /dev/null
+++ b/src/mpeg_decode.c
@@ -0,0 +1,644 @@
+/*
+** Copyright (C) 2019 - 2021 Arthur Taylor <art@ified.ca>
+** Copyright (C) 2019 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software ; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation ; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program ; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<math.h>
+
+#include	"sndfile.h"
+#include	"common.h"
+#include	"mpeg.h"
+
+#if HAVE_MPEG
+
+#include	"sfendian.h"
+#include	"id3.h"
+
+#include <mpg123.h>
+
+typedef struct
+{	mpg123_handle *pmh ;
+	size_t header_remaining ;
+} MPEG_DEC_PRIVATE ;
+
+static int mpeg_dec_close (SF_PRIVATE *psf) ;
+static sf_count_t mpeg_dec_seek (SF_PRIVATE *psf, int whence, sf_count_t count) ;
+
+static ssize_t mpeg_dec_io_read (void *priv, void *buffer, size_t nbytes) ;
+static off_t mpeg_dec_io_lseek (void *priv, off_t offset, int whence) ;
+
+static ssize_t
+mpeg_dec_io_read (void *priv, void *buffer, size_t nbytes)
+{	SF_PRIVATE *psf = (SF_PRIVATE *) priv ;
+	MPEG_DEC_PRIVATE *pmp3d = (MPEG_DEC_PRIVATE *) psf->codec_data ;
+
+	if (pmp3d->header_remaining)
+	{	if (pmp3d->header_remaining < nbytes)
+			nbytes = pmp3d->header_remaining ;
+		psf_binheader_readf (psf, "b", buffer, nbytes) ;
+		pmp3d->header_remaining -= nbytes ;
+		return nbytes ;
+		} ;
+
+	return psf_fread (buffer, 1, nbytes, psf) ;
+} /* mpeg_dec_io_read */
+
+static off_t
+mpeg_dec_io_lseek (void *priv, off_t offset, int whence)
+{	SF_PRIVATE *psf = (SF_PRIVATE *) priv ;
+
+	return psf_fseek (psf, offset, whence) ;
+} /* mpeg_dec_io_lseek */
+
+static int
+mpeg_dec_close (SF_PRIVATE *psf)
+{	MPEG_DEC_PRIVATE *pmp3d = (MPEG_DEC_PRIVATE *) psf->codec_data ;
+
+	if (pmp3d)
+	{	if (pmp3d->pmh)
+		{	mpg123_close (pmp3d->pmh) ;
+			mpg123_delete (pmp3d->pmh) ;
+			pmp3d->pmh = NULL ;
+			}
+		free (psf->codec_data) ;
+		psf->codec_data = NULL ;
+		} ;
+
+	return 0 ;
+} /* mpeg_dec_close */
+
+static sf_count_t
+mpeg_dec_decode (SF_PRIVATE *psf, float *ptr, sf_count_t len)
+{	MPEG_DEC_PRIVATE *pmp3d = (MPEG_DEC_PRIVATE *) psf->codec_data ;
+	size_t done ;
+	int error ;
+
+	error = mpg123_read (pmp3d->pmh, (unsigned char *) ptr, len * sizeof (float), &done) ;
+
+	if (error == MPG123_OK || error == MPG123_DONE)
+		return done / sizeof (float) ;
+
+	if (error == MPG123_NEW_FORMAT)
+	{	psf->error = SFE_MALFORMED_FILE ;
+		return -1 ;
+		} ;
+
+	psf->error = SFE_INTERNAL ;
+	return -1 ;
+} /* mpeg_dec_decode */
+
+static sf_count_t
+mpeg_dec_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len)
+{	BUF_UNION ubuf ;
+	sf_count_t total, readlen ;
+	void (*convert) (const float *, short *, int, int) ;
+	const sf_count_t buflen = ARRAY_LEN (ubuf.fbuf) ;
+
+	convert = (psf->add_clipping) ? psf_f2s_clip_array : psf_f2s_array ;
+	for (total = 0 ; total < len ; total += readlen)
+	{	readlen = mpeg_dec_decode (psf, ubuf.fbuf, SF_MIN (buflen, len - total)) ;
+		if (readlen <= 0)
+			break ;
+
+		convert (ubuf.fbuf, ptr + total, readlen, SF_TRUE) ;
+		} ;
+
+	return total ;
+} /*mpeg_dec_read_s */
+
+static sf_count_t
+mpeg_dec_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len)
+{	BUF_UNION ubuf ;
+	sf_count_t total, readlen ;
+	void (*convert) (const float *, int *, int, int) ;
+	const sf_count_t buflen = ARRAY_LEN (ubuf.fbuf) ;
+
+	convert = (psf->add_clipping) ? psf_f2i_clip_array : psf_f2i_array ;
+	for (total = 0 ; total < len ; total += readlen)
+	{	readlen = mpeg_dec_decode (psf, ubuf.fbuf, SF_MIN (buflen, len - total)) ;
+		if (readlen <= 0)
+			break ;
+
+		convert (ubuf.fbuf, ptr + total, readlen, SF_TRUE) ;
+		} ;
+
+	return total ;
+} /* mpeg_dec_read_i */
+
+static sf_count_t
+mpeg_dec_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len)
+{	sf_count_t readlen ;
+
+	readlen = mpeg_dec_decode (psf, ptr, len) ;
+	if (readlen <= 0)
+		return 0 ;
+
+	if (psf->norm_float == SF_FALSE)
+		for (int i = 0 ; i < readlen ; i++)
+		{	ptr [i] *= (1.0f * 0x8000) ;
+			} ;
+
+	return readlen ;
+} /* mpeg_dec_read_f */
+
+static inline void
+f2d_array (const float *src, int count, double *dest, double normfact)
+{	for (int i = 0 ; i < count ; i++)
+	{	dest [i] = src [i] * normfact ;
+		}
+} /* f2d_array */
+
+static sf_count_t
+mpeg_dec_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len)
+{	BUF_UNION ubuf ;
+	sf_count_t total, readlen ;
+	double normfact ;
+	const sf_count_t buflen = ARRAY_LEN (ubuf.fbuf) ;
+
+	normfact = (psf->norm_double == SF_TRUE) ? 1.0 : (1.0 * 0x8000) ;
+
+	for (total = 0 ; total < len ; total += readlen)
+	{	readlen = mpeg_dec_decode (psf, ubuf.fbuf, SF_MIN (buflen, len - total)) ;
+		if (readlen <= 0)
+			break ;
+
+		f2d_array (ubuf.fbuf, readlen, ptr + total, normfact) ;
+		} ;
+
+	return total ;
+} /* mpeg_dec_read_d */
+
+static sf_count_t
+mpeg_dec_seek (SF_PRIVATE *psf, int mode, sf_count_t count)
+{	MPEG_DEC_PRIVATE *pmp3d = (MPEG_DEC_PRIVATE *) psf->codec_data ;
+	off_t ret ;
+
+	if (mode != SFM_READ || psf->file.mode != SFM_READ)
+	{	psf->error = SFE_BAD_SEEK ;
+		return PSF_SEEK_ERROR ;
+		} ;
+
+	ret = mpg123_seek (pmp3d->pmh, count, SEEK_SET) ;
+
+	if (ret < 0)
+		return PSF_SEEK_ERROR ;
+
+	return (sf_count_t) ret ;
+} /* mpeg_dec_seek */
+
+static int
+mpeg_dec_fill_sfinfo (SF_PRIVATE* psf, mpg123_handle *mh, SF_INFO *info)
+{	int error ;
+	int channels ;
+	int encoding ;
+	long rate ;
+	off_t length ;
+
+	error = mpg123_getformat (mh, &rate, &channels, &encoding) ;
+	if (error != MPG123_OK)
+		return error ;
+
+	info->samplerate = rate ;
+	info->channels = channels ;
+
+	length = mpg123_length (mh) ;
+	if (length <= 0 && !psf->is_pipe)
+	{	if ((error = mpg123_scan (mh)) != MPG123_OK)
+			return error ;
+		length = mpg123_length (mh) ;
+		}
+
+	if (length >= 0)
+	{	info->frames = length ;
+		info->seekable = SF_TRUE ;
+		}
+	else
+	{	info->frames = SF_COUNT_MAX ;
+		info->seekable = SF_FALSE ;
+		}
+
+	/* Force 32-bit float samples. */
+	if (encoding != MPG123_ENC_FLOAT_32)
+	{	error = mpg123_format (mh, rate, channels, MPG123_ENC_FLOAT_32) ;
+		} ;
+
+	return error ;
+} /* mpeg_dec_fill_sfinfo */
+
+static void
+mpeg_dec_print_frameinfo (SF_PRIVATE *psf, const struct mpg123_frameinfo *fi)
+{	psf_log_printf (psf, "MPEG-1/2 Audio\n----------------------------------------\n") ;
+	psf_log_printf (psf, "  MPEG version   : %s\n",
+		fi->version == MPG123_1_0 ? "MPEG 1.0" :
+		fi->version == MPG123_2_0 ? "MPEG 2.0" :
+		fi->version == MPG123_2_5 ? "MPEG 2.5" : "???") ;
+	psf_log_printf (psf, "  layer          : %d\n", fi->layer) ;
+	psf_log_printf (psf, "  rate           : %d\n", fi->rate) ;
+	psf_log_printf (psf, "  mode           : %s\n",
+		fi->mode == MPG123_M_STEREO ? "stereo" :
+		fi->mode == MPG123_M_JOINT ? "joint stereo" :
+		fi->mode == MPG123_M_DUAL ? "dual channel" :
+		fi->mode == MPG123_M_MONO ? "mono" : "???") ;
+	psf_log_printf (psf, "  mode ext       : %d\n", fi->mode_ext) ;
+	psf_log_printf (psf, "  framesize      : %d\n", fi->framesize) ;
+	psf_log_printf (psf, "  crc            : %d\n", !! (fi->flags & MPG123_CRC)) ;
+	psf_log_printf (psf, "  copyright flag : %d\n", !! (fi->flags & MPG123_COPYRIGHT)) ;
+	psf_log_printf (psf, "  private flag   : %d\n", !! (fi->flags & MPG123_PRIVATE)) ;
+	psf_log_printf (psf, "  original flag  : %d\n", !! (fi->flags & MPG123_ORIGINAL)) ;
+	psf_log_printf (psf, "  emphasis       : %d\n", fi->emphasis) ;
+	psf_log_printf (psf, "  bitrate mode   : ") ;
+	switch (fi->vbr)
+	{	case MPG123_CBR :
+			psf_log_printf (psf, "constant\n") ;
+			psf_log_printf (psf, "  bitrate        : %d kbps\n", fi->bitrate) ;
+			break ;
+		case MPG123_VBR :
+			psf_log_printf (psf, "variable\n") ;
+			break ;
+
+		case MPG123_ABR :
+			psf_log_printf (psf, "average\n") ;
+			psf_log_printf (psf, "  ABR target     : %d\n", fi->abr_rate) ;
+			break ;
+
+		default :
+			psf_log_printf (psf, "(%d) ???\n", fi->vbr) ;
+			break ;
+		} ;
+} /* mpeg_dec_print_frameinfo */
+
+/*
+ * Like strlcpy, except the size argument is the maximum size of the input,
+ * always null terminates the output string. Thus, up to size + 1 bytes may be
+ * written.
+ *
+ * Returns the length of the copied string.
+ */
+static int
+strcpy_inbounded (char *dest, size_t size, const char *src)
+{	char *c = memccpy (dest, src, '\0', size) ;
+	if (!c)
+		c = dest + size ;
+	*c = '\0' ;
+	return c - dest ;
+} /* strcpy_inbounded */
+
+static void
+mpeg_decoder_read_strings_id3v1 (SF_PRIVATE *psf, mpg123_id3v1 *tags)
+{	const char *genre ;
+	char buf [31] ;
+
+	psf_log_printf (psf, "ID3v1 Tags\n") ;
+
+	if (strcpy_inbounded (buf, ARRAY_LEN (tags->title), tags->title))
+	{	psf_log_printf (psf, "  Title       : %s\n", buf) ;
+		psf_store_string (psf, SF_STR_TITLE, buf) ;
+		} ;
+
+	if (strcpy_inbounded (buf, ARRAY_LEN (tags->artist), tags->artist))
+	{	psf_log_printf (psf, "  Artist      : %s\n", buf) ;
+		psf_store_string (psf, SF_STR_ARTIST, buf) ;
+		} ;
+
+	if (strcpy_inbounded (buf, ARRAY_LEN (tags->album), tags->album))
+	{	psf_log_printf (psf, "  Album       : %s\n", buf) ;
+		psf_store_string (psf, SF_STR_ALBUM, buf) ;
+		} ;
+
+	if (strcpy_inbounded (buf, ARRAY_LEN (tags->year), tags->year))
+	{	psf_log_printf (psf, "  Year        : %s\n", buf) ;
+		psf_store_string (psf, SF_STR_DATE, buf) ;
+		} ;
+
+	if (strcpy_inbounded (buf, ARRAY_LEN (tags->comment), tags->comment))
+	{	psf_log_printf (psf, "  Comment     : %s\n", buf) ;
+		psf_store_string (psf, SF_STR_COMMENT, buf) ;
+		} ;
+
+	/* ID3v1.1 Tracknumber */
+	if (tags->comment [28] == '\0' && tags->comment [29] != '\0')
+	{	snprintf (buf, ARRAY_LEN (buf), "%hhu", (unsigned char) tags->comment [29]) ;
+		psf_log_printf (psf, "  Tracknumber : %s\n", buf) ;
+		psf_store_string (psf, SF_STR_TRACKNUMBER, buf) ;
+		} ;
+
+	if ((genre = id3_lookup_v1_genre (tags->genre)) != NULL)
+	{	psf_log_printf (psf, "  Genre       : %s\n", genre) ;
+		psf_store_string (psf, SF_STR_GENRE, genre) ;
+		} ;
+} /* mpeg_decoder_read_strings_id3v1 */
+
+static void
+mpeg_decoder_read_strings_id3v2 (SF_PRIVATE *psf, mpg123_id3v2 *tags)
+{	mpg123_text *text_frame ;
+	size_t i ;
+	uint32_t marker ;
+	const char *title		= NULL ;
+	const char *copyright	= NULL ;
+	const char *software	= NULL ;
+	const char *artist		= NULL ;
+	const char *comment		= NULL ;
+	const char *date		= NULL ;
+	const char *album		= NULL ;
+	const char *license		= NULL ;
+	const char *tracknumber	= NULL ;
+	const char *genre		= NULL ;
+	const char *tlen		= NULL ;
+
+	psf_log_printf (psf, "ID3v2 Tags\n") ;
+
+	// Read the parsed text tags
+	for (i = 0 ; i < tags->texts ; i++)
+	{	text_frame = &tags->text [i] ;
+		psf_log_printf (psf, "  %.4s        : %s\n", text_frame->id, text_frame->text.p) ;
+
+		// Thankfully mpg123 translates v2.2 3-byte frames to v2.3 4-byte for us.
+		marker = MAKE_MARKER (text_frame->id [0], text_frame->id [1],
+			text_frame->id [2], text_frame->id [3]) ;
+
+		/* Use our own map of frame types to metadata for text frames */
+		switch (marker)
+		{	case MAKE_MARKER ('T', 'I', 'T', '2') :
+				title = text_frame->text.p ;
+				break ;
+
+			case MAKE_MARKER ('T', 'C', 'O', 'P') :
+				copyright = text_frame->text.p ;
+				break ;
+
+			case MAKE_MARKER ('T', 'E', 'N', 'C') :
+			case MAKE_MARKER ('T', 'S', 'S', 'E') :
+				software = text_frame->text.p ;
+				break ;
+
+			case MAKE_MARKER ('T', 'P', 'E', '1') :
+				artist = text_frame->text.p ;
+				break ;
+
+			case MAKE_MARKER ('T', 'A', 'L', 'B') :
+				album = text_frame->text.p ;
+				break ;
+
+			case MAKE_MARKER ('T', 'R', 'C', 'K') :
+				tracknumber = text_frame->text.p ;
+				break ;
+
+			case MAKE_MARKER ('T', 'Y', 'E', 'R') :
+			case MAKE_MARKER ('T', 'D', 'R', 'C') :
+			/* TODO (maybe)
+			case MAKE_MARKER ('T', 'D', 'A', 'T') :
+			case MAKE_MARKER ('T', 'I', 'M', 'E') :
+			case MAKE_MARKER ('T', 'D', 'R', 'A') :
+			*/
+				date = text_frame->text.p ;
+				break ;
+
+			case MAKE_MARKER ('T', 'O', 'W', 'N') :
+				tracknumber = text_frame->text.p ;
+				break ;
+
+			case MAKE_MARKER ('T', 'C', 'O', 'N') :
+				genre = text_frame->text.p ;
+				break ;
+
+			case MAKE_MARKER ('T', 'L', 'E', 'N') :
+				tlen = text_frame->text.p ;
+				break ;
+			} ;
+		} ;
+
+	/* Use mpg123's handling of comment headers, but print all the comment headers anyways. */
+	if (tags->comment)
+		comment = tags->comment->p ;
+	for (i = 0 ; i < tags->comments ; i++)
+	{	text_frame = &tags->comment_list [i] ;
+		psf_log_printf (psf, "  %.4s        : (%s)[%s] %s\n", text_frame->id,
+			text_frame->description. p, text_frame->lang, text_frame->text.p) ;
+		} ;
+
+	/* Print extra headers */
+	for (i = 0 ; i < tags->extras ; i++)
+	{	text_frame = &tags->extra [i] ;
+		psf_log_printf (psf, "  %.4s        : (%s) %s\n", text_frame->id,
+			text_frame->description.p, text_frame->text.p) ;
+		} ;
+
+	if (title != NULL)
+		psf_store_string (psf, SF_STR_TITLE, title) ;
+	if (copyright != NULL)
+		psf_store_string (psf, SF_STR_COPYRIGHT, copyright) ;
+	if (software != NULL)
+		psf_store_string (psf, SF_STR_SOFTWARE, software) ;
+	if (artist != NULL)
+		psf_store_string (psf, SF_STR_ARTIST, artist) ;
+	if (comment != NULL)
+		psf_store_string (psf, SF_STR_COMMENT, comment) ;
+	if (date != NULL)
+		psf_store_string (psf, SF_STR_DATE, date) ;
+	if (album != NULL)
+		psf_store_string (psf, SF_STR_ALBUM, album) ;
+	if (license != NULL)
+		psf_store_string (psf, SF_STR_LICENSE, license) ;
+	if (tracknumber != NULL)
+		psf_store_string (psf, SF_STR_TRACKNUMBER, tracknumber) ;
+	if (genre != NULL)
+		psf_store_string (psf, SF_STR_GENRE, id3_process_v2_genre (genre)) ;
+	if (tlen != NULL)
+	{	/* If non-seekable, set framecount? Can we trust it? */
+		} ;
+} /* mpeg_decoder_read_strings_id3v2 */
+
+static void
+mpeg_decoder_read_strings (SF_PRIVATE *psf)
+{	MPEG_DEC_PRIVATE *pmp3d = (MPEG_DEC_PRIVATE *) psf->codec_data ;
+	mpg123_id3v1 *v1_tags ;
+	mpg123_id3v2 *v2_tags ;
+
+	if (mpg123_id3 (pmp3d->pmh, &v1_tags, &v2_tags) != MPG123_OK)
+		return ;
+
+	if (v1_tags != NULL)
+		mpeg_decoder_read_strings_id3v1 (psf, v1_tags) ;
+
+	if (v2_tags != NULL)
+		mpeg_decoder_read_strings_id3v2 (psf, v2_tags) ;
+} /* mpeg_decoder_read_strings */
+
+static int
+mpeg_dec_byterate (SF_PRIVATE *psf)
+{	MPEG_DEC_PRIVATE *pmp3d = (MPEG_DEC_PRIVATE *) psf->codec_data ;
+	struct mpg123_frameinfo fi ;
+
+	if (mpg123_info (pmp3d->pmh, &fi) == MPG123_OK)
+		return (fi.bitrate + 7) / 8 ;
+
+	return -1 ;
+
+} /* mpeg_dec_byterate */
+
+/*==============================================================================
+** exported functions
+*/
+
+int
+mpeg_decoder_init (SF_PRIVATE *psf)
+{	MPEG_DEC_PRIVATE *pmp3d ;
+	struct mpg123_frameinfo fi ;
+	int error ;
+
+	if (! (psf->file.mode & SFM_READ))
+		return SFE_INTERNAL ;
+
+	/*
+	** *** FIXME - Threading issues ***
+	**
+	** mpg123_init() is a global call that should only be called once, and
+	** should be paried with mpg123_exit() when done. libsndfile does not
+	** provide for these requirements.
+	**
+	** Currently this is a moot issue as mpg123_init() non-conditionally writes
+	** static areas with calculated data, and mpg123_exit() is a NOP, but this
+	** could change in a future version of it!
+	**
+	** From mpg123.h:
+	** > This should be called once in a non-parallel context. It is not explicitly
+	** > thread-safe, but repeated/concurrent calls still _should_ be safe as static
+	** > tables are filled with the same values anyway.
+	**
+	** Note that calling mpg123_init() after it has already completed is a NOP.
+	**
+	** Update 2021-07-04
+	** mpg123 upstream has confirmed that mpg132_init() will become a NOP in future,
+	** so this is moot.
+	*/
+	if (mpg123_init () != MPG123_OK)
+		return SFE_INTERNAL ;
+
+	psf->codec_data = pmp3d = calloc (1, sizeof (MPEG_DEC_PRIVATE)) ;
+	if (!psf->codec_data)
+		return SFE_MALLOC_FAILED ;
+
+	pmp3d->pmh = mpg123_new (NULL, &error) ;
+	if (!pmp3d->pmh)
+	{ psf_log_printf (psf, "Could not obtain a mpg123 handle: %s\n", mpg123_plain_strerror (error)) ;
+		return SFE_INTERNAL ;
+		} ;
+
+	psf->codec_close = mpeg_dec_close ;
+
+	mpg123_replace_reader_handle (pmp3d->pmh,
+		mpeg_dec_io_read, mpeg_dec_io_lseek, NULL) ;
+
+	mpg123_param (pmp3d->pmh, MPG123_REMOVE_FLAGS, MPG123_AUTO_RESAMPLE, 1.0) ;
+	mpg123_param (pmp3d->pmh, MPG123_ADD_FLAGS, MPG123_FORCE_FLOAT | MPG123_GAPLESS, 1.0) ;
+#if MPG123_API_VERSION >= 45
+	mpg123_param (pmp3d->pmh, MPG123_ADD_FLAGS, MPG123_NO_FRANKENSTEIN, 1.0) ;
+#endif
+
+	/*
+	** Need to pass the first MPEG frame to libmpg123, but that frame was read
+	** into psf->binheader in order that we could identify the stream.
+	*/
+	if (psf->is_pipe)
+	{	/*
+		** Can't seek, so setup our libmpg123 io callbacks to read the binheader
+		** buffer first.
+		*/
+		psf_binheader_readf (psf, "p", psf->dataoffset) ;
+		pmp3d->header_remaining = psf_binheader_readf (psf, NULL) - psf->dataoffset ;
+
+		/* Tell libmpg123 we can't seek the file. */
+		mpg123_param (pmp3d->pmh, MPG123_ADD_FLAGS, MPG123_NO_PEEK_END, 1.0) ;
+		}
+	else
+	{	/*
+		** libmpg123 can parse the ID3v2 header. Undo the embedded file offset if the
+		** enclosing file data is the ID3v2 header.
+		*/
+		if (psf->id3_header.len > 0 && psf->id3_header.len + psf->id3_header.offset == psf->fileoffset)
+			psf->fileoffset = psf->id3_header.offset ;
+
+		psf_fseek (psf, 0, SEEK_SET) ;
+		} ;
+
+	error = mpg123_open_handle (pmp3d->pmh, psf) ;
+	if (error != MPG123_OK)
+	{	psf_log_printf (psf, "mpg123 could not open the file: %s\n", mpg123_plain_strerror (error)) ;
+		return SFE_BAD_FILE ;
+		} ;
+
+	if (mpeg_dec_fill_sfinfo (psf, pmp3d->pmh, &psf->sf) != MPG123_OK)
+	{	psf_log_printf (psf, "Cannot get MPEG decoder configuration: %s\n", mpg123_plain_strerror (error)) ;
+		return SFE_BAD_FILE ;
+		} ;
+
+	error = mpg123_info (pmp3d->pmh, &fi) ;
+	if (error != MPG123_OK)
+	{	psf_log_printf (psf, "Cannot get MPEG frame info: %s\n", mpg123_plain_strerror (error)) ;
+		return SFE_INTERNAL ;
+		}
+
+	switch (fi.layer)
+	{	case 1 : psf->sf.format |= SF_FORMAT_MPEG_LAYER_I ; break ;
+		case 2 : psf->sf.format |= SF_FORMAT_MPEG_LAYER_II ; break ;
+		case 3 : psf->sf.format |= SF_FORMAT_MPEG_LAYER_III ; break ;
+		default :
+			return SFE_BAD_FILE ;
+		} ;
+	mpeg_dec_print_frameinfo (psf, &fi) ;
+
+	psf->read_short = mpeg_dec_read_s ;
+	psf->read_int	= mpeg_dec_read_i ;
+	psf->read_float	= mpeg_dec_read_f ;
+	psf->read_double = mpeg_dec_read_d ;
+	psf->seek		= mpeg_dec_seek ;
+	psf->byterate	= mpeg_dec_byterate ;
+
+	mpeg_decoder_read_strings (psf) ;
+
+	return 0 ;
+} /* mpeg_decoder_init */
+
+int
+mpeg_decoder_get_bitrate_mode (SF_PRIVATE *psf)
+{	MPEG_DEC_PRIVATE *pmp3d = (MPEG_DEC_PRIVATE *) psf->codec_data ;
+	struct mpg123_frameinfo fi ;
+
+	if (mpg123_info (pmp3d->pmh, &fi) == MPG123_OK)
+	{
+		switch (fi.vbr)
+		{	case MPG123_CBR : return SF_BITRATE_MODE_CONSTANT ;
+			case MPG123_ABR : return SF_BITRATE_MODE_AVERAGE ;
+			case MPG123_VBR : return SF_BITRATE_MODE_VARIABLE ;
+			default : break ;
+			} ;
+		} ;
+
+	psf_log_printf (psf, "Cannot determine MPEG bitrate mode.\n") ;
+	return -1 ;
+} /* mpeg_decoder_get_bitrate_mode */
+
+#else /* HAVE_MPEG */
+
+int mpeg_decoder_init (SF_PRIVATE *psf)
+{	psf_log_printf (psf, "This version of libsndfile was compiled without MPEG decode support.\n") ;
+	return SFE_UNIMPLEMENTED ;
+} /* mpeg_decoder_init */
+
+#endif /* HAVE_MPEG */
diff --git a/src/mpeg_l3_encode.c b/src/mpeg_l3_encode.c
new file mode 100644
index 0000000..97324f7
--- /dev/null
+++ b/src/mpeg_l3_encode.c
@@ -0,0 +1,784 @@
+/*
+** Copyright (C) 2020 Arthur Taylor <art@ified.ca>
+** Copyright (C) 2019 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software ; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation ; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program ; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+#include	"sndfile.h"
+#include	"common.h"
+#include	"mpeg.h"
+
+
+#if HAVE_MPEG
+
+#include <lame/lame.h>
+
+/*
+ * RANT RANT RANT
+ *
+ * Lame has 11 functions for inputing sample data of various types and
+ * configurations, but due to bad definitions, or missing combinations, they
+ * aren't really of much help to us.
+ *
+ */
+
+typedef struct
+{	lame_t lamef ;
+	unsigned char *block ;
+	size_t block_len ;
+	int frame_samples ;
+	double compression ;
+	int initialized ;
+} MPEG_L3_ENC_PRIVATE ;
+
+
+/*-----------------------------------------------------------------------------------------------
+** Private function prototypes.
+*/
+
+static int mpeg_l3_encoder_close (SF_PRIVATE *psf) ;
+static int mpeg_l3_encoder_construct (SF_PRIVATE *psf) ;
+static int mpeg_l3_encoder_byterate (SF_PRIVATE *psf) ;
+
+static sf_count_t mpeg_l3_encode_write_short_stereo (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
+static sf_count_t mpeg_l3_encode_write_int_stereo (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
+static sf_count_t mpeg_l3_encode_write_float_stereo (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
+static sf_count_t mpeg_l3_encode_write_double_stereo (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
+static sf_count_t mpeg_l3_encode_write_short_mono (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
+static sf_count_t mpeg_l3_encode_write_int_mono (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
+static sf_count_t mpeg_l3_encode_write_float_mono (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
+static sf_count_t mpeg_l3_encode_write_double_mono (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
+
+/*-----------------------------------------------------------------------------------------------
+** Exported functions.
+*/
+
+int
+mpeg_l3_encoder_init (SF_PRIVATE *psf, int info_tag)
+{	MPEG_L3_ENC_PRIVATE* pmpeg = NULL ;
+
+	if (psf->file.mode == SFM_RDWR)
+		return SFE_BAD_MODE_RW ;
+
+	if (psf->file.mode != SFM_WRITE)
+		return SFE_INTERNAL ;
+
+	psf->codec_data = pmpeg = calloc (1, sizeof (MPEG_L3_ENC_PRIVATE)) ;
+	if (!pmpeg)
+		return SFE_MALLOC_FAILED ;
+
+	if (psf->sf.channels < 1 || psf->sf.channels > 2)
+		return SFE_BAD_OPEN_FORMAT ;
+
+	if (! (pmpeg->lamef = lame_init ()))
+		return SFE_MALLOC_FAILED ;
+
+	pmpeg->compression = -1.0 ; /* Unset */
+
+	lame_set_in_samplerate (pmpeg->lamef, psf->sf.samplerate) ;
+	lame_set_num_channels (pmpeg->lamef, psf->sf.channels) ;
+	if (lame_set_out_samplerate (pmpeg->lamef, psf->sf.samplerate) < 0)
+		return SFE_MPEG_BAD_SAMPLERATE ;
+
+	lame_set_write_id3tag_automatic (pmpeg->lamef, 0) ;
+
+	if (!info_tag || psf->is_pipe)
+	{	/* Can't seek back, so force disable Xing/Lame/Info header. */
+		lame_set_bWriteVbrTag (pmpeg->lamef, 0) ;
+		} ;
+
+	if (psf->sf.channels == 2)
+	{	psf->write_short	= mpeg_l3_encode_write_short_stereo ;
+		psf->write_int		= mpeg_l3_encode_write_int_stereo ;
+		psf->write_float	= mpeg_l3_encode_write_float_stereo ;
+		psf->write_double	= mpeg_l3_encode_write_double_stereo ;
+		}
+	else
+	{	psf->write_short	= mpeg_l3_encode_write_short_mono ;
+		psf->write_int		= mpeg_l3_encode_write_int_mono ;
+		psf->write_float	= mpeg_l3_encode_write_float_mono ;
+		psf->write_double	= mpeg_l3_encode_write_double_mono ;
+		}
+
+	psf->sf.seekable	= 0 ;
+	psf->codec_close	= mpeg_l3_encoder_close ;
+	psf->byterate		= mpeg_l3_encoder_byterate ;
+	psf->datalength		= 0 ;
+
+	return 0 ;
+} /* mpeg_l3_encoder_init */
+
+int
+mpeg_l3_encoder_write_id3tag (SF_PRIVATE *psf)
+{	MPEG_L3_ENC_PRIVATE *pmpeg = (MPEG_L3_ENC_PRIVATE *) psf->codec_data ;
+	unsigned char *id3v2_buffer ;
+	int i, id3v2_size ;
+
+	if (psf->have_written)
+		return 0 ;
+
+	if ((i = mpeg_l3_encoder_construct (psf)))
+		return i ;
+
+	if (psf_fseek (psf, 0, SEEK_SET) != 0)
+		return SFE_NOT_SEEKABLE ;
+
+	/* Safe to call multiple times. */
+	id3tag_init (pmpeg->lamef) ;
+
+	for (i = 0 ; i < SF_MAX_STRINGS ; i++)
+	{	switch (psf->strings.data [i].type)
+		{	case SF_STR_TITLE :
+				id3tag_set_title (pmpeg->lamef, psf->strings.storage + psf->strings.data [i].offset) ;
+				break ;
+
+			case SF_STR_ARTIST :
+				id3tag_set_artist (pmpeg->lamef, psf->strings.storage + psf->strings.data [i].offset) ;
+				break ;
+
+			case SF_STR_ALBUM :
+				id3tag_set_album (pmpeg->lamef, psf->strings.storage + psf->strings.data [i].offset) ;
+				break ;
+
+			case SF_STR_DATE :
+				id3tag_set_year (pmpeg->lamef, psf->strings.storage + psf->strings.data [i].offset) ;
+				break ;
+
+			case SF_STR_COMMENT :
+				id3tag_set_comment (pmpeg->lamef, psf->strings.storage + psf->strings.data [i].offset) ;
+				break ;
+
+			case SF_STR_GENRE :
+				id3tag_set_genre (pmpeg->lamef, psf->strings.storage + psf->strings.data [i].offset) ;
+				break ;
+
+			case SF_STR_TRACKNUMBER :
+				id3tag_set_track (pmpeg->lamef, psf->strings.storage + psf->strings.data [i].offset) ;
+				break ;
+
+			default:
+				break ;
+			} ;
+		} ;
+
+	/* The header in this case is the ID3v2 tag header. */
+	id3v2_size = lame_get_id3v2_tag (pmpeg->lamef, 0, 0) ;
+	if (id3v2_size > 0)
+	{	psf_log_printf (psf, "Writing ID3v2 header.\n") ;
+		if (! (id3v2_buffer = malloc (id3v2_size)))
+			return SFE_MALLOC_FAILED ;
+		lame_get_id3v2_tag (pmpeg->lamef, id3v2_buffer, id3v2_size) ;
+		psf_fwrite (id3v2_buffer, 1, id3v2_size, psf) ;
+		psf->dataoffset = id3v2_size ;
+		free (id3v2_buffer) ;
+		} ;
+
+	return 0 ;
+}
+
+int
+mpeg_l3_encoder_set_quality (SF_PRIVATE *psf, double compression)
+{	MPEG_L3_ENC_PRIVATE *pmpeg = (MPEG_L3_ENC_PRIVATE *) psf->codec_data ;
+	int bitrate_mode ;
+	int bitrate ;
+	int ret ;
+
+	if (compression < 0.0 || compression > 1.0)
+		return SF_FALSE ;
+
+	/*
+	** Save the compression setting, as we may have to re-interpret it if
+	** the bitrate mode changes.
+	*/
+	pmpeg->compression = compression ;
+
+	bitrate_mode = mpeg_l3_encoder_get_bitrate_mode (psf) ;
+	if (bitrate_mode == SF_BITRATE_MODE_VARIABLE)
+	{	ret = lame_set_VBR_quality (pmpeg->lamef, compression * 10.0) ;
+		}
+	else
+	{	/* Choose a bitrate. */
+		if (psf->sf.samplerate >= 32000)
+		{	/* MPEG-1.0, bitrates are [32,320] kbps */
+			bitrate = (320.0 - (compression * (320.0 - 32.0))) ;
+			}
+		else if (psf->sf.samplerate >= 16000)
+		{	/* MPEG-2.0, bitrates are [8,160] */
+			bitrate = (160.0 - (compression * (160.0 - 8.0))) ;
+			}
+		else
+		{	/* MPEG-2.5, bitrates are [8,64] */
+			bitrate = (64.0 - (compression * (64.0 - 8.0))) ;
+			}
+
+		if (bitrate_mode == SF_BITRATE_MODE_AVERAGE)
+			ret = lame_set_VBR_mean_bitrate_kbps (pmpeg->lamef, bitrate) ;
+		else
+			ret = lame_set_brate (pmpeg->lamef, bitrate) ;
+		} ;
+
+	if (ret == LAME_OKAY)
+		return SF_TRUE ;
+
+	psf_log_printf (psf, "Failed to set lame encoder quality.\n") ;
+	return SF_FALSE ;
+} /* mpeg_l3_encoder_set_quality */
+
+int
+mpeg_l3_encoder_set_bitrate_mode (SF_PRIVATE *psf, int mode)
+{	MPEG_L3_ENC_PRIVATE *pmpeg = (MPEG_L3_ENC_PRIVATE *) psf->codec_data ;
+	enum vbr_mode_e vbr_mode ;
+
+	if (pmpeg->initialized)
+	{	psf->error = SFE_CMD_HAS_DATA ;
+		return SF_FALSE ;
+		} ;
+
+	switch (mode)
+	{	case SF_BITRATE_MODE_CONSTANT :	vbr_mode = vbr_off ; break ;
+		case SF_BITRATE_MODE_AVERAGE : vbr_mode = vbr_abr ; break ;
+		case SF_BITRATE_MODE_VARIABLE : vbr_mode = vbr_default ; break ;
+		default :
+			psf->error = SFE_BAD_COMMAND_PARAM ;
+			return SF_FALSE ;
+		} ;
+
+	if (lame_set_VBR (pmpeg->lamef, vbr_mode) == LAME_OKAY)
+	{	/* Re-evaluate the compression setting. */
+		return mpeg_l3_encoder_set_quality (psf, pmpeg->compression) ;
+		} ;
+
+	psf_log_printf (psf, "Failed to set LAME vbr mode to %d.\n", vbr_mode) ;
+	return SF_FALSE ;
+} /* mpeg_l3_encoder_set_bitrate_mode */
+
+int
+mpeg_l3_encoder_get_bitrate_mode (SF_PRIVATE *psf)
+{	MPEG_L3_ENC_PRIVATE *pmpeg = (MPEG_L3_ENC_PRIVATE *) psf->codec_data ;
+	enum vbr_mode_e vbr_mode ;
+
+	vbr_mode = lame_get_VBR (pmpeg->lamef) ;
+
+	if (vbr_mode == vbr_off)
+		return SF_BITRATE_MODE_CONSTANT ;
+	if (vbr_mode == vbr_abr)
+		return SF_BITRATE_MODE_AVERAGE ;
+	if (vbr_mode == vbr_default || vbr_mode < vbr_max_indicator)
+		return SF_BITRATE_MODE_VARIABLE ;
+
+	/* Something is wrong. */
+	psf->error = SFE_INTERNAL ;
+	return -1 ;
+} /* mpeg_l3_encoder_get_bitrate_mode */
+
+
+/*-----------------------------------------------------------------------------------------------
+** Private functions.
+*/
+
+static int
+mpeg_l3_encoder_close (SF_PRIVATE *psf)
+{	MPEG_L3_ENC_PRIVATE* pmpeg = (MPEG_L3_ENC_PRIVATE *) psf->codec_data ;
+	int ret, len ;
+	sf_count_t pos ;
+	unsigned char *buffer ;
+
+	/* Magic number 7200 comes from a comment in lame.h */
+	len = 7200 ;
+	if (! (buffer = malloc (len)))
+		return SFE_MALLOC_FAILED ;
+	ret = lame_encode_flush (pmpeg->lamef, buffer, len) ;
+	if (ret > 0)
+		psf_fwrite (buffer, 1, ret, psf) ;
+
+	/*
+	** Write an IDv1 trailer. The whole tag structure is always 128 bytes, so is
+	** guaranteed to fit in the buffer allocated above.
+	*/
+	ret = lame_get_id3v1_tag (pmpeg->lamef, buffer, len) ;
+	if (ret > 0)
+	{	psf_log_printf (psf, "  Writing ID3v1 trailer.\n") ;
+		psf_fwrite (buffer, 1, ret, psf) ;
+		} ;
+
+	/*
+	** If possible, seek back and write the LAME/XING/Info headers. This
+	** contains information about the whole file and a seek table, and can
+	** only be written after encoding.
+	**
+	** If enabled, Lame wrote an empty header at the beginning of the data
+	** that we now fill in.
+	*/
+	ret = lame_get_lametag_frame (pmpeg->lamef, 0, 0) ;
+	if (ret > 0)
+	{	if (ret > len)
+		{	len = ret ;
+			free (buffer) ;
+			if (! (buffer = malloc (len)))
+				return SFE_MALLOC_FAILED ;
+			} ;
+		psf_log_printf (psf, "  Writing LAME info header at offset %d, %d bytes.\n",
+			psf->dataoffset, len) ;
+		lame_get_lametag_frame (pmpeg->lamef, buffer, len) ;
+		pos = psf_ftell (psf) ;
+		if (psf_fseek (psf, psf->dataoffset, SEEK_SET) == psf->dataoffset)
+		{	psf_fwrite (buffer, 1, ret, psf) ;
+			psf_fseek (psf, pos, SEEK_SET) ;
+			} ;
+		} ;
+	free (buffer) ;
+
+	free (pmpeg->block) ;
+	pmpeg->block = NULL ;
+
+	if (pmpeg->lamef)
+	{	lame_close (pmpeg->lamef) ;
+		pmpeg->lamef = NULL ;
+		} ;
+
+	return 0 ;
+} /* mpeg_l3_encoder_close */
+
+static void
+mpeg_l3_encoder_log_config (SF_PRIVATE *psf, lame_t lamef)
+{	const char *version ;
+	const char *chn_mode ;
+
+	switch (lame_get_version (lamef))
+	{	case 0 : version = "2" ; break ;
+		case 1 : version = "1" ; break ;
+		case 2 : version = "2.5" ; break ;
+		default : version = "unknown!?" ; break ;
+		} ;
+	switch (lame_get_mode (lamef))
+	{	case STEREO : chn_mode = "stereo" ; break ;
+		case JOINT_STEREO : chn_mode = "joint-stereo" ; break ;
+		case MONO : chn_mode = "mono" ; break ;
+		default : chn_mode = "unknown!?" ; break ;
+		} ;
+	psf_log_printf (psf, "  MPEG Version      : %s\n", version) ;
+	psf_log_printf (psf, "  Block samples     : %d\n", lame_get_framesize (lamef)) ;
+	psf_log_printf (psf, "  Channel mode      : %s\n", chn_mode) ;
+	psf_log_printf (psf, "  Samplerate        : %d\n", lame_get_out_samplerate (lamef)) ;
+	psf_log_printf (psf, "  Encoder mode      : ") ;
+	switch (lame_get_VBR (lamef))
+	{	case vbr_off :
+			psf_log_printf (psf, "CBR\n") ;
+			psf_log_printf (psf, "  Bitrate           : %d kbps\n", lame_get_brate (lamef)) ;
+			break ;
+		case vbr_abr :
+			psf_log_printf (psf, "ABR\n") ;
+			psf_log_printf (psf, "  Mean Bitrate      : %d kbps\n", lame_get_VBR_mean_bitrate_kbps (lamef)) ;
+			break ;
+
+		case vbr_mt :
+		case vbr_default :
+			psf_log_printf (psf, "VBR\n") ;
+			psf_log_printf (psf, "  Quality           : %d\n", lame_get_VBR_q (lamef)) ;
+			break ;
+
+		default:
+			psf_log_printf (psf, "Unknown!? (%d)\n", lame_get_VBR (lamef)) ;
+			break ;
+		} ;
+
+	psf_log_printf (psf, "  Encoder delay     : %d\n", lame_get_encoder_delay (lamef)) ;
+	psf_log_printf (psf, "  Write INFO header : %d\n", lame_get_bWriteVbrTag (lamef)) ;
+} /* mpeg_l3_encoder_log_config */
+
+static int
+mpeg_l3_encoder_construct (SF_PRIVATE *psf)
+{	MPEG_L3_ENC_PRIVATE *pmpeg = (MPEG_L3_ENC_PRIVATE *) psf->codec_data ;
+	int frame_samples_per_channel ;
+
+	if (pmpeg->initialized == SF_FALSE)
+	{	if (lame_init_params (pmpeg->lamef) < 0)
+		{	psf_log_printf (psf, "Failed to initialize lame encoder!\n") ;
+			return SFE_INTERNAL ;
+			} ;
+
+		psf_log_printf (psf, "Initialized LAME encoder.\n") ;
+		mpeg_l3_encoder_log_config (psf, pmpeg->lamef) ;
+
+		frame_samples_per_channel = lame_get_framesize (pmpeg->lamef) ;
+
+		/*
+		 * Suggested output buffer size in bytes from lame.h comment is
+		 * 1.25 * samples + 7200
+		 */
+		pmpeg->block_len = (frame_samples_per_channel * 4) / 3 + 7200 ;
+		pmpeg->frame_samples = frame_samples_per_channel * psf->sf.channels ;
+
+		pmpeg->block = malloc (pmpeg->block_len) ;
+		if (!pmpeg->block)
+			return SFE_MALLOC_FAILED ;
+
+		pmpeg->initialized = SF_TRUE ;
+		} ;
+
+	return 0 ;
+} /* mpeg_l3_encoder_construct */
+
+static int
+mpeg_l3_encoder_byterate (SF_PRIVATE *psf)
+{	MPEG_L3_ENC_PRIVATE *pmpeg = (MPEG_L3_ENC_PRIVATE *) psf->codec_data ;
+	int bitrate_mode ;
+	int byterate ;
+	float calculated_byterate ;
+
+	bitrate_mode = mpeg_l3_encoder_get_bitrate_mode (psf) ;
+	byterate = (lame_get_brate (pmpeg->lamef) + 7) / 8 ;
+
+	if (bitrate_mode == SF_BITRATE_MODE_VARIABLE)
+	{	/*
+		** For VBR, lame_get_brate returns the minimum bitrate, so calculate the
+		** average byterate so far.
+		*/
+		calculated_byterate = psf_ftell (psf) - psf->dataoffset ;
+		calculated_byterate /= (float) psf->write_current ;
+		calculated_byterate *= (float) psf->sf.samplerate ;
+
+		return SF_MIN (byterate, (int) calculated_byterate) ;
+	}
+
+	return byterate ;
+} /* mpeg_l3_encoder_byterate */
+
+static sf_count_t
+mpeg_l3_encode_write_short_mono (SF_PRIVATE *psf, const short *ptr, sf_count_t len)
+{	MPEG_L3_ENC_PRIVATE *pmpeg = (MPEG_L3_ENC_PRIVATE*) psf->codec_data ;
+	sf_count_t total = 0 ;
+	int nbytes, writecount, writen ;
+
+	if ((psf->error = mpeg_l3_encoder_construct (psf)))
+		return 0 ;
+
+	while (len)
+	{	writecount = SF_MIN (len, (sf_count_t) pmpeg->frame_samples) ;
+
+		nbytes = lame_encode_buffer (pmpeg->lamef, ptr + total, NULL, writecount, pmpeg->block, pmpeg->block_len) ;
+		if (nbytes < 0)
+		{	psf_log_printf (psf, "lame_encode_buffer returned %d\n", nbytes) ;
+			break ;
+			} ;
+
+		if (nbytes)
+		{	writen = psf_fwrite (pmpeg->block, 1, nbytes, psf) ;
+			if (writen != nbytes)
+			{	psf_log_printf (psf, "*** Warning : short write (%d != %d).\n", writen, nbytes) ;
+				} ;
+			} ;
+
+		total += writecount ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+}
+
+
+static sf_count_t
+mpeg_l3_encode_write_short_stereo (SF_PRIVATE *psf, const short *ptr, sf_count_t len)
+{	BUF_UNION ubuf ;
+	MPEG_L3_ENC_PRIVATE *pmpeg = (MPEG_L3_ENC_PRIVATE*) psf->codec_data ;
+	sf_count_t total = 0 ;
+	int nbytes, writecount, writen ;
+
+	if ((psf->error = mpeg_l3_encoder_construct (psf)))
+		return 0 ;
+
+	const sf_count_t max_samples = SF_MIN (ARRAY_LEN (ubuf.sbuf), pmpeg->frame_samples) ;
+	while (len)
+	{	writecount = SF_MIN (len, max_samples) ;
+		/*
+		 * An oversight, but lame_encode_buffer_interleaved() lacks a const.
+		 * As such, need another memcpy to not cause a warning.
+		 */
+		memcpy (ubuf.sbuf, ptr + total, writecount) ;
+		nbytes = lame_encode_buffer_interleaved (pmpeg->lamef, ubuf.sbuf, writecount / 2, pmpeg->block, pmpeg->block_len) ;
+		if (nbytes < 0)
+		{	psf_log_printf (psf, "lame_encode_buffer returned %d\n", nbytes) ;
+			break ;
+			} ;
+
+		if (nbytes)
+		{	writen = psf_fwrite (pmpeg->block, 1, nbytes, psf) ;
+			if (writen != nbytes)
+			{	psf_log_printf (psf, "*** Warning : short write (%d != %d).\n", writen, nbytes) ;
+				} ;
+			} ;
+
+		total += writecount ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+}
+
+
+static sf_count_t
+mpeg_l3_encode_write_int_mono (SF_PRIVATE *psf, const int *ptr, sf_count_t len)
+{	MPEG_L3_ENC_PRIVATE *pmpeg = (MPEG_L3_ENC_PRIVATE*) psf->codec_data ;
+	sf_count_t total = 0 ;
+	int nbytes, writecount, writen ;
+
+	if ((psf->error = mpeg_l3_encoder_construct (psf)))
+		return 0 ;
+
+	while (len)
+	{	writecount = SF_MIN (len, (sf_count_t) pmpeg->frame_samples) ;
+
+		nbytes = lame_encode_buffer_int (pmpeg->lamef, ptr + total, NULL, writecount, pmpeg->block, pmpeg->block_len) ;
+		if (nbytes < 0)
+		{	psf_log_printf (psf, "lame_encode_buffer returned %d\n", nbytes) ;
+			break ;
+			} ;
+
+		if (nbytes)
+		{	writen = psf_fwrite (pmpeg->block, 1, nbytes, psf) ;
+			if (writen != nbytes)
+			{	psf_log_printf (psf, "*** Warning : short write (%d != %d).\n", writen, nbytes) ;
+				} ;
+			} ;
+
+		total += writecount ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+}
+
+
+static sf_count_t
+mpeg_l3_encode_write_int_stereo (SF_PRIVATE *psf, const int *ptr, sf_count_t len)
+{	MPEG_L3_ENC_PRIVATE *pmpeg = (MPEG_L3_ENC_PRIVATE*) psf->codec_data ;
+	sf_count_t total = 0 ;
+	int nbytes, writecount, writen ;
+
+	if ((psf->error = mpeg_l3_encoder_construct (psf)))
+		return 0 ;
+
+	while (len)
+	{	writecount = SF_MIN (len, (sf_count_t) pmpeg->frame_samples) ;
+
+		nbytes = lame_encode_buffer_interleaved_int (pmpeg->lamef, ptr + total, writecount / 2, pmpeg->block, pmpeg->block_len) ;
+		if (nbytes < 0)
+		{	psf_log_printf (psf, "lame_encode_buffer returned %d\n", nbytes) ;
+			break ;
+			} ;
+
+		if (nbytes)
+		{	writen = psf_fwrite (pmpeg->block, 1, nbytes, psf) ;
+			if (writen != nbytes)
+			{	psf_log_printf (psf, "*** Warning : short write (%d != %d).\n", writen, nbytes) ;
+				} ;
+			} ;
+
+		total += writecount ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+}
+
+
+static sf_count_t
+mpeg_l3_encode_write_float_mono (SF_PRIVATE *psf, const float *ptr, sf_count_t len)
+{	MPEG_L3_ENC_PRIVATE *pmpeg = (MPEG_L3_ENC_PRIVATE*) psf->codec_data ;
+	sf_count_t total = 0 ;
+	int nbytes, writecount, writen ;
+
+	if ((psf->error = mpeg_l3_encoder_construct (psf)))
+		return 0 ;
+
+	while (len)
+	{	writecount = SF_MIN (len, (sf_count_t) pmpeg->frame_samples) ;
+
+		if (psf->norm_float)
+			nbytes = lame_encode_buffer_ieee_float (pmpeg->lamef, ptr + total, NULL, writecount, pmpeg->block, pmpeg->block_len) ;
+		else
+			nbytes = lame_encode_buffer_float (pmpeg->lamef, ptr + total, NULL, writecount, pmpeg->block, pmpeg->block_len) ;
+		if (nbytes < 0)
+		{	psf_log_printf (psf, "lame_encode_buffer returned %d\n", nbytes) ;
+			break ;
+			} ;
+
+		if (nbytes)
+		{	writen = psf_fwrite (pmpeg->block, 1, nbytes, psf) ;
+			if (writen != nbytes)
+			{	psf_log_printf (psf, "*** Warning : short write (%d != %d).\n", writen, nbytes) ;
+				} ;
+			} ;
+
+		total += writecount ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+}
+
+
+static inline void
+normalize_float (float *dest, const float *src, sf_count_t count, float norm_fact)
+{	while (--count >= 0)
+	{	dest [count] = src [count] * norm_fact ;
+		} ;
+}
+
+
+static sf_count_t
+mpeg_l3_encode_write_float_stereo (SF_PRIVATE *psf, const float *ptr, sf_count_t len)
+{	BUF_UNION ubuf ;
+	MPEG_L3_ENC_PRIVATE *pmpeg = (MPEG_L3_ENC_PRIVATE*) psf->codec_data ;
+	sf_count_t total = 0 ;
+	int nbytes, writecount, writen ;
+
+	if ((psf->error = mpeg_l3_encoder_construct (psf)))
+		return 0 ;
+
+	const sf_count_t max_samples = SF_MIN (ARRAY_LEN (ubuf.fbuf), pmpeg->frame_samples) ;
+	while (len)
+	{	writecount = SF_MIN (len, max_samples) ;
+
+		if (psf->norm_float)
+			nbytes = lame_encode_buffer_interleaved_ieee_float (pmpeg->lamef, ptr + total, writecount / 2, pmpeg->block, pmpeg->block_len) ;
+		else
+		{	/* Lame lacks a non-normalized interleaved float write. Bummer. */
+			normalize_float (ubuf.fbuf, ptr + total, writecount, 1.0 / (float) 0x8000) ;
+			nbytes = lame_encode_buffer_interleaved_ieee_float (pmpeg->lamef, ubuf.fbuf, writecount / 2, pmpeg->block, pmpeg->block_len) ;
+			}
+
+		if (nbytes < 0)
+		{	psf_log_printf (psf, "lame_encode_buffer returned %d\n", nbytes) ;
+			break ;
+			} ;
+
+		if (nbytes)
+		{	writen = psf_fwrite (pmpeg->block, 1, nbytes, psf) ;
+			if (writen != nbytes)
+			{	psf_log_printf (psf, "*** Warning : short write (%d != %d).\n", writen, nbytes) ;
+				} ;
+			} ;
+
+		total += writecount ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+}
+
+
+static inline void
+normalize_double (double *dest, const double *src, sf_count_t count, double norm_fact)
+{	while (--count >= 0)
+	{	dest [count] = src [count] * norm_fact ;
+		} ;
+}
+
+
+static sf_count_t
+mpeg_l3_encode_write_double_mono (SF_PRIVATE *psf, const double *ptr, sf_count_t len)
+{	BUF_UNION ubuf ;
+	MPEG_L3_ENC_PRIVATE *pmpeg = (MPEG_L3_ENC_PRIVATE*) psf->codec_data ;
+	sf_count_t total = 0 ;
+	int nbytes, writecount, writen ;
+
+	if ((psf->error = mpeg_l3_encoder_construct (psf)))
+		return 0 ;
+
+	const sf_count_t max_samples = SF_MIN (ARRAY_LEN (ubuf.dbuf), pmpeg->frame_samples) ;
+	while (len)
+	{	writecount = SF_MIN (len, max_samples) ;
+
+		if (psf->norm_double)
+			nbytes = lame_encode_buffer_ieee_double (pmpeg->lamef, ptr + total, NULL, writecount, pmpeg->block, pmpeg->block_len) ;
+		else
+		{	/* Lame lacks non-normalized double writing */
+			normalize_double (ubuf.dbuf, ptr + total, writecount, 1.0 / (double) 0x8000) ;
+			nbytes = lame_encode_buffer_ieee_double (pmpeg->lamef, ubuf.dbuf, NULL, writecount, pmpeg->block, pmpeg->block_len) ;
+			}
+
+		if (nbytes < 0)
+		{	psf_log_printf (psf, "lame_encode_buffer returned %d\n", nbytes) ;
+			break ;
+			} ;
+
+		if (nbytes)
+		{	writen = psf_fwrite (pmpeg->block, 1, nbytes, psf) ;
+			if (writen != nbytes)
+			{	psf_log_printf (psf, "*** Warning : short write (%d != %d).\n", writen, nbytes) ;
+				} ;
+			} ;
+
+		total += writecount ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+}
+
+
+static sf_count_t
+mpeg_l3_encode_write_double_stereo (SF_PRIVATE *psf, const double *ptr, sf_count_t len)
+{	BUF_UNION ubuf ;
+	MPEG_L3_ENC_PRIVATE *pmpeg = (MPEG_L3_ENC_PRIVATE*) psf->codec_data ;
+	sf_count_t total = 0 ;
+	int nbytes, writecount, writen ;
+
+	if ((psf->error = mpeg_l3_encoder_construct (psf)))
+		return 0 ;
+
+	const sf_count_t max_samples = SF_MIN (ARRAY_LEN (ubuf.dbuf), pmpeg->frame_samples) ;
+	while (len)
+	{	writecount = SF_MIN (len, max_samples) ;
+
+		if (psf->norm_double)
+			nbytes = lame_encode_buffer_interleaved_ieee_double (pmpeg->lamef, ptr + total, writecount / 2, pmpeg->block, pmpeg->block_len) ;
+		else
+		{	/* Lame lacks interleaved non-normalized double writing */
+			normalize_double (ubuf.dbuf, ptr + total, writecount, 1.0 / (double) 0x8000) ;
+			nbytes = lame_encode_buffer_interleaved_ieee_double (pmpeg->lamef, ubuf.dbuf, writecount / 2, pmpeg->block, pmpeg->block_len) ;
+			}
+
+		if (nbytes < 0)
+		{	psf_log_printf (psf, "lame_encode_buffer returned %d\n", nbytes) ;
+			break ;
+			} ;
+
+		if (nbytes)
+		{	writen = psf_fwrite (pmpeg->block, 1, nbytes, psf) ;
+			if (writen != nbytes)
+			{	psf_log_printf (psf, "*** Warning : short write (%d != %d).\n", writen, nbytes) ;
+				} ;
+			} ;
+
+		total += writecount ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+}
+
+#else /* HAVE_MPEG */
+
+int
+mpeg_l3_encoder_init (SF_PRIVATE *psf, int UNUSED (vbr))
+{	psf_log_printf (psf, "This version of libsndfile was compiled without MPEG Layer 3 encoding support.\n") ;
+	return SFE_UNIMPLEMENTED ;
+} /* mpeg_l3_encoder_init */
+
+#endif
diff --git a/src/ms_adpcm.c b/src/ms_adpcm.c
new file mode 100644
index 0000000..0e05181
--- /dev/null
+++ b/src/ms_adpcm.c
@@ -0,0 +1,857 @@
+/*
+** Copyright (C) 1999-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<stdlib.h>
+#include	<string.h>
+#include	<math.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+#include	"wavlike.h"
+
+
+typedef struct
+{	int				channels, blocksize, samplesperblock, blocks, dataremaining ;
+	int				blockcount ;
+	int				sync_error ;
+	sf_count_t		samplecount ;
+	short			*samples ;
+	unsigned char	*block ;
+	short			dummydata [] ; /* ISO C99 struct flexible array. */
+} MSADPCM_PRIVATE ;
+
+/*============================================================================================
+** MS ADPCM static data and functions.
+*/
+
+static int AdaptationTable [] =
+{	230, 230, 230, 230, 307, 409, 512, 614,
+	768, 614, 512, 409, 307, 230, 230, 230
+} ;
+
+/* TODO : The first 7 coef's are are always hardcode and must
+   appear in the actual WAVE file.  They should be read in
+   in case a sound program added extras to the list. */
+
+static int AdaptCoeff1 [WAVLIKE_MSADPCM_ADAPT_COEFF_COUNT] =
+{	256, 512, 0, 192, 240, 460, 392
+} ;
+
+static int AdaptCoeff2 [WAVLIKE_MSADPCM_ADAPT_COEFF_COUNT] =
+{	0, -256, 0, 64, 0, -208, -232
+} ;
+
+/*============================================================================================
+**	MS ADPCM Block Layout.
+**	======================
+**	Block is usually 256, 512 or 1024 bytes depending on sample rate.
+**	For a mono file, the block is laid out as follows:
+**		byte	purpose
+**		0		block predictor [0..6]
+**		1,2		initial idelta (positive)
+**		3,4		sample 1
+**		5,6		sample 0
+**		7..n	packed bytecodes
+**
+**	For a stereo file, the block is laid out as follows:
+**		byte	purpose
+**		0		block predictor [0..6] for left channel
+**		1		block predictor [0..6] for right channel
+**		2,3		initial idelta (positive) for left channel
+**		4,5		initial idelta (positive) for right channel
+**		6,7		sample 1 for left channel
+**		8,9		sample 1 for right channel
+**		10,11	sample 0 for left channel
+**		12,13	sample 0 for right channel
+**		14..n	packed bytecodes
+*/
+
+/*============================================================================================
+** Static functions.
+*/
+
+static	int	msadpcm_decode_block	(SF_PRIVATE *psf, MSADPCM_PRIVATE *pms) ;
+static sf_count_t msadpcm_read_block	(SF_PRIVATE *psf, MSADPCM_PRIVATE *pms, short *ptr, int len) ;
+
+static	int	msadpcm_encode_block	(SF_PRIVATE *psf, MSADPCM_PRIVATE *pms) ;
+static sf_count_t msadpcm_write_block	(SF_PRIVATE *psf, MSADPCM_PRIVATE *pms, const short *ptr, int len) ;
+
+static sf_count_t	msadpcm_read_s	(SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
+static sf_count_t	msadpcm_read_i	(SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
+static sf_count_t	msadpcm_read_f	(SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
+static sf_count_t	msadpcm_read_d	(SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
+
+static sf_count_t	msadpcm_write_s	(SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
+static sf_count_t	msadpcm_write_i	(SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
+static sf_count_t	msadpcm_write_f	(SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
+static sf_count_t	msadpcm_write_d	(SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
+
+static sf_count_t msadpcm_seek	(SF_PRIVATE *psf, int mode, sf_count_t offset) ;
+static	int	msadpcm_close	(SF_PRIVATE *psf) ;
+
+static	void	choose_predictor (unsigned int channels, short *data, int *bpred, int *idelta) ;
+
+/*============================================================================================
+** MS ADPCM Read Functions.
+*/
+
+int
+wavlike_msadpcm_init	(SF_PRIVATE *psf, int blockalign, int samplesperblock)
+{	MSADPCM_PRIVATE	*pms ;
+	unsigned int	pmssize ;
+	int				count ;
+
+	if (psf->codec_data != NULL)
+	{	psf_log_printf (psf, "*** psf->codec_data is not NULL.\n") ;
+		return SFE_INTERNAL ;
+		} ;
+
+	if (psf->file.mode == SFM_WRITE)
+		samplesperblock = 2 + 2 * (blockalign - 7 * psf->sf.channels) / psf->sf.channels ;
+
+	/* There's 7 samples per channel in the preamble of each block */
+	if (samplesperblock < 7 * psf->sf.channels)
+	{	psf_log_printf (psf, "*** Error samplesperblock (%d) should be >= %d.\n", samplesperblock, 7 * psf->sf.channels) ;
+		return SFE_INTERNAL ;
+		} ;
+
+	if (2 * blockalign < samplesperblock * psf->sf.channels)
+	{	psf_log_printf (psf, "*** Error blockalign (%d) should be >= %d.\n", blockalign, samplesperblock * psf->sf.channels / 2) ;
+		return SFE_INTERNAL ;
+		} ;
+
+	pmssize = sizeof (MSADPCM_PRIVATE) + blockalign + 3 * psf->sf.channels * samplesperblock ;
+
+	if (! (psf->codec_data = calloc (1, pmssize)))
+		return SFE_MALLOC_FAILED ;
+	pms = (MSADPCM_PRIVATE*) psf->codec_data ;
+
+	pms->sync_error = 0 ;
+	pms->samples	= pms->dummydata ;
+	pms->block		= (unsigned char*) (pms->dummydata + psf->sf.channels * samplesperblock) ;
+
+	pms->channels	= psf->sf.channels ;
+	pms->blocksize	= blockalign ;
+	pms->samplesperblock = samplesperblock ;
+
+	if (pms->blocksize <= 0)
+	{	psf_log_printf (psf, "*** Error : pms->blocksize should be > 0.\n") ;
+		return SFE_INTERNAL ;
+		} ;
+
+	if (psf->file.mode == SFM_READ)
+	{	pms->dataremaining = psf->datalength ;
+
+		if (psf->datalength % pms->blocksize)
+			pms->blocks = psf->datalength / pms->blocksize + 1 ;
+		else
+			pms->blocks = psf->datalength / pms->blocksize ;
+
+		count = 2 * (pms->blocksize - 6 * pms->channels) / pms->channels ;
+		if (pms->samplesperblock != count)
+		{	psf_log_printf (psf, "*** Error : samplesperblock should be %d.\n", count) ;
+			return SFE_INTERNAL ;
+			} ;
+
+		psf->sf.frames = (psf->datalength / pms->blocksize) * pms->samplesperblock ;
+
+		msadpcm_decode_block (psf, pms) ;
+
+		psf->read_short		= msadpcm_read_s ;
+		psf->read_int		= msadpcm_read_i ;
+		psf->read_float		= msadpcm_read_f ;
+		psf->read_double	= msadpcm_read_d ;
+		} ;
+
+	if (psf->file.mode == SFM_WRITE)
+	{	pms->samples = pms->dummydata ;
+
+		pms->samplecount = 0 ;
+
+		psf->write_short	= msadpcm_write_s ;
+		psf->write_int		= msadpcm_write_i ;
+		psf->write_float	= msadpcm_write_f ;
+		psf->write_double	= msadpcm_write_d ;
+		} ;
+
+	psf->codec_close = msadpcm_close ;
+	psf->seek = msadpcm_seek ;
+
+	return 0 ;
+} /* wavlike_msadpcm_init */
+
+
+static inline short
+msadpcm_get_bpred (SF_PRIVATE *psf, MSADPCM_PRIVATE *pms, unsigned char value)
+{	if (value >= WAVLIKE_MSADPCM_ADAPT_COEFF_COUNT)
+	{	if (pms->sync_error == 0)
+		{	pms->sync_error = 1 ;
+			psf_log_printf (psf, "MS ADPCM synchronisation error (%u should be < %u).\n", value, WAVLIKE_MSADPCM_ADAPT_COEFF_COUNT) ;
+			} ;
+		return 0 ;
+		} ;
+	return value ;
+} /* msadpcm_get_bpred */
+
+
+static int
+msadpcm_decode_block	(SF_PRIVATE *psf, MSADPCM_PRIVATE *pms)
+{	int		chan, k, blockindx, sampleindx ;
+	short	bytecode, bpred [2], chan_idelta [2] ;
+
+	int predict ;
+	int current ;
+	int idelta ;
+
+	pms->blockcount ++ ;
+	pms->samplecount = 0 ;
+
+	if (pms->blockcount > pms->blocks)
+	{	memset (pms->samples, 0, pms->samplesperblock * pms->channels) ;
+		return 1 ;
+		} ;
+
+	if ((k = (int) psf_fread (pms->block, 1, pms->blocksize, psf)) != pms->blocksize)
+	{	psf_log_printf (psf, "*** Warning : short read (%d != %d).\n", k, pms->blocksize) ;
+		if (k <= 0)
+			return 1 ;
+		} ;
+
+	/* Read and check the block header. */
+
+	if (pms->channels == 1)
+	{	bpred [0] = msadpcm_get_bpred (psf, pms, pms->block [0]) ;
+
+		chan_idelta [0] = pms->block [1] | (pms->block [2] << 8) ;
+		chan_idelta [1] = 0 ;
+
+		pms->samples [1] = pms->block [3] | (pms->block [4] << 8) ;
+		pms->samples [0] = pms->block [5] | (pms->block [6] << 8) ;
+		blockindx = 7 ;
+		}
+	else
+	{	bpred [0] = msadpcm_get_bpred (psf, pms, pms->block [0]) ;
+		bpred [1] = msadpcm_get_bpred (psf, pms, pms->block [1]) ;
+
+		chan_idelta [0] = pms->block [2] | (pms->block [3] << 8) ;
+		chan_idelta [1] = pms->block [4] | (pms->block [5] << 8) ;
+
+		pms->samples [2] = pms->block [6] | (pms->block [7] << 8) ;
+		pms->samples [3] = pms->block [8] | (pms->block [9] << 8) ;
+
+		pms->samples [0] = pms->block [10] | (pms->block [11] << 8) ;
+		pms->samples [1] = pms->block [12] | (pms->block [13] << 8) ;
+
+		blockindx = 14 ;
+		} ;
+
+	/*--------------------------------------------------------
+	This was left over from a time when calculations were done
+	as ints rather than shorts. Keep this around as a reminder
+	in case I ever find a file which decodes incorrectly.
+
+    if (chan_idelta [0] & 0x8000)
+		chan_idelta [0] -= 0x10000 ;
+    if (chan_idelta [1] & 0x8000)
+		chan_idelta [1] -= 0x10000 ;
+	--------------------------------------------------------*/
+
+	/* Pull apart the packed 4 bit samples and store them in their
+	** correct sample positions.
+	*/
+
+	sampleindx = 2 * pms->channels ;
+	while (blockindx < pms->blocksize)
+	{	bytecode = pms->block [blockindx++] ;
+		pms->samples [sampleindx++] = (bytecode >> 4) & 0x0F ;
+		pms->samples [sampleindx++] = bytecode & 0x0F ;
+		} ;
+
+	/* Decode the encoded 4 bit samples. */
+
+	for (k = 2 * pms->channels ; k < (pms->samplesperblock * pms->channels) ; k ++)
+	{	chan = (pms->channels > 1) ? (k % 2) : 0 ;
+
+		bytecode = pms->samples [k] & 0xF ;
+
+		/* Compute next Adaptive Scale Factor (ASF) */
+		idelta = chan_idelta [chan] ;
+		chan_idelta [chan] = (AdaptationTable [bytecode] * idelta) >> 8 ;	/* => / 256 => FIXED_POINT_ADAPTATION_BASE == 256 */
+		if (chan_idelta [chan] < 16)
+			chan_idelta [chan] = 16 ;
+		if (bytecode & 0x8)
+			bytecode -= 0x10 ;
+
+		predict = ((pms->samples [k - pms->channels] * AdaptCoeff1 [bpred [chan]])
+					+ (pms->samples [k - 2 * pms->channels] * AdaptCoeff2 [bpred [chan]])) >> 8 ; /* => / 256 => FIXED_POINT_COEFF_BASE == 256 */
+		current = (bytecode * idelta) + predict ;
+
+		if (current > 32767)
+			current = 32767 ;
+		else if (current < -32768)
+			current = -32768 ;
+
+		pms->samples [k] = current ;
+		} ;
+
+	return 0 ;
+} /* msadpcm_decode_block */
+
+static sf_count_t
+msadpcm_read_block	(SF_PRIVATE *psf, MSADPCM_PRIVATE *pms, short *ptr, int len)
+{	int	count, total = 0, indx = 0 ;
+
+	while (indx < len)
+	{	if (pms->blockcount >= pms->blocks && pms->samplecount >= pms->samplesperblock)
+		{	memset (&(ptr [indx]), 0, (size_t) ((len - indx) * sizeof (short))) ;
+			return total ;
+			} ;
+
+		if (pms->samplecount >= pms->samplesperblock)
+			if (msadpcm_decode_block (psf, pms) != 0)
+				return total ;
+
+		count = (pms->samplesperblock - pms->samplecount) * pms->channels ;
+		count = (len - indx > count) ? count : len - indx ;
+
+		memcpy (&(ptr [indx]), &(pms->samples [pms->samplecount * pms->channels]), count * sizeof (short)) ;
+		indx += count ;
+		pms->samplecount += count / pms->channels ;
+		total = indx ;
+		} ;
+
+	return total ;
+} /* msadpcm_read_block */
+
+static sf_count_t
+msadpcm_read_s	(SF_PRIVATE *psf, short *ptr, sf_count_t len)
+{	MSADPCM_PRIVATE 	*pms ;
+	int			readcount, count ;
+	sf_count_t	total = 0 ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	pms = (MSADPCM_PRIVATE*) psf->codec_data ;
+
+	while (len > 0)
+	{	readcount = (len > 0x10000000) ? 0x10000000 : (int) len ;
+
+		if ((count = (int) msadpcm_read_block (psf, pms, ptr, readcount)) <= 0)
+			return -1 ;
+
+		total += count ;
+		len -= count ;
+		if (count != readcount)
+			break ;
+		} ;
+
+	return total ;
+} /* msadpcm_read_s */
+
+static sf_count_t
+msadpcm_read_i	(SF_PRIVATE *psf, int *ptr, sf_count_t len)
+{	MSADPCM_PRIVATE *pms ;
+	BUF_UNION	ubuf ;
+	short		*sptr ;
+	int			k, bufferlen, readcount = 0, count ;
+	sf_count_t	total = 0 ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	pms = (MSADPCM_PRIVATE*) psf->codec_data ;
+
+	sptr = ubuf.sbuf ;
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+	while (len > 0)
+	{	readcount = (len >= bufferlen) ? bufferlen : (int) len ;
+
+		if ((count = (int) msadpcm_read_block (psf, pms, sptr, readcount)) <= 0)
+			return -1 ;
+
+		for (k = 0 ; k < readcount ; k++)
+			ptr [total + k] = arith_shift_left (sptr [k], 16) ;
+		total += count ;
+		len -= readcount ;
+		if (count != readcount)
+			break ;
+		} ;
+	return total ;
+} /* msadpcm_read_i */
+
+static sf_count_t
+msadpcm_read_f	(SF_PRIVATE *psf, float *ptr, sf_count_t len)
+{	MSADPCM_PRIVATE *pms ;
+	BUF_UNION	ubuf ;
+	short		*sptr ;
+	int			k, bufferlen, readcount = 0, count ;
+	sf_count_t	total = 0 ;
+	float		normfact ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	pms = (MSADPCM_PRIVATE*) psf->codec_data ;
+
+	normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x8000) : 1.0 ;
+	sptr = ubuf.sbuf ;
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+	while (len > 0)
+	{	readcount = (len >= bufferlen) ? bufferlen : (int) len ;
+
+		if ((count = (int) msadpcm_read_block (psf, pms, sptr, readcount)) <= 0)
+			return -1 ;
+
+		for (k = 0 ; k < readcount ; k++)
+			ptr [total + k] = normfact * (float) (sptr [k]) ;
+		total += count ;
+		len -= readcount ;
+		if (count != readcount)
+			break ;
+		} ;
+	return total ;
+} /* msadpcm_read_f */
+
+static sf_count_t
+msadpcm_read_d	(SF_PRIVATE *psf, double *ptr, sf_count_t len)
+{	MSADPCM_PRIVATE *pms ;
+	BUF_UNION	ubuf ;
+	short		*sptr ;
+	int			k, bufferlen, readcount = 0, count ;
+	sf_count_t	total = 0 ;
+	double 		normfact ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	pms = (MSADPCM_PRIVATE*) psf->codec_data ;
+
+	normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((double) 0x8000) : 1.0 ;
+	sptr = ubuf.sbuf ;
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+	while (len > 0)
+	{	readcount = (len >= bufferlen) ? bufferlen : (int) len ;
+
+		if ((count = (int) msadpcm_read_block (psf, pms, sptr, readcount)) <= 0)
+			return -1 ;
+
+		for (k = 0 ; k < readcount ; k++)
+			ptr [total + k] = normfact * (double) (sptr [k]) ;
+		total += count ;
+		len -= readcount ;
+		if (count != readcount)
+			break ;
+		} ;
+
+	return total ;
+} /* msadpcm_read_d */
+
+static sf_count_t
+msadpcm_seek	(SF_PRIVATE *psf, int mode, sf_count_t offset)
+{	MSADPCM_PRIVATE *pms ;
+	int			newblock, newsample ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	pms = (MSADPCM_PRIVATE*) psf->codec_data ;
+
+	if (psf->datalength < 0 || psf->dataoffset < 0)
+	{	psf->error = SFE_BAD_SEEK ;
+		return	PSF_SEEK_ERROR ;
+		} ;
+
+	if (offset == 0)
+	{	psf_fseek (psf, psf->dataoffset, SEEK_SET) ;
+		pms->blockcount = 0 ;
+		msadpcm_decode_block (psf, pms) ;
+		pms->samplecount = 0 ;
+		return 0 ;
+		} ;
+
+	if (offset < 0 || offset > pms->blocks * pms->samplesperblock)
+	{	psf->error = SFE_BAD_SEEK ;
+		return	PSF_SEEK_ERROR ;
+		} ;
+
+	newblock	= offset / pms->samplesperblock ;
+	newsample	= offset % pms->samplesperblock ;
+
+	if (mode == SFM_READ)
+	{	psf_fseek (psf, psf->dataoffset + newblock * pms->blocksize, SEEK_SET) ;
+		pms->blockcount = newblock ;
+		msadpcm_decode_block (psf, pms) ;
+		pms->samplecount = newsample ;
+		}
+	else
+	{	/* What to do about write??? */
+		psf->error = SFE_BAD_SEEK ;
+		return	PSF_SEEK_ERROR ;
+		} ;
+
+	return newblock * pms->samplesperblock + newsample ;
+} /* msadpcm_seek */
+
+/*==========================================================================================
+** MS ADPCM Write Functions.
+*/
+
+void
+wavlike_msadpcm_write_adapt_coeffs	(SF_PRIVATE *psf)
+{	int k ;
+
+	for (k = 0 ; k < WAVLIKE_MSADPCM_ADAPT_COEFF_COUNT ; k++)
+		psf_binheader_writef (psf, "22", BHW2 (AdaptCoeff1 [k]), BHW2 (AdaptCoeff2 [k])) ;
+} /* wavlike_msadpcm_write_adapt_coeffs */
+
+/*==========================================================================================
+*/
+
+static int
+msadpcm_encode_block	(SF_PRIVATE *psf, MSADPCM_PRIVATE *pms)
+{	unsigned int	blockindx ;
+	unsigned char	byte ;
+	int				chan, k, predict, bpred [2] = { 0 }, idelta [2] = { 0 },
+					errordelta, newsamp ;
+
+	choose_predictor (pms->channels, pms->samples, bpred, idelta) ;
+
+	/* Write the block header. */
+
+	if (pms->channels == 1)
+	{	pms->block [0]	= bpred [0] ;
+		pms->block [1]	= idelta [0] & 0xFF ;
+		pms->block [2]	= idelta [0] >> 8 ;
+		pms->block [3]	= pms->samples [1] & 0xFF ;
+		pms->block [4]	= pms->samples [1] >> 8 ;
+		pms->block [5]	= pms->samples [0] & 0xFF ;
+		pms->block [6]	= pms->samples [0] >> 8 ;
+
+		blockindx = 7 ;
+		byte = 0 ;
+
+		/* Encode the samples as 4 bit. */
+
+		for (k = 2 ; k < pms->samplesperblock ; k++)
+		{	predict = (pms->samples [k-1] * AdaptCoeff1 [bpred [0]] + pms->samples [k-2] * AdaptCoeff2 [bpred [0]]) >> 8 ;
+			errordelta = (pms->samples [k] - predict) / idelta [0] ;
+			if (errordelta < -8)
+				errordelta = -8 ;
+			else if (errordelta > 7)
+				errordelta = 7 ;
+			newsamp = predict + (idelta [0] * errordelta) ;
+			if (newsamp > 32767)
+				newsamp = 32767 ;
+			else if (newsamp < -32768)
+				newsamp = -32768 ;
+			if (errordelta < 0)
+				errordelta += 0x10 ;
+
+			byte = (byte << 4) | (errordelta & 0xF) ;
+			if (k % 2)
+			{	pms->block [blockindx++] = byte ;
+				byte = 0 ;
+				} ;
+
+			idelta [0] = (idelta [0] * AdaptationTable [errordelta]) >> 8 ;
+			if (idelta [0] < 16)
+				idelta [0] = 16 ;
+			pms->samples [k] = newsamp ;
+			} ;
+		}
+	else
+	{	/* Stereo file. */
+		pms->block [0]	= bpred [0] ;
+		pms->block [1]	= bpred [1] ;
+
+		pms->block [2]	= idelta [0] & 0xFF ;
+		pms->block [3]	= idelta [0] >> 8 ;
+		pms->block [4]	= idelta [1] & 0xFF ;
+		pms->block [5]	= idelta [1] >> 8 ;
+
+		pms->block [6]	= pms->samples [2] & 0xFF ;
+		pms->block [7]	= pms->samples [2] >> 8 ;
+		pms->block [8]	= pms->samples [3] & 0xFF ;
+		pms->block [9]	= pms->samples [3] >> 8 ;
+
+		pms->block [10]	= pms->samples [0] & 0xFF ;
+		pms->block [11]	= pms->samples [0] >> 8 ;
+		pms->block [12]	= pms->samples [1] & 0xFF ;
+		pms->block [13]	= pms->samples [1] >> 8 ;
+
+		blockindx = 14 ;
+		byte = 0 ;
+		chan = 1 ;
+
+		for (k = 4 ; k < 2 * pms->samplesperblock ; k++)
+		{	chan = k & 1 ;
+
+			predict = (pms->samples [k-2] * AdaptCoeff1 [bpred [chan]] + pms->samples [k-4] * AdaptCoeff2 [bpred [chan]]) >> 8 ;
+			errordelta = (pms->samples [k] - predict) / idelta [chan] ;
+
+
+			if (errordelta < -8)
+				errordelta = -8 ;
+			else if (errordelta > 7)
+				errordelta = 7 ;
+			newsamp = predict + (idelta [chan] * errordelta) ;
+			if (newsamp > 32767)
+				newsamp = 32767 ;
+			else if (newsamp < -32768)
+				newsamp = -32768 ;
+			if (errordelta < 0)
+				errordelta += 0x10 ;
+
+			byte = (byte << 4) | (errordelta & 0xF) ;
+
+			if (chan)
+			{	pms->block [blockindx++] = byte ;
+				byte = 0 ;
+				} ;
+
+			idelta [chan] = (idelta [chan] * AdaptationTable [errordelta]) >> 8 ;
+			if (idelta [chan] < 16)
+				idelta [chan] = 16 ;
+			pms->samples [k] = newsamp ;
+			} ;
+		} ;
+
+	/* Write the block to disk. */
+
+	if ((k = (int) psf_fwrite (pms->block, 1, pms->blocksize, psf)) != pms->blocksize)
+		psf_log_printf (psf, "*** Warning : short write (%d != %d).\n", k, pms->blocksize) ;
+
+	memset (pms->samples, 0, pms->samplesperblock * sizeof (short)) ;
+
+	pms->blockcount ++ ;
+	pms->samplecount = 0 ;
+
+	return 1 ;
+} /* msadpcm_encode_block */
+
+static sf_count_t
+msadpcm_write_block	(SF_PRIVATE *psf, MSADPCM_PRIVATE *pms, const short *ptr, int len)
+{	int		count, total = 0, indx = 0 ;
+
+	while (indx < len)
+	{	count = (pms->samplesperblock - pms->samplecount) * pms->channels ;
+
+		if (count > len - indx)
+			count = len - indx ;
+
+		memcpy (&(pms->samples [pms->samplecount * pms->channels]), &(ptr [total]), count * sizeof (short)) ;
+		indx += count ;
+		pms->samplecount += count / pms->channels ;
+		total = indx ;
+
+		if (pms->samplecount >= pms->samplesperblock)
+			msadpcm_encode_block (psf, pms) ;
+		} ;
+
+	return total ;
+} /* msadpcm_write_block */
+
+static sf_count_t
+msadpcm_write_s	(SF_PRIVATE *psf, const short *ptr, sf_count_t len)
+{	MSADPCM_PRIVATE *pms ;
+	int			writecount, count ;
+	sf_count_t	total = 0 ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	pms = (MSADPCM_PRIVATE*) psf->codec_data ;
+
+	while (len > 0)
+	{	writecount = (len > 0x10000000) ? 0x10000000 : (int) len ;
+
+		count = (int) msadpcm_write_block (psf, pms, ptr, writecount) ;
+
+		total += count ;
+		len -= count ;
+		if (count != writecount)
+			break ;
+		} ;
+
+	return total ;
+} /* msadpcm_write_s */
+
+static sf_count_t
+msadpcm_write_i	(SF_PRIVATE *psf, const int *ptr, sf_count_t len)
+{	MSADPCM_PRIVATE *pms ;
+	BUF_UNION	ubuf ;
+	short		*sptr ;
+	int			k, bufferlen, writecount, count ;
+	sf_count_t	total = 0 ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	pms = (MSADPCM_PRIVATE*) psf->codec_data ;
+
+	sptr = ubuf.sbuf ;
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+	while (len > 0)
+	{	writecount = (len >= bufferlen) ? bufferlen : (int) len ;
+		for (k = 0 ; k < writecount ; k++)
+			sptr [k] = ptr [total + k] >> 16 ;
+		count = (int) msadpcm_write_block (psf, pms, sptr, writecount) ;
+		total += count ;
+		len -= writecount ;
+		if (count != writecount)
+			break ;
+		} ;
+	return total ;
+} /* msadpcm_write_i */
+
+static sf_count_t
+msadpcm_write_f	(SF_PRIVATE *psf, const float *ptr, sf_count_t len)
+{	MSADPCM_PRIVATE *pms ;
+	BUF_UNION	ubuf ;
+	short		*sptr ;
+	int			k, bufferlen, writecount, count ;
+	sf_count_t	total = 0 ;
+	float		normfact ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	pms = (MSADPCM_PRIVATE*) psf->codec_data ;
+
+	normfact = (psf->norm_float == SF_TRUE) ? (1.0 * 0x7FFF) : 1.0 ;
+
+	sptr = ubuf.sbuf ;
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+	while (len > 0)
+	{	writecount = (len >= bufferlen) ? bufferlen : (int) len ;
+		for (k = 0 ; k < writecount ; k++)
+			sptr [k] = psf_lrintf (normfact * ptr [total + k]) ;
+		count = (int) msadpcm_write_block (psf, pms, sptr, writecount) ;
+		total += count ;
+		len -= writecount ;
+		if (count != writecount)
+			break ;
+		} ;
+	return total ;
+} /* msadpcm_write_f */
+
+static sf_count_t
+msadpcm_write_d	(SF_PRIVATE *psf, const double *ptr, sf_count_t len)
+{	MSADPCM_PRIVATE *pms ;
+	BUF_UNION	ubuf ;
+	short		*sptr ;
+	int			k, bufferlen, writecount, count ;
+	sf_count_t	total = 0 ;
+	double 		normfact ;
+
+	normfact = (psf->norm_double == SF_TRUE) ? (1.0 * 0x7FFF) : 1.0 ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	pms = (MSADPCM_PRIVATE*) psf->codec_data ;
+
+	sptr = ubuf.sbuf ;
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+	while (len > 0)
+	{	writecount = (len >= bufferlen) ? bufferlen : (int) len ;
+		for (k = 0 ; k < writecount ; k++)
+			sptr [k] = psf_lrint (normfact * ptr [total + k]) ;
+		count = (int) msadpcm_write_block (psf, pms, sptr, writecount) ;
+		total += count ;
+		len -= writecount ;
+		if (count != writecount)
+			break ;
+		} ;
+	return total ;
+} /* msadpcm_write_d */
+
+/*========================================================================================
+*/
+
+static int
+msadpcm_close	(SF_PRIVATE *psf)
+{	MSADPCM_PRIVATE *pms ;
+
+	pms = (MSADPCM_PRIVATE*) psf->codec_data ;
+
+	if (psf->file.mode == SFM_WRITE)
+	{	/*  Now we know static int for certain the length of the file we can
+		**  re-write the header.
+		*/
+
+		if (pms->samplecount && pms->samplecount < pms->samplesperblock)
+			msadpcm_encode_block (psf, pms) ;
+		} ;
+
+	return 0 ;
+} /* msadpcm_close */
+
+/*========================================================================================
+** Static functions.
+*/
+
+/*----------------------------------------------------------------------------------------
+**	Choosing the block predictor.
+**	Each block requires a predictor and an idelta for each channel.
+**	The predictor is in the range [0..6] which is an indx into the	two AdaptCoeff tables.
+**	The predictor is chosen by trying all of the possible predictors on a small set of
+**	samples at the beginning of the block. The predictor with the smallest average
+**	abs (idelta) is chosen as the best predictor for this block.
+**	The value of idelta is chosen to to give a 4 bit code value of +/- 4 (approx. half the
+**	max. code value). If the average abs (idelta) is zero, the sixth predictor is chosen.
+**	If the value of idelta is less then 16 it is set to 16.
+**
+**	Microsoft uses an IDELTA_COUNT (number of sample pairs used to choose best predictor)
+**	value of 3. The best possible results would be obtained by using all the samples to
+**	choose the predictor.
+*/
+
+#define		IDELTA_COUNT	3
+
+static	void
+choose_predictor (unsigned int channels, short *data, int *block_pred, int *idelta)
+{	unsigned int	chan, k, bpred, idelta_sum, best_bpred, best_idelta ;
+
+	for (chan = 0 ; chan < channels ; chan++)
+	{	best_bpred = best_idelta = 0 ;
+
+		for (bpred = 0 ; bpred < 7 ; bpred++)
+		{	idelta_sum = 0 ;
+			for (k = 2 ; k < 2 + IDELTA_COUNT ; k++)
+				idelta_sum += abs (data [k * channels] - ((data [(k - 1) * channels] * AdaptCoeff1 [bpred] + data [(k - 2) * channels] * AdaptCoeff2 [bpred]) >> 8)) ;
+			idelta_sum /= (4 * IDELTA_COUNT) ;
+
+			if (bpred == 0 || idelta_sum < best_idelta)
+			{	best_bpred = bpred ;
+				best_idelta = idelta_sum ;
+				} ;
+
+			if (! idelta_sum)
+			{	best_bpred = bpred ;
+				best_idelta = 16 ;
+				break ;
+				} ;
+
+			} ; /* for bpred ... */
+		if (best_idelta < 16)
+			best_idelta = 16 ;
+
+		block_pred [chan]	= best_bpred ;
+		idelta [chan]		= best_idelta ;
+		} ;
+
+	return ;
+} /* choose_predictor */
+
diff --git a/src/new.c b/src/new.c
new file mode 100644
index 0000000..23f3087
--- /dev/null
+++ b/src/new.c
@@ -0,0 +1,116 @@
+/*
+** Copyright (C) 2002-2011 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<fcntl.h>
+#include	<string.h>
+#include	<ctype.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+#if (ENABLE_EXPERIMENTAL_CODE == 0)
+
+int
+new_open	(SF_PRIVATE *psf)
+{	if (psf)
+		return SFE_UNIMPLEMENTED ;
+	return (psf && 0) ;
+} /* new_open */
+
+#else
+
+/*------------------------------------------------------------------------------
+** Macros to handle big/little endian issues.
+*/
+
+/*------------------------------------------------------------------------------
+** Typedefs.
+*/
+
+/*------------------------------------------------------------------------------
+** Private static functions.
+*/
+
+static int	new_read_header (SF_PRIVATE *psf) ;
+
+/*------------------------------------------------------------------------------
+** Public function.
+*/
+
+int
+new_open (SF_PRIVATE *psf)
+{	int	subformat, error = 0 ;
+
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+		return SFE_UNIMPLEMENTED ;
+
+	if ((error = new_read_header (psf)))
+			return error ;
+
+	if ((SF_CONTAINER (psf->sf.format)) != SF_FORMAT_WVE)
+		return	SFE_BAD_OPEN_FORMAT ;
+
+	subformat = SF_CODEC (psf->sf.format) ;
+
+	return error ;
+} /* new_open */
+
+/*------------------------------------------------------------------------------
+*/
+
+static int
+new_read_header (SF_PRIVATE *psf)
+{	int marker ;
+
+	/* Set position to start of file to begin reading header. */
+	psf_binheader_readf (psf, "pm", 0, &marker) ;
+	if (marker != ALAW_MARKER)
+		return SFE_WVE_NOT_WVE ;
+
+	psf_binheader_readf (psf, "m", &marker) ;
+	if (marker != SOUN_MARKER)
+		return SFE_WVE_NOT_WVE ;
+
+	psf_binheader_readf (psf, "m", &marker) ;
+	if (marker != DFIL_MARKER)
+		return SFE_WVE_NOT_WVE ;
+
+	psf_log_printf (psf, "Read only : Psion Alaw\n"
+			"  Sample Rate : 8000\n"
+			"  Channels    : 1\n"
+			"  Encoding    : A-law\n") ;
+
+	psf->dataoffset = 0x20 ;
+	psf->datalength = psf->filelength - psf->dataoffset ;
+
+	psf->sf.format		= SF_FORMAT_WVE | SF_FORMAT_ALAW ;
+	psf->sf.samplerate	= 8000 ;
+	psf->sf.frames		= psf->datalength ;
+	psf->sf.channels	= 1 ;
+
+	return alaw_init (psf) ;
+} /* new_read_header */
+
+/*------------------------------------------------------------------------------
+*/
+
+#endif
diff --git a/src/nist.c b/src/nist.c
new file mode 100644
index 0000000..657c4d4
--- /dev/null
+++ b/src/nist.c
@@ -0,0 +1,372 @@
+/*
+** Copyright (C) 1999-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*
+**	Some of the information used to read NIST files was gleaned from
+**	reading the code of Bill Schottstaedt's sndlib library
+**		ftp://ccrma-ftp.stanford.edu/pub/Lisp/sndlib.tar.gz
+**	However, no code from that package was used.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<fcntl.h>
+#include	<string.h>
+#include	<ctype.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+/*------------------------------------------------------------------------------
+*/
+
+#define	NIST_HEADER_LENGTH	1024
+
+/*------------------------------------------------------------------------------
+** Private static functions.
+*/
+
+static	int	nist_close	(SF_PRIVATE *psf) ;
+static	int nist_write_header	(SF_PRIVATE *psf, int calc_length) ;
+static	int nist_read_header	(SF_PRIVATE *psf) ;
+
+/*------------------------------------------------------------------------------
+*/
+
+int
+nist_open	(SF_PRIVATE *psf)
+{	int error ;
+
+	if (psf->file.mode == SFM_READ || (psf->file.mode == SFM_RDWR && psf->filelength > 0))
+	{	if ((error = nist_read_header (psf)))
+			return error ;
+		} ;
+
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	if (psf->is_pipe)
+			return SFE_NO_PIPE_WRITE ;
+
+		if ((SF_CONTAINER (psf->sf.format)) != SF_FORMAT_NIST)
+			return	SFE_BAD_OPEN_FORMAT ;
+
+		psf->endian = SF_ENDIAN (psf->sf.format) ;
+		if (psf->endian == 0 || psf->endian == SF_ENDIAN_CPU)
+			psf->endian = (CPU_IS_BIG_ENDIAN) ? SF_ENDIAN_BIG : SF_ENDIAN_LITTLE ;
+
+		psf->blockwidth = psf->bytewidth * psf->sf.channels ;
+		psf->sf.frames = 0 ;
+
+		if ((error = nist_write_header (psf, SF_FALSE)))
+			return error ;
+
+		psf->write_header = nist_write_header ;
+		} ;
+
+	psf->container_close = nist_close ;
+
+	switch (SF_CODEC (psf->sf.format))
+	{	case SF_FORMAT_PCM_S8 :
+				error = pcm_init (psf) ;
+				break ;
+
+		case SF_FORMAT_PCM_16 :
+		case SF_FORMAT_PCM_24 :
+		case SF_FORMAT_PCM_32 :
+				error = pcm_init (psf) ;
+				break ;
+
+		case SF_FORMAT_ULAW :
+				error = ulaw_init (psf) ;
+				break ;
+
+		case SF_FORMAT_ALAW :
+				error = alaw_init (psf) ;
+				break ;
+
+		default : error = SFE_UNIMPLEMENTED ;
+				break ;
+		} ;
+
+	return error ;
+} /* nist_open */
+
+/*------------------------------------------------------------------------------
+*/
+
+static char bad_header [] =
+{	'N', 'I', 'S', 'T', '_', '1', 'A', 0x0d, 0x0a,
+	' ', ' ', ' ', '1', '0', '2', '4', 0x0d, 0x0a,
+	0
+} ;
+
+	static int
+nist_read_header (SF_PRIVATE *psf)
+{	char	psf_header [NIST_HEADER_LENGTH + 2] ;
+	int		bitwidth = 0, count, encoding ;
+	unsigned bytes = 0 ;
+	char 	str [64], *cptr ;
+	long	samples ;
+
+	/* Go to start of file and read in the whole header. */
+	psf_binheader_readf (psf, "pb", 0, psf_header, NIST_HEADER_LENGTH) ;
+
+	/* Header is a string, so make sure it is null terminated. */
+	psf_header [NIST_HEADER_LENGTH] = 0 ;
+
+	/* Now trim the header after the end marker. */
+	if ((cptr = strstr (psf_header, "end_head")))
+	{	cptr += strlen ("end_head") + 1 ;
+		cptr [0] = 0 ;
+		} ;
+
+	if (strstr (psf_header, bad_header) == psf_header)
+		return SFE_NIST_CRLF_CONVERISON ;
+
+	/* Make sure its a NIST file. */
+	if (strstr (psf_header, "NIST_1A\n") != psf_header)
+	{	psf_log_printf (psf, "Not a NIST file.\n") ;
+		return SFE_NIST_BAD_HEADER ;
+		} ;
+
+	if (sscanf (psf_header, "NIST_1A\n%d\n", &count) == 1)
+		psf->dataoffset = count ;
+	else
+	{	psf_log_printf (psf, "*** Suspicious header length.\n") ;
+		psf->dataoffset = NIST_HEADER_LENGTH ;
+		} ;
+
+	/* Determine sample encoding, start by assuming PCM. */
+	encoding = SF_FORMAT_PCM_U8 ;
+	if ((cptr = strstr (psf_header, "sample_coding -s")))
+	{	sscanf (cptr, "sample_coding -s%d %63s", &count, str) ;
+
+		if (strcmp (str, "pcm") == 0)
+		{	/* Correct this later when we find out the bitwidth. */
+			encoding = SF_FORMAT_PCM_U8 ;
+			}
+		else if (strcmp (str, "alaw") == 0)
+			encoding = SF_FORMAT_ALAW ;
+		else if ((strcmp (str, "ulaw") == 0) || (strcmp (str, "mu-law") == 0))
+			encoding = SF_FORMAT_ULAW ;
+		else
+		{	psf_log_printf (psf, "*** Unknown encoding : %s\n", str) ;
+			encoding = 0 ;
+			} ;
+		} ;
+
+	if ((cptr = strstr (psf_header, "channel_count -i ")) != NULL)
+		sscanf (cptr, "channel_count -i %d", &(psf->sf.channels)) ;
+
+	if ((cptr = strstr (psf_header, "sample_rate -i ")) != NULL)
+		sscanf (cptr, "sample_rate -i %d", &(psf->sf.samplerate)) ;
+
+	if ((cptr = strstr (psf_header, "sample_count -i ")) != NULL)
+	{	sscanf (cptr, "sample_count -i %ld", &samples) ;
+		psf->sf.frames = samples ;
+		} ;
+
+	if ((cptr = strstr (psf_header, "sample_n_bytes -i ")) != NULL)
+		sscanf (cptr, "sample_n_bytes -i %d", &(psf->bytewidth)) ;
+
+	/* Default endian-ness (for 8 bit, u-law, A-law. */
+	psf->endian = (CPU_IS_BIG_ENDIAN) ? SF_ENDIAN_BIG : SF_ENDIAN_LITTLE ;
+
+	/* This is where we figure out endian-ness. */
+	if ((cptr = strstr (psf_header, "sample_byte_format -s"))
+		&& sscanf (cptr, "sample_byte_format -s%u %8s", &bytes, str) == 2)
+	{
+		if (bytes != strlen (str))
+			psf_log_printf (psf, "Weird sample_byte_format : strlen '%s' != %d\n", str, bytes) ;
+
+		if (bytes > 1)
+		{	if (psf->bytewidth == 0)
+				psf->bytewidth = bytes ;
+			else if (psf->bytewidth - bytes != 0)
+			{	psf_log_printf (psf, "psf->bytewidth (%d) != bytes (%d)\n", psf->bytewidth, bytes) ;
+				return SFE_NIST_BAD_ENCODING ;
+				} ;
+
+			if (strcmp (str, "01") == 0)
+				psf->endian = SF_ENDIAN_LITTLE ;
+			else if (strcmp (str, "10") == 0)
+				psf->endian = SF_ENDIAN_BIG ;
+			else
+			{	psf_log_printf (psf, "Weird endian-ness : %s\n", str) ;
+				return SFE_NIST_BAD_ENCODING ;
+				} ;
+			} ;
+
+		psf->sf.format |= psf->endian ;
+		} ;
+
+	if ((cptr = strstr (psf_header, "sample_sig_bits -i ")))
+		sscanf (cptr, "sample_sig_bits -i %d", &bitwidth) ;
+
+	if (strstr (psf_header, "channels_interleaved -s5 FALSE"))
+	{	psf_log_printf (psf, "Non-interleaved data unsupported.\n", str) ;
+		return SFE_NIST_BAD_ENCODING ;
+		} ;
+
+	psf->blockwidth = psf->sf.channels * psf->bytewidth ;
+	psf->datalength = psf->filelength - psf->dataoffset ;
+
+	psf_fseek (psf, psf->dataoffset, SEEK_SET) ;
+
+	if (encoding == SF_FORMAT_PCM_U8)
+	{	switch (psf->bytewidth)
+		{	case 1 :
+					psf->sf.format |= SF_FORMAT_PCM_S8 ;
+					break ;
+
+			case 2 :
+					psf->sf.format |= SF_FORMAT_PCM_16 ;
+					break ;
+
+			case 3 :
+					psf->sf.format |= SF_FORMAT_PCM_24 ;
+					break ;
+
+			case 4 :
+					psf->sf.format |= SF_FORMAT_PCM_32 ;
+					break ;
+
+			default : break ;
+			} ;
+		}
+	else if (encoding != 0)
+		psf->sf.format |= encoding ;
+	else
+		return SFE_UNIMPLEMENTED ;
+
+	/* Sanitize psf->sf.format. */
+	switch (SF_CODEC (psf->sf.format))
+	{	case SF_FORMAT_ULAW :
+		case SF_FORMAT_ALAW :
+		case SF_FORMAT_PCM_U8 :
+			/* Blank out endian bits. */
+			psf->sf.format = SF_FORMAT_NIST | SF_CODEC (psf->sf.format) ;
+			break ;
+
+		default :
+			break ;
+		} ;
+
+	return 0 ;
+} /* nist_read_header */
+
+static int
+nist_close	(SF_PRIVATE *psf)
+{
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+		nist_write_header (psf, SF_TRUE) ;
+
+	return 0 ;
+} /* nist_close */
+
+/*=========================================================================
+*/
+
+static int
+nist_write_header (SF_PRIVATE *psf, int calc_length)
+{	const char	*end_str ;
+	long		samples ;
+	sf_count_t	current ;
+
+	current = psf_ftell (psf) ;
+
+	if (calc_length)
+	{	psf->filelength = psf_get_filelen (psf) ;
+
+		psf->datalength = psf->filelength - psf->dataoffset ;
+
+		if (psf->dataend)
+			psf->datalength -= psf->filelength - psf->dataend ;
+
+		if (psf->bytewidth > 0)
+			psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ;
+		} ;
+
+	if (psf->endian == SF_ENDIAN_BIG)
+		end_str = "10" ;
+	else if (psf->endian == SF_ENDIAN_LITTLE)
+		end_str = "01" ;
+	else
+		end_str = "error" ;
+
+	/* Clear the whole header. */
+	memset (psf->header.ptr, 0, psf->header.len) ;
+	psf->header.indx = 0 ;
+
+	psf_fseek (psf, 0, SEEK_SET) ;
+
+	psf_asciiheader_printf (psf, "NIST_1A\n   1024\n") ;
+	psf_asciiheader_printf (psf, "channel_count -i %d\n", psf->sf.channels) ;
+	psf_asciiheader_printf (psf, "sample_rate -i %d\n", psf->sf.samplerate) ;
+
+	switch (SF_CODEC (psf->sf.format))
+	{	case SF_FORMAT_PCM_S8 :
+				psf_asciiheader_printf (psf, "sample_coding -s3 pcm\n") ;
+				psf_asciiheader_printf (psf, "sample_n_bytes -i 1\n"
+											"sample_sig_bits -i 8\n") ;
+				break ;
+
+		case SF_FORMAT_PCM_16 :
+		case SF_FORMAT_PCM_24 :
+		case SF_FORMAT_PCM_32 :
+				psf_asciiheader_printf (psf, "sample_n_bytes -i %d\n", psf->bytewidth) ;
+				psf_asciiheader_printf (psf, "sample_sig_bits -i %d\n", psf->bytewidth * 8) ;
+				psf_asciiheader_printf (psf, "sample_coding -s3 pcm\n"
+								"sample_byte_format -s%d %s\n", psf->bytewidth, end_str) ;
+				break ;
+
+		case SF_FORMAT_ALAW :
+				psf_asciiheader_printf (psf, "sample_coding -s4 alaw\n") ;
+				psf_asciiheader_printf (psf, "sample_n_bytes -s1 1\n") ;
+				break ;
+
+		case SF_FORMAT_ULAW :
+				psf_asciiheader_printf (psf, "sample_coding -s4 ulaw\n") ;
+				psf_asciiheader_printf (psf, "sample_n_bytes -s1 1\n") ;
+				break ;
+
+		default : return SFE_UNIMPLEMENTED ;
+		} ;
+
+	psf->dataoffset = NIST_HEADER_LENGTH ;
+
+	/* Fix this */
+	samples = psf->sf.frames ;
+	psf_asciiheader_printf (psf, "sample_count -i %ld\n", samples) ;
+	psf_asciiheader_printf (psf, "end_head\n") ;
+
+	/* Zero fill to dataoffset. */
+	psf_binheader_writef (psf, "z", BHWz ((size_t) (NIST_HEADER_LENGTH - psf->header.indx))) ;
+
+	psf_fwrite (psf->header.ptr, psf->header.indx, 1, psf) ;
+
+	if (psf->error)
+		return psf->error ;
+
+	if (current > 0)
+		psf_fseek (psf, current, SEEK_SET) ;
+
+	return psf->error ;
+} /* nist_write_header */
+
diff --git a/src/nms_adpcm.c b/src/nms_adpcm.c
new file mode 100644
index 0000000..96d6ad2
--- /dev/null
+++ b/src/nms_adpcm.c
@@ -0,0 +1,1156 @@
+/*
+** Copyright (C) 1999-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2017 Arthur Taylor <art@ified.ca>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*
+** This is a Natural MicroSystems ADPCM encoder/decoder. It converts 14 bit linear
+** PCM to and from either a 2, 3, or 4 bit ADPCM. NMS-ADPCM does not have appeared
+** to have ever been publicly documented, and appears to have debuted in the early
+** 90s in the Natural Access suite of PC-based telephony products. Raw NMS ADPCM
+** files usually have a .vce extension, although this does not encode what bitrate
+** is used.
+**
+** NMS-ADPCM is an 'optimised variation' of the ITU G.726 ADPCM scheme. The dominant
+** variation is that it removes the tone (modem) operation mode, and it's associated
+** voice/modem transition detection. This simplifies the computation of the step
+** size multiplier, as all operations on it remain in a log domain.
+*/
+
+#include	"sfconfig.h"
+
+#include	<math.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+
+#define NMS_SAMPLES_PER_BLOCK 160
+#define NMS_BLOCK_SHORTS_32 41
+#define NMS_BLOCK_SHORTS_24 31
+#define NMS_BLOCK_SHORTS_16 21
+
+/* Variable names from ITU G.726 spec */
+struct nms_adpcm_state
+{	/* Log of the step size multiplier. Operated on by codewords. */
+	int yl ;
+
+	/* Quantizer step size multiplier. Generated from yl. */
+	int y ;
+
+	/* Coefficents of the pole predictor */
+	int a [2] ;
+
+	/* Coefficents of the zero predictor  */
+	int b [6] ;
+
+	/* Previous quantized deltas (multiplied by 2^14) */
+	int d_q [7] ;
+
+	/* d_q [x] + s_ez [x], used by the pole-predictor for signs only. */
+	int p [3] ;
+
+	/* Previous reconstructed signal values. */
+	int s_r [2] ;
+
+	/* Zero predictor components of the signal estimate. */
+	int s_ez ;
+
+	/* Signal estimate, (including s_ez). */
+	int s_e ;
+
+	/* The most recent codeword (enc:generated, dec:inputted) */
+	int Ik ;
+
+	int parity ;
+
+	/*
+	** Offset into code tables for the bitrate.
+	** 2-bit words: +0
+	** 3-bit words: +8
+	** 4-bit words: +16
+	*/
+	int t_off ;
+} ;
+
+enum nms_enc_type
+{	NMS16,
+	NMS24,
+	NMS32
+} ;
+
+typedef struct
+{	struct nms_adpcm_state state ;
+
+	/* The encoding type */
+	enum nms_enc_type type ;
+
+	int shortsperblock ;
+	int	blocks_total ;
+	int block_curr, sample_curr ;
+
+	unsigned short block [NMS_BLOCK_SHORTS_32] ;
+	short samples [NMS_SAMPLES_PER_BLOCK] ;
+} NMS_ADPCM_PRIVATE ;
+
+/* Pre-computed exponential interval used in the antilog approximation. */
+static unsigned int table_expn [] =
+{	0x4000, 0x4167, 0x42d5, 0x444c,	0x45cb, 0x4752, 0x48e2, 0x4a7a,
+	0x4c1b, 0x4dc7, 0x4f7a, 0x5138,	0x52ff, 0x54d1, 0x56ac, 0x5892,
+	0x5a82, 0x5c7e, 0x5e84, 0x6096,	0x62b4, 0x64dd, 0x6712, 0x6954,
+	0x6ba2, 0x6dfe, 0x7066, 0x72dc,	0x7560, 0x77f2, 0x7a93, 0x7d42,
+} ;
+
+/* Table mapping codewords to scale factor deltas. */
+static int table_scale_factor_step [] =
+{	0x0,	0x0,	0x0,	0x0,	0x4b0,	0x0,	0x0,	0x0,	/* 2-bit */
+	-0x3c,	0x0,	0x90,	0x0,	0x2ee,	0x0,	0x898,	0x0,	/* 3-bit */
+	-0x30,	0x12,	0x6b,	0xc8,	0x188,	0x2e0,	0x551,	0x1150,	/* 4-bit */
+} ;
+
+/* Table mapping codewords to quantized delta interval steps. */
+static unsigned int table_step [] =
+{	0x73F,	0,		0,		0,		0x1829,	0,		0,		0,		/* 2-bit */
+	0x3EB,	0,		0xC18,	0,		0x1581,	0,		0x226E,	0,		/* 3-bit */
+	0x20C,	0x635,	0xA83,	0xF12,	0x1418,	0x19E3,	0x211A,	0x2BBA,	/* 4-bit */
+} ;
+
+/* Binary search lookup table for quantizing using table_step. */
+static int table_step_search [] =
+{	0,		0x1F6D,	0,		-0x1F6D,	0,		0,			0,			0, /* 2-bit */
+	0x1008,	0x1192,	0,		-0x219A,	0x1656,	-0x1656,	0,			0, /* 3-bit */
+	0x872,	0x1277,	-0x8E6,	-0x232B,	0xD06,	-0x17D7,	-0x11D3,	0, /* 4-bit */
+} ;
+
+
+/*============================================================================================
+** Static functions.
+*/
+
+static void nms_adpcm_update (struct nms_adpcm_state *s) ;
+static void nms_adpcm_codec_init (struct nms_adpcm_state *s, enum nms_enc_type type) ;
+
+static int16_t nms_adpcm_reconstruct_sample (struct nms_adpcm_state *s, uint8_t I) ;
+static uint8_t nms_adpcm_encode_sample (struct nms_adpcm_state *s, int16_t sl) ;
+static int16_t nms_adpcm_decode_sample (struct nms_adpcm_state *s, uint8_t code) ;
+
+static void nms_adpcm_block_pack_16 (const int16_t codewords [], uint16_t block [], int16_t rms) ;
+static void nms_adpcm_block_pack_24 (const int16_t codewords [], uint16_t block [], int16_t rms) ;
+static void nms_adpcm_block_pack_32 (const int16_t codewords [], uint16_t block [], int16_t rms) ;
+
+static void nms_adpcm_block_unpack_16 (const uint16_t block [], int16_t codewords [], int16_t *rms) ;
+static void nms_adpcm_block_unpack_24 (const uint16_t block [], int16_t codewords [], int16_t *rms) ;
+static void nms_adpcm_block_unpack_32 (const uint16_t block [], int16_t codewords [], int16_t *rms) ;
+
+static int nms_adpcm_decode_block (SF_PRIVATE *psf, NMS_ADPCM_PRIVATE *pnms, uint16_t block [], int16_t samples []) ;
+static int nms_adpcm_encode_block (SF_PRIVATE *psf, NMS_ADPCM_PRIVATE *pnms, int16_t samples [], uint16_t block []) ;
+
+static sf_count_t nms_adpcm_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
+static sf_count_t nms_adpcm_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
+static sf_count_t nms_adpcm_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
+static sf_count_t nms_adpcm_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
+
+static sf_count_t nms_adpcm_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
+static sf_count_t nms_adpcm_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
+static sf_count_t nms_adpcm_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
+static sf_count_t nms_adpcm_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
+
+static int nms_adpcm_close (SF_PRIVATE *psf) ;
+static sf_count_t nms_adpcm_seek (SF_PRIVATE *psf, int mode, sf_count_t offset) ;
+
+/*
+** An exponential function (antilog) approximation.
+**
+** Maps [1,20480] to [1,1024] in an exponential relationship. This is
+** approximately ret = b^exp where b = e^(ln(1024)/ln(20480)) ~= 1.0003385
+*/
+static inline int
+nms_adpcm_antilog (int exp)
+{	int ret ;
+
+	ret = 0x1000 ;
+	ret += (((exp & 0x3f) * 0x166b) >> 12) ;
+	ret *= table_expn [(exp & 0x7c0) >> 6] ;
+	ret >>= (26 - (exp >> 11)) ;
+
+	return ret ;
+} /* nms_adpcm_antilog */
+
+static void
+nms_adpcm_update (struct nms_adpcm_state *s)
+{	/* Variable names from ITU G.726 spec */
+	int a1ul ;
+	int fa1 ;
+	int i ;
+
+	/* Decay and Modify the scale factor in the log domain based on the codeword. */
+	s->yl = ((s->yl *0xf8) >> 8) + table_scale_factor_step [s->t_off + (s->Ik & 7)] ;
+	if (s->yl < 2171)
+		s->yl = 2171 ;
+	else if (s->yl > 20480)
+		s->yl = 20480 ;
+	s->y = nms_adpcm_antilog (s->yl) ;
+
+	/* Update the zero predictor coefficents. */
+	for (i = 0 ; i < 6 ; i++)
+	{	s->b [i] = (s->b [i] * 0xff) >> 8 ;
+		if ((s->d_q [0] ^ s->d_q [i + 1]) >= 0)
+			s->b [i] += 128 ;
+		else
+			s->b [i] -= 128 ;
+		}
+
+	/* Update the pole predictor coefficents. */
+	fa1 = s->a [0] >> 5 ;
+	if (fa1 < -256)
+		fa1 = -256 ;
+	else if (fa1 > 256)
+		fa1 = 256 ;
+
+	s->a [0] = (0xff * s->a [0]) >> 8 ;
+	if (s->p [0] != 0 && s->p [1] != 0 && ((s->p [0] ^ s->p [1]) < 0))
+		s->a [0] -= 192 ;
+	else
+	{	s->a [0] += 192 ;
+		fa1 = -fa1 ;
+		}
+
+	s->a [1] = fa1 + ((0xfe * s->a [1]) >> 8) ;
+	if (s->p [0] != 0 && s->p [2] != 0 && ((s->p [0] ^ s->p [2]) < 0))
+		s->a [1] -= 128 ;
+	else
+		s->a [1] += 128 ;
+
+	/* Stability constraints. */
+	if (s->a [1] < -12288)
+		s->a [1] = -12288 ;
+	else if (s->a [1] > 12288)
+		s->a [1] = 12288 ;
+	a1ul = 15360 - s->a [1] ;
+	if (s->a [0] >= a1ul)
+		s->a [0] = a1ul ;
+	else
+	{	a1ul = -a1ul ;
+		if (s->a [0] < a1ul)
+			s->a [0] = a1ul ;
+		} ;
+
+	/* Compute the zero predictor estimate. Rotate past deltas too. */
+	s->s_ez = 0 ;
+	for (i = 5 ; i >= 0 ; i--)
+	{	s->s_ez += s->d_q [i] * s->b [i] ;
+		s->d_q [i + 1] = s->d_q [i] ;
+		} ;
+
+	/* Compute the signal estimate. */
+	s->s_e = s->a [0] * s->s_r [0] + s->a [1] * s->s_r [1] + s->s_ez ;
+
+	/* Return to scale */
+	s->s_ez >>= 14 ;
+	s->s_e >>= 14 ;
+
+	/* Rotate members to prepare for next iteration. */
+	s->s_r [1] = s->s_r [0] ;
+	s->p [2] = s->p [1] ;
+	s->p [1] = s->p [0] ;
+} /* nms_adpcm_update */
+
+
+static int16_t
+nms_adpcm_reconstruct_sample (struct nms_adpcm_state *s, uint8_t I)
+{	/* Variable names from ITU G.726 spec */
+	int dqx ;
+
+	/*
+	** The ordering of the 12-bit right-shift is a precision loss. It agrees
+	** with the output of a 16-bit NMSVCE.DLL, but disagrees with the output
+	** of a CG6565 board.
+	*/
+
+	/* Look up the delta, scale and sign it. */
+	dqx = table_step [s->t_off + (I & 7)] * s->y ;
+	if (I & 8)
+		dqx = -dqx ;
+
+	/* Take from delta scale to actual scale. */
+	dqx >>= 12 ;
+
+	/* Set variables used as input for the next predictor update. */
+	s->d_q [0] = dqx ;
+	s->s_r [0] = s->s_e + dqx ;
+	s->Ik = I & 0xf ;
+	s->p [0] = s->s_ez + dqx ;
+
+	return s->s_r [0] ;
+} /* nms_adpcm_reconstruct_sample */
+
+static void
+nms_adpcm_codec_init (struct nms_adpcm_state *s, enum nms_enc_type type)
+{	memset (s, 0, sizeof (struct nms_adpcm_state)) ;
+	s->t_off = (type == NMS32) ? 16 : (type == NMS24) ? 8 : 0 ;
+} /* nms_adpcm_codec_init */
+
+/*
+** nms_adpcm_encode_sample()
+**
+** Encode a linear 16-bit pcm sample into a 2,3, or 4 bit NMS-ADPCM codeword
+** using and updating the predictor state.
+*/
+static uint8_t
+nms_adpcm_encode_sample (struct nms_adpcm_state *s, int16_t sl)
+{	/* Variable names from ITU G.726 spec */
+	int d ;
+	uint8_t I ;
+
+	/* Down scale the sample from 16 => ~14 bits. */
+	sl = (sl * 0x1fdf) / 0x7fff ;
+
+	/* Compute estimate, and delta from actual value */
+	nms_adpcm_update (s) ;
+	d = sl - s->s_e ;
+
+	/*
+	** Vary the input signal. Not sure why. It agrees with NMSVCE.DLL and
+	** a CG6565 board.
+	*/
+	if (s->parity ^= 1)
+		d -= 2 ;
+
+	/* Encode the delta signed-ness (Codeword bit 4) */
+	if (d < 0)
+	{	d = -d ;
+		I = 8 ;
+		}
+	else
+		I = 0 ;
+
+	/* Increase magnitude to be in the range of the delta steps */
+	d <<= 13 ;
+
+	/* Quantize the delta using a binary search. */
+	d += table_step_search [s->t_off + 3] * s->y ;
+	/* Codeword bit 3 */
+	if (d >= 0)
+	{	d += table_step_search [s->t_off + 5] * s->y ;
+		/* Codeword bit 2 */
+		if (d >= 0)
+		{	d += table_step_search [s->t_off + 6] * s->y ;
+			/* Codeword bit 1 */
+			if (d >= 0)
+				I |= 7 ;
+			else
+				I |= 6 ;
+			}
+		else
+		{	d += table_step_search [s->t_off + 4] * s->y ;
+			/* Codeword bit 1 */
+			if (d >= 0)
+				I |= 5 ;
+			else
+				I |= 4 ;
+			} ;
+		}
+	else {
+		d += table_step_search [s->t_off + 1] * s->y ;
+		/* Codeword bit 2 */
+		if (d >= 0)
+		{	d += table_step_search [s->t_off + 2] * s->y ;
+			/* Codeword bit 1 */
+			if (d >= 0)
+				I |= 3 ;
+			else
+				I |= 2 ;
+			}
+		else {
+			d += table_step_search [s->t_off + 0] * s->y ;
+			/* Codeword bit 1 */
+			if (d >= 0)
+				I |= 1 ;
+			else
+				I |= 0 ;
+			} ;
+		} ;
+	/* What's left in d is actually our quantizer noise. */
+
+	/* Reduce the codeword size for the bitrate accordingly. */
+	if (s->t_off == 8)
+		I &= 0xe ;
+	else if (s->t_off == 0)
+		I &= 0xc ;
+
+	/* Call reconstruct for side effects preparing for the next update. */
+	nms_adpcm_reconstruct_sample (s, I) ;
+
+	return I ;
+} /* nms_adpcm_encode_sample */
+
+/*
+** nms_adpcm_decode_sample()
+**
+** Given a 2,3 or 4-bit NMS-ADPCM codeword, decode the next 16-bit linear PCM
+** sample using and updating the predictor state.
+*/
+static int16_t
+nms_adpcm_decode_sample (struct nms_adpcm_state *s, uint8_t I)
+{	int sl ;
+
+	nms_adpcm_update (s) ;
+	sl = nms_adpcm_reconstruct_sample (s, I) ;
+
+	/* Clamp to [-0x1fdf, 0x1fdf] (just under 14 bits resolution) */
+	if (sl < -0x1fdf)
+		sl = -0x1fdf ;
+	else if (sl > 0x1fdf)
+		sl = 0x1fdf ;
+
+	/* Expand from 14 to 16 bits */
+	sl = (sl * 0x7fff) / 0x1fdf ;
+
+	return (int16_t) sl ;
+} /* nms_adpcm_decode_sample */
+
+/**
+** NMS ADPCM Codeword packing scheme.
+**
+** The serialized form of NMS-ADPCM operates on blocks of 160 mono samples
+** (20ms at 8000Hz.) Blocks are 42, 62 and 82 bytes in size for the 2, 3, and
+** 4 bit codeword sizes respectively. The data is treated as an array of
+** little-endian 2-byte shorts, and the data is packed into the first 20, 30
+** or 40 shorts. The last short represents the block's root-mean-square
+** average. This is apparently an optimization so that energy/silence
+** detection processes can avoid decoding a block.
+**
+** All codewords are nibbles, with the least significant bits dropped as
+** required for the 3 and 2 bit codeword sizes.
+**
+** Nibbles are packed into shorts in order of most significant to least. The
+** 4-bit scheme is trivial. The three bit scheme reconstructs a fourth sample
+** from the leftover bits of the proceeding three samples. The 2-bit scheme
+** uses a two-pass, left two bit shift.
+*/
+
+/*
+** Reads 21 shorts from block, unpacks 160 codewords of 2-bits each, writing
+** each to its sequential array index of codewords. If rms is non-null, the
+** read block rms is copied to its location.
+*/
+static void
+nms_adpcm_block_unpack_16 (const uint16_t block [], int16_t codewords [], int16_t *rms)
+{	int k ;
+	uint16_t w = 0 ;
+
+	for (k = 0 ; k < NMS_SAMPLES_PER_BLOCK ; )
+	{	/*
+		** k % 8 == [0-3]: Top 2-bits of a nibble
+		** k % 8 == [4-7]: Bottom 2-bits of a nibble
+		*/
+		if ((k & 4) == 0)
+			w = *(block++) ;
+		else
+			w <<= 2 ;
+		codewords [k++] = (w >> 12) & 0xc ;
+		codewords [k++] = (w >> 8) & 0xc ;
+		codewords [k++] = (w >> 4) & 0xc ;
+		codewords [k++] = w & 0xc ;
+		} ;
+
+	/*
+	** Every block ends with a short representing a RMS-approximation for the
+	** block.
+	**/
+	if (rms)
+		*rms = *block ;
+} /* nms_adpcm_unpack_16 */
+
+/*
+** Reads 31 shorts from block, unpacks 160 codewords of 3-bits each, writing
+** each to its sequential array index of codewords. If rms is non-null, the
+** read block rms is copied to its location.
+*/
+static void
+nms_adpcm_block_unpack_24 (const uint16_t block [], int16_t codewords [], int16_t *rms)
+{	int k ;
+	uint16_t w = 0, residual = 0 ;
+
+	for (k = 0 ; k < NMS_SAMPLES_PER_BLOCK ; )
+	{		/*
+		** k % 16 == [0, 11]: Unpack new nibble, build residual
+		** k % 16 == [12, 15]: Unpack residual
+		*/
+		if ((k & 12) != 12)
+		{	w = *(block++) ;
+			residual = (residual << 1) | (w & 0x1111) ;
+			}
+		else
+		{	w = residual << 1 ;
+			residual = 0 ;
+			} ;
+		codewords [k++] = (w >> 12) & 0xe ;
+		codewords [k++] = (w >> 8) & 0xe ;
+		codewords [k++] = (w >> 4) & 0xe ;
+		codewords [k++] = w & 0xe ;
+		} ;
+
+	/*
+	** Every block ends with a short representing a RMS-approximation for the
+	** block.
+	**/
+	if (rms)
+		*rms = *block ;
+} /* nms_adpcm_unpack_24 */
+
+/*
+** Reads 41 shorts from block, unpacks 160 codewords of 4-bits each, writing
+** each to its sequential array index of codewords. If rms is non-null, the
+** read block rms is copied to its location.
+*/
+static void
+nms_adpcm_block_unpack_32 (const uint16_t block [], int16_t codewords [], int16_t *rms)
+{	int k ;
+	uint16_t w = 0 ;
+
+	for (k = 0 ; k < NMS_SAMPLES_PER_BLOCK ; )
+	{	w = *(block++) ;
+		codewords [k++] = (w >> 12) & 0xf ;
+		codewords [k++] = (w >> 8) & 0xf ;
+		codewords [k++] = (w >> 4) & 0xf ;
+		codewords [k++] = w & 0xf ;
+		} ;
+	/*
+	** Every block ends with a short representing a RMS-approximation for the
+	** block.
+	**/
+	if (rms)
+		*rms = *block ;
+} /* nms_adpcm_unpack_32 */
+
+/*
+** Reads 160 indicies of codewords for one 2-bit codeword each, packing them
+** into 20 shorts of block, and writes the short rms for a total of 42 bytes.
+*/
+static void
+nms_adpcm_block_pack_16 (const int16_t codewords [], uint16_t block [], int16_t rms)
+{	int k ;
+	uint16_t w ;
+
+	for (k = 0 ; k < NMS_SAMPLES_PER_BLOCK ; )
+	{	w = codewords [k++] << 12 ;
+		w |= codewords [k++] << 8 ;
+		w |= codewords [k++] << 4 ;
+		w |= codewords [k++] ;
+		w |= codewords [k++] << 10 ;
+		w |= codewords [k++] << 6 ;
+		w |= codewords [k++] << 2 ;
+		w |= codewords [k++] >> 2 ;
+
+		*(block++) = w ;
+		} ;
+
+	/* Every block ends with a short representing the blocks RMS */
+	*block = rms ;
+} /* nms_adpcm_pack_16 */
+
+/*
+** Reads 160 indicies of codewords for one 3-bit codeword each, packing them
+** into 30 shorts of block, and writes the short rms for a total of 62 bytes.
+*/
+static void
+nms_adpcm_block_pack_24 (const int16_t codewords [], uint16_t block [], int16_t rms)
+{	int k ;
+	uint16_t w [3] ;
+	uint16_t residual ;
+
+	for (k = 0 ; k < NMS_SAMPLES_PER_BLOCK ; )
+	{	w [0] = codewords [k++] << 12 ;
+		w [0] |= codewords [k++] << 8 ;
+		w [0] |= codewords [k++] << 4 ;
+		w [0] |= codewords [k++] ;
+
+		w [1] = codewords [k++] << 12 ;
+		w [1] |= codewords [k++] << 8 ;
+		w [1] |= codewords [k++] << 4 ;
+		w [1] |= codewords [k++] ;
+
+		w [2] = codewords [k++] << 12 ;
+		w [2] |= codewords [k++] << 8 ;
+		w [2] |= codewords [k++] << 4 ;
+		w [2] |= codewords [k++] ;
+
+		residual = codewords [k++] << 12 ;
+		residual |= codewords [k++] << 8 ;
+		residual |= codewords [k++] << 4 ;
+		residual |= codewords [k++] ;
+
+		residual >>= 1 ;
+		w [2] |= (residual & 0x1111) ;
+		residual >>= 1 ;
+		w [1] |= (residual & 0x1111) ;
+		residual >>= 1 ;
+		w [0] |= (residual & 0x1111) ;
+
+		*(block++) = w [0] ;
+		*(block++) = w [1] ;
+		*(block++) = w [2] ;
+		} ;
+
+	/* Every block ends with a short representing the blocks RMS */
+	*block = rms ;
+} /* nms_adpcm_pack_24 */
+
+/*
+** Reads 160 indicies of codewords for one 4-bit codeword each, packing them
+** into 40 shorts of block, and writes the short rms for a total of 82 bytes.
+*/
+static void
+nms_adpcm_block_pack_32 (const int16_t codewords [], uint16_t block [], int16_t rms)
+{	int k ;
+	uint16_t w ;
+
+	for (k = 0 ; k < NMS_SAMPLES_PER_BLOCK ; )
+	{	w = codewords [k++] << 12 ;
+		w |= codewords [k++] << 8 ;
+		w |= codewords [k++] << 4 ;
+		w |= codewords [k++] ;
+
+		*(block++) = w ;
+		} ;
+
+	/* Every block ends with a short representing the blocks RMS */
+	*block = rms ;
+} /*nms_adpcm_block_pack_32 */
+
+static int
+nms_adpcm_decode_block (SF_PRIVATE *psf, NMS_ADPCM_PRIVATE *pnms, uint16_t block [], int16_t samples [])
+{	int k ;
+
+	switch (pnms->type)
+	{	case NMS16 :
+			nms_adpcm_block_unpack_16 (block, samples, NULL) ;
+			break ;
+		case NMS24 :
+			nms_adpcm_block_unpack_24 (block, samples, NULL) ;
+			break ;
+		case NMS32 :
+			nms_adpcm_block_unpack_32 (block, samples, NULL) ;
+			break ;
+
+		default :
+			psf_log_printf (psf, "*** Error : Unhandled NMS ADPCM type %d.\n", pnms->type) ;
+			return 0 ;
+		} ;
+
+	for (k = 0 ; k < NMS_SAMPLES_PER_BLOCK ; k++)
+		samples [k] = nms_adpcm_decode_sample (&pnms->state, samples [k]) ;
+
+	return NMS_SAMPLES_PER_BLOCK ;
+} /* nms_adpcm_decode_block */
+
+static int
+nms_adpcm_encode_block (SF_PRIVATE *psf, NMS_ADPCM_PRIVATE *pnms, int16_t samples [], uint16_t block [])
+{	int k ;
+	unsigned int rms = 0 ;
+
+	/*
+	** The rms we write is a complete lie. Considering that the various
+	** other implementations I've tested don't completely agree, that this data
+	** is usually ignored, and except for some weird offloading of "energy
+	** detection", so long as we don't write zeros for non-zero data, I don't
+	** think it really matters.
+	*/
+
+	for (k = 0 ; k < NMS_SAMPLES_PER_BLOCK ; k++)
+	{	rms += (samples [k] * samples [k]) >> 2 ;
+		samples [k] = nms_adpcm_encode_sample (&pnms->state, samples [k]) ;
+		} ;
+
+	rms <<= 12 ;
+	switch (pnms->type)
+	{	case NMS16 :
+			nms_adpcm_block_pack_16 (samples, block, rms) ;
+			break ;
+		case NMS24 :
+			nms_adpcm_block_pack_24 (samples, block, rms) ;
+			break ;
+		case NMS32 :
+			nms_adpcm_block_pack_32 (samples, block, rms) ;
+			break ;
+
+		default :
+			psf_log_printf (psf, "*** Error : Unhandled NMS ADPCM type %d.\n", pnms->type) ;
+			return 0 ;
+		} ;
+
+	return NMS_SAMPLES_PER_BLOCK ;
+} /* nms_adpcm_encode_block */
+
+static int
+psf_nms_adpcm_decode_block (SF_PRIVATE *psf, NMS_ADPCM_PRIVATE *pnms)
+{	int k ;
+
+	if ((k = (int) psf_fread (pnms->block, sizeof (short), pnms->shortsperblock, psf)) != pnms->shortsperblock)
+	{	psf_log_printf (psf, "*** Warning : short read (%d != %d).\n", k, pnms->shortsperblock) ;
+		memset (pnms->block + (k * sizeof (short)), 0, (pnms->shortsperblock - k) * sizeof (short)) ;
+		} ;
+
+	if (CPU_IS_BIG_ENDIAN)
+		endswap_short_array ((signed short *) pnms->block, pnms->shortsperblock) ;
+
+	nms_adpcm_decode_block (psf, pnms, pnms->block, pnms->samples) ;
+
+	return 1 ;
+} /* nms_adpcm_decode_block */
+
+static int
+nms_adpcm_read_block (SF_PRIVATE *psf, NMS_ADPCM_PRIVATE *pnms, short *ptr, int len)
+{	int	count, indx = 0 ;
+
+	while (indx < len)
+	{	if (pnms->sample_curr >= NMS_SAMPLES_PER_BLOCK)
+		{	pnms->block_curr ++ ;
+			pnms->sample_curr = 0 ;
+			} ;
+
+		if (pnms->block_curr > pnms->blocks_total)
+		{	memset (&(ptr [indx]), 0, (len - indx) * sizeof (short)) ;
+			return indx ;
+			} ;
+
+		if (pnms->sample_curr == 0)
+			psf_nms_adpcm_decode_block (psf, pnms) ;
+
+		count = NMS_SAMPLES_PER_BLOCK - pnms->sample_curr ;
+		if (len - indx < count)
+			count = len - indx ;
+
+		memcpy (&(ptr [indx]), &(pnms->samples [pnms->sample_curr]), count * sizeof (short)) ;
+		indx += count ;
+		pnms->sample_curr += count ;
+		} ;
+
+	return indx ;
+} /* nms_adpcm_read_block */
+
+static sf_count_t
+nms_adpcm_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len)
+{	NMS_ADPCM_PRIVATE 	*pnms ;
+	int					readcount, count ;
+	sf_count_t			total = 0 ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	pnms = (NMS_ADPCM_PRIVATE*) psf->codec_data ;
+
+	while (len > 0)
+	{	readcount = (len > 0x10000000) ? 0x10000000 : (int) len ;
+
+		count = nms_adpcm_read_block (psf, pnms, ptr, readcount) ;
+
+		total += count ;
+		len -= count ;
+
+		if (count != readcount)
+			break ;
+		} ;
+
+	return total ;
+} /* nms_adpcm_read_s */
+
+static sf_count_t
+nms_adpcm_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	NMS_ADPCM_PRIVATE *pnms ;
+	short		*sptr ;
+	int			k, bufferlen, readcount = 0, count ;
+	sf_count_t	total = 0 ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	pnms = (NMS_ADPCM_PRIVATE *) psf->codec_data ;
+
+	sptr = ubuf.sbuf ;
+	bufferlen = SF_BUFFER_LEN / sizeof (short) ;
+	while (len > 0)
+	{	readcount = (len >= bufferlen) ? bufferlen : (int) len ;
+		count = nms_adpcm_read_block (psf, pnms, sptr, readcount) ;
+
+		for (k = 0 ; k < readcount ; k++)
+			ptr [total + k] = arith_shift_left (sptr [k], 16) ;
+
+		total += count ;
+		len -= readcount ;
+		if (count != readcount)
+			break ;
+		} ;
+
+	return total ;
+} /* nms_adpcm_read_i */
+
+static sf_count_t
+nms_adpcm_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	NMS_ADPCM_PRIVATE *pnms ;
+	short		*sptr ;
+	int			k, bufferlen, readcount = 0, count ;
+	sf_count_t	total = 0 ;
+	float 		normfact ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	pnms = (NMS_ADPCM_PRIVATE*) psf->codec_data ;
+
+	normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x8000) : 1.0 ;
+
+	sptr = ubuf.sbuf ;
+	bufferlen = SF_BUFFER_LEN / sizeof (short) ;
+	while (len > 0)
+	{	readcount = (len >= bufferlen) ? bufferlen : (int) len ;
+		count = nms_adpcm_read_block (psf, pnms, sptr, readcount) ;
+		for (k = 0 ; k < readcount ; k++)
+			ptr [total + k] = normfact * sptr [k] ;
+
+		total += count ;
+		len -= readcount ;
+		if (count != readcount)
+			break ;
+		} ;
+
+	return total ;
+} /* nms_adpcm_read_f */
+
+static sf_count_t
+nms_adpcm_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	NMS_ADPCM_PRIVATE *pnms ;
+	short		*sptr ;
+	int			k, bufferlen, readcount = 0, count ;
+	sf_count_t	total = 0 ;
+	double		normfact ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	pnms = (NMS_ADPCM_PRIVATE*) psf->codec_data ;
+
+	normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((double) 0x8000) : 1.0 ;
+
+	sptr = ubuf.sbuf ;
+	bufferlen = SF_BUFFER_LEN / sizeof (short) ;
+	while (len > 0)
+	{	readcount = (len >= bufferlen) ? bufferlen : (int) len ;
+		count = nms_adpcm_read_block (psf, pnms, sptr, readcount) ;
+		for (k = 0 ; k < readcount ; k++)
+			ptr [total + k] = normfact * (double) (sptr [k]) ;
+
+		total += count ;
+		len -= readcount ;
+		if (count != readcount)
+			break ;
+		} ;
+
+	return total ;
+} /* nms_adpcm_read_d */
+
+static int
+psf_nms_adpcm_encode_block (SF_PRIVATE *psf, NMS_ADPCM_PRIVATE *pnms)
+{	int k ;
+
+	/* Encode the samples. */
+	nms_adpcm_encode_block (psf, pnms, pnms->samples, pnms->block) ;
+
+	if (CPU_IS_BIG_ENDIAN)
+		endswap_short_array ((signed short *) pnms->block, pnms->shortsperblock) ;
+
+	/* Write the block to disk. */
+	if ((k = (int) psf_fwrite (pnms->block, sizeof (short), pnms->shortsperblock, psf)) != pnms->shortsperblock)
+		psf_log_printf (psf, "*** Warning : short write (%d != %d).\n", k, pnms->shortsperblock) ;
+
+	pnms->sample_curr = 0 ;
+	pnms->block_curr ++ ;
+
+	return 1 ;
+} /* psf_nms_adpcm_encode_block */
+
+static int
+nms_adpcm_write_block (SF_PRIVATE *psf, NMS_ADPCM_PRIVATE *pnms, const short *ptr, int len)
+{	int	count, total = 0, indx = 0 ;
+
+	while (indx < len)
+	{	count = NMS_SAMPLES_PER_BLOCK - pnms->sample_curr ;
+
+		if (count > len - indx)
+			count = len - indx ;
+
+		memcpy (&(pnms->samples [pnms->sample_curr]), &(ptr [indx]), count * sizeof (short)) ;
+		indx += count ;
+		pnms->sample_curr += count ;
+		total = indx ;
+
+		if (pnms->sample_curr >= NMS_SAMPLES_PER_BLOCK)
+			psf_nms_adpcm_encode_block (psf, pnms) ;
+		} ;
+
+	return total ;
+} /* nms_adpcm_write_block */
+
+static sf_count_t
+nms_adpcm_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len)
+{	NMS_ADPCM_PRIVATE 	*pnms ;
+	int			writecount, count ;
+	sf_count_t	total = 0 ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	pnms = (NMS_ADPCM_PRIVATE*) psf->codec_data ;
+
+	while (len > 0)
+	{	writecount = (len > 0x10000000) ? 0x10000000 : (int) len ;
+
+		count = nms_adpcm_write_block (psf, pnms, ptr, writecount) ;
+
+		total += count ;
+		len -= count ;
+		if (count != writecount)
+			break ;
+		} ;
+
+	return total ;
+} /* nms_adpcm_write_s */
+
+static sf_count_t
+nms_adpcm_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	NMS_ADPCM_PRIVATE *pnms ;
+	short		*sptr ;
+	int			k, bufferlen, writecount = 0, count ;
+	sf_count_t	total = 0 ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	pnms = (NMS_ADPCM_PRIVATE*) psf->codec_data ;
+
+	sptr = ubuf.sbuf ;
+	bufferlen = SF_BUFFER_LEN / sizeof (short) ;
+	while (len > 0)
+	{	writecount = (len >= bufferlen) ? bufferlen : (int) len ;
+		for (k = 0 ; k < writecount ; k++)
+			sptr [k] = ptr [total + k] >> 16 ;
+		count = nms_adpcm_write_block (psf, pnms, sptr, writecount) ;
+
+		total += count ;
+		len -= writecount ;
+		if (count != writecount)
+			break ;
+		} ;
+	return total ;
+} /* nms_adpcm_write_i */
+
+static sf_count_t
+nms_adpcm_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	NMS_ADPCM_PRIVATE *pnms ;
+	short		*sptr ;
+	int			k, bufferlen, writecount = 0, count ;
+	sf_count_t	total = 0 ;
+	float		normfact ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	pnms = (NMS_ADPCM_PRIVATE*) psf->codec_data ;
+
+	normfact = (psf->norm_float == SF_TRUE) ? (1.0 * 0x8000) : 1.0 ;
+
+	sptr = ubuf.sbuf ;
+	bufferlen = SF_BUFFER_LEN / sizeof (short) ;
+	while (len > 0)
+	{	writecount = (len >= bufferlen) ? bufferlen : (int) len ;
+		for (k = 0 ; k < writecount ; k++)
+			sptr [k] = psf_lrintf (normfact * ptr [total + k]) ;
+		count = nms_adpcm_write_block (psf, pnms, sptr, writecount) ;
+
+		total += count ;
+		len -= writecount ;
+		if (count != writecount)
+			break ;
+		} ;
+
+	return total ;
+} /* nms_adpcm_write_f */
+
+static sf_count_t
+nms_adpcm_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	NMS_ADPCM_PRIVATE *pnms ;
+	short		*sptr ;
+	int			k, bufferlen, writecount = 0, count ;
+	sf_count_t	total = 0 ;
+	double		normfact ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	pnms = (NMS_ADPCM_PRIVATE*) psf->codec_data ;
+
+	normfact = (psf->norm_double == SF_TRUE) ? (1.0 * 0x8000) : 1.0 ;
+
+	sptr = ubuf.sbuf ;
+	bufferlen = SF_BUFFER_LEN / sizeof (short) ;
+	while (len > 0)
+	{	writecount = (len >= bufferlen) ? bufferlen : (int) len ;
+		for (k = 0 ; k < writecount ; k++)
+			sptr [k] = psf_lrint (normfact * ptr [total + k]) ;
+		count = nms_adpcm_write_block (psf, pnms, sptr, writecount) ;
+
+		total += count ;
+		len -= writecount ;
+		if (count != writecount)
+			break ;
+		} ;
+
+	return total ;
+} /* nms_adpcm_write_d */
+
+int
+nms_adpcm_init (SF_PRIVATE *psf)
+{	NMS_ADPCM_PRIVATE	*pnms ;
+
+	if (psf->codec_data != NULL)
+	{	psf_log_printf (psf, "*** psf->codec_data is not NULL.\n") ;
+		return SFE_INTERNAL ;
+		} ;
+
+	psf->sf.seekable = SF_FALSE ;
+
+	if (psf->sf.channels != 1)
+		return SFE_NMS_ADPCM_NOT_MONO ;
+
+	if ((pnms = calloc (1, sizeof (NMS_ADPCM_PRIVATE))) == NULL)
+		return SFE_MALLOC_FAILED ;
+
+	psf->codec_data = (void*) pnms ;
+
+	pnms->block_curr = 0 ;
+	pnms->sample_curr = 0 ;
+
+	switch (SF_CODEC (psf->sf.format))
+	{	case SF_FORMAT_NMS_ADPCM_16 :
+					pnms->type = NMS16 ;
+					pnms->shortsperblock = NMS_BLOCK_SHORTS_16 ;
+					break ;
+		case SF_FORMAT_NMS_ADPCM_24 :
+					pnms->type = NMS24 ;
+					pnms->shortsperblock = NMS_BLOCK_SHORTS_24 ;
+					break ;
+		case SF_FORMAT_NMS_ADPCM_32 :
+					pnms->type = NMS32 ;
+					pnms->shortsperblock = NMS_BLOCK_SHORTS_32 ;
+					break ;
+
+		default : return SFE_UNIMPLEMENTED ;
+	} ;
+	nms_adpcm_codec_init (&pnms->state, pnms->type) ;
+
+	psf->filelength = psf_get_filelen (psf) ;
+	if (psf->filelength < psf->dataoffset)
+		psf->filelength = psf->dataoffset ;
+
+	psf->datalength = psf->filelength - psf->dataoffset ;
+	if (psf->dataend > 0)
+		psf->datalength -= psf->filelength - psf->dataend ;
+
+	if (psf->file.mode == SFM_READ)
+	{	psf->read_short		= nms_adpcm_read_s ;
+		psf->read_int		= nms_adpcm_read_i ;
+		psf->read_float		= nms_adpcm_read_f ;
+		psf->read_double	= nms_adpcm_read_d ;
+		}
+	else if (psf->file.mode == SFM_WRITE)
+	{	psf->write_short	= nms_adpcm_write_s ;
+		psf->write_int		= nms_adpcm_write_i ;
+		psf->write_float	= nms_adpcm_write_f ;
+		psf->write_double	= nms_adpcm_write_d ;
+		} ;
+
+	if (psf->datalength % (pnms->shortsperblock * sizeof (short)))
+	{	psf_log_printf (psf, "*** Odd psf->datalength (%D) should be a multiple of %d\n",
+						psf->datalength, pnms->shortsperblock * sizeof (short)) ;
+		pnms->blocks_total = (psf->datalength / (pnms->shortsperblock * sizeof (short))) + 1 ;
+		}
+	else
+		pnms->blocks_total = psf->datalength / (pnms->shortsperblock * sizeof (short)) ;
+
+	psf->sf.frames		= pnms->blocks_total * NMS_SAMPLES_PER_BLOCK ;
+	psf->codec_close	= nms_adpcm_close ;
+	psf->seek			= nms_adpcm_seek ;
+
+	return 0 ;
+} /* nms_adpcm_init */
+
+static int
+nms_adpcm_close (SF_PRIVATE *psf)
+{	NMS_ADPCM_PRIVATE *pnms ;
+
+	pnms = (NMS_ADPCM_PRIVATE*) psf->codec_data ;
+
+	/*
+	** If a block has been partially assembled, write it out as the final
+	** block.
+	*/
+	if (psf->file.mode == SFM_WRITE)
+	{	if (pnms->sample_curr && pnms->sample_curr < NMS_SAMPLES_PER_BLOCK)
+		{	memset (pnms->samples + pnms->sample_curr, 0, (NMS_SAMPLES_PER_BLOCK - pnms->sample_curr) * sizeof (short)) ;
+			psf_nms_adpcm_encode_block (psf, pnms) ;
+			}
+
+		if (psf->write_header)
+			psf->write_header (psf, SF_FALSE) ;
+		}
+
+	return 0 ;
+} /* nms_adpcm_close */
+
+static sf_count_t
+nms_adpcm_seek (SF_PRIVATE *psf, int mode, sf_count_t offset)
+{	NMS_ADPCM_PRIVATE *pnms ;
+
+	pnms = (NMS_ADPCM_PRIVATE *) psf->codec_data ;
+
+	/*
+	** NMS ADPCM is symmetric, so transitioning from reading and writing is
+	** possible, but unimplemented, as it would require syncing partial blocks.
+	*/
+	if (mode != psf->file.mode)
+	{	psf->error = SFE_BAD_SEEK ;
+		return PSF_SEEK_ERROR ;
+		} ;
+
+	/*
+	** NMS ADPCM cannot be seek'ed, as codec state depends on previous samples,
+	** so only a seek to 0 is supported.
+	*/
+	if (offset != 0)
+	{	psf->error = SFE_BAD_SEEK ;
+		return PSF_SEEK_ERROR ;
+		} ;
+
+	if (psf_fseek (psf, psf->dataoffset, SEEK_SET) == PSF_SEEK_ERROR)
+			return PSF_SEEK_ERROR ;
+
+	nms_adpcm_codec_init (&pnms->state, pnms->type) ;
+	pnms->block_curr = 0 ;
+	pnms->sample_curr = 0 ;
+	return 0 ;
+} /* nms_adpcm_seek */
+
diff --git a/src/ogg.c b/src/ogg.c
new file mode 100644
index 0000000..8cd4379
--- /dev/null
+++ b/src/ogg.c
@@ -0,0 +1,921 @@
+/*
+** Copyright (C) 2002-2019 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2007 John ffitch
+** Copyright (C) 2018 Arthur Taylor <art@ified.ca>
+**
+** This program is free software ; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation ; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program ; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*
+** This file contains code based on OpusFile and Opus-Tools, both by
+** Xiph.Org. COPYING from each is identical and is as follows:
+**
+** Copyright (c) 1994-2013 Xiph.Org Foundation and contributors
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+**
+** - Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+**
+** - Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** - Neither the name of the Xiph.Org Foundation nor the names of its
+** contributors may be used to endorse or promote products derived from
+** this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION
+** OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include <math.h>
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#else
+#include "sf_unistd.h"
+#endif
+
+#include "sndfile.h"
+#include "sfendian.h"
+#include "common.h"
+
+#if HAVE_EXTERNAL_XIPH_LIBS
+
+#include <ogg/ogg.h>
+
+#include "ogg.h"
+
+#define OGG_SYNC_READ_SIZE (2048)
+#define OGG_PAGE_SIZE_MAX (65307)
+#define OGG_CHUNK_SIZE (65536)
+#define OGG_CHUNK_SIZE_MAX (1024*1024)
+
+/*
+ * The Ogg container may seem overly complicated, particularly when used for a
+ * on-disk audio file format. This is probably because Ogg is designed with
+ * streaming rather than storage as a priority, and can handle multiple codec
+ * payloads multiplexed together, then possibly chained on top of that.
+ * Ogg achieves its goals well, but it does lend to a bit of a learning curve,
+ * with many internal structures to push data around in compared to most sound
+ * file formats which only have a header and raw data.
+ *
+ * See
+ *  - [https://xiph.org/ogg/doc/oggstream.html]
+ *  - [https://xiph.org/ogg/doc/framing.html]
+ *
+ * libogg Memory Management
+ * ===========================================================================
+ *
+ * libOgg's memory management is documented in code, not in headers or external
+ * documentation. What follows is not an attempt to completely document it, but
+ * an explanation of the basics.
+ *
+ * libOgg has two data structures which allocate and manage data buffers: The
+ * ogg_sync_state structure and the ogg_stream_state structure. The remaining
+ * structures of ogg_page and ogg_packet are views into the buffers managed by
+ * the previous structures.
+ *
+ * ogg_sync_state is used for reading purposes. It takes a physical bitstream
+ * and searches for, validates, and returns complete Ogg Pages. The
+ * ogg_sync_state buffers the returned page data, holding at most one
+ * complete page at a time. A returned Ogg page remains valid until any
+ * operation other than ogg_sync_check() is called.
+ *
+ * ogg_stream_state is used for both reading and writing. For reading, the
+ * contents of an ogg_page is copied into the stream state. This data is
+ * buffered to be split or joined as necessary into complete ogg_packets. If,
+ * after copying an ogg_page into an ogg_stream_state, packets are available to
+ * be read, then all of those packets remain in memory and valid until either
+ * the ogg_stream_state is reset, destroyed, or a new ogg_page is read into it.
+ * As the maximum number of packets an Ogg Page may contain is 255, at most 255
+ * packets may be available from an ogg_stream_state at one time.
+ *
+ * For writing, the life cycle of a buffer pointed to by a ogg_packet is the
+ * responsibility of the caller. Packets written into an ogg_stream_state are
+ * buffered until a complete page is ready for writing. Pages for writing out
+ * remain in the ogg_stream_state's buffer and valid until either the
+ * ogg_stream_state is reset, cleared, destroyed. Writing another packet into
+ * the ogg_stream_state might also invalidate such pages, but writing in
+ * packets when a page is ready to be written out is a caller bug anyways.
+ */
+
+/*-----------------------------------------------------------------------------------------------
+** Private function prototypes.
+*/
+
+static int		ogg_close (SF_PRIVATE *psf) ;
+static int		ogg_stream_classify (SF_PRIVATE *psf, OGG_PRIVATE * odata) ;
+static int		ogg_page_classify (SF_PRIVATE * psf, const ogg_page * og) ;
+static uint64_t	ogg_page_search_do_rescale (uint64_t x, uint64_t from, uint64_t to) ;
+static void		ogg_page_search_continued_data (OGG_PRIVATE *odata, ogg_page *page) ;
+
+/*-----------------------------------------------------------------------------------------------
+** Exported functions.
+*/
+
+int
+ogg_read_first_page (SF_PRIVATE *psf, OGG_PRIVATE *odata)
+{	int ret ;
+	char *buffer ;
+
+	/*
+	** The ogg standard requires that the first pages of a physical ogg
+	** bitstream be only the first pages of each logical bitstream. These
+	** pages MUST have the Beginning-Of-Stream bit set, and must contain
+	** only the stream's relevant header. Currently we only load the first
+	** page and check that it contains a codec we support as supporting
+	** multiplexed streams (video+audio(en)+audio(fs)+subtitles, etc) is
+	** beyond the scope of this library.
+	*/
+
+	ret = ogg_sync_fseek (psf, psf->header.indx, SEEK_SET) ;
+	if (ret < 0)
+		return SFE_NOT_SEEKABLE ;
+
+	buffer = ogg_sync_buffer (&odata->osync, psf->header.indx) ;
+	if (buffer == NULL)
+		return SFE_MALLOC_FAILED ;
+	memcpy (buffer, psf->header.ptr, psf->header.indx) ;
+	ogg_sync_wrote (&odata->osync, psf->header.indx) ;
+
+	ret = ogg_sync_next_page (psf, &odata->opage, SF_MAX ((sf_count_t) 0, 4096 - psf->header.indx), NULL) ;
+
+	/* Have we simply run out of data?  If so, we're done. */
+	if (ret == 0)
+		return 0 ;
+	if (ret < 0)
+		return psf->error ;
+
+	if (!ogg_page_bos (&odata->opage))
+	{	/*
+		** Error case. Either must not be an Ogg bitstream, or is in the
+		** middle of a bitstream (live capture), or in the middle of a
+		** bitstream and no complete page was in the buffer.
+		*/
+		psf_log_printf (psf, "Input does not appear to be the start of an Ogg bitstream.\n") ;
+		return SFE_MALFORMED_FILE ;
+		} ;
+
+	/*
+	**	Get the serial number and set up the rest of decode.
+	**	Serialno first ; use it to set up a logical stream.
+	*/
+	ogg_stream_reset_serialno (&odata->ostream, ogg_page_serialno (&odata->opage)) ;
+
+	if (ogg_stream_pagein (&odata->ostream, &odata->opage) < 0)
+	{	/* Error ; stream version mismatch perhaps. */
+		psf_log_printf (psf, "Error reading first page of Ogg bitstream data\n") ;
+		return SFE_MALFORMED_FILE ;
+		} ;
+
+	if (ogg_stream_packetout (&odata->ostream, &odata->opacket) != 1)
+	{	/* No page? */
+		psf_log_printf (psf, "Error reading initial header page packet.\n") ;
+		return SFE_MALFORMED_FILE ;
+		} ;
+
+	return 0 ;
+} /* ogg_read_first_page */
+
+int
+ogg_write_page (SF_PRIVATE *psf, ogg_page *page)
+{	int bytes ;
+
+	bytes = psf_fwrite (page->header, 1, page->header_len, psf) ;
+	bytes += psf_fwrite (page->body, 1, page->body_len, psf) ;
+
+	return bytes == page->header_len + page->body_len ;
+} /* ogg_write_page */
+
+sf_count_t
+ogg_sync_ftell (SF_PRIVATE *psf)
+{	OGG_PRIVATE* odata = (OGG_PRIVATE *) psf->container_data ;
+	sf_count_t position ;
+
+	position = psf_ftell (psf) ;
+	if (position >= 0)
+	{	/* success */
+		if (position < odata->osync.fill)
+		{	/* Really, this should be an assert. */
+			psf->error = SFE_INTERNAL ;
+			return -1 ;
+			}
+		position += (sf_count_t) (odata->osync.returned - odata->osync.fill) ;
+		}
+
+	return position ;
+} /* ogg_sync_ftell */
+
+sf_count_t
+ogg_sync_fseek (SF_PRIVATE *psf, sf_count_t offset, int whence)
+{	OGG_PRIVATE* odata = (OGG_PRIVATE *) psf->container_data ;
+	sf_count_t ret ;
+
+	ret = psf_fseek (psf, offset, whence) ;
+	if (ret >= 0)
+	{	/* success */
+		odata->eos = 0 ;
+		ogg_sync_reset (&odata->osync) ;
+		}
+
+	return ret ;
+} /* ogg_sync_fseek */
+
+int
+ogg_sync_next_page (SF_PRIVATE * psf, ogg_page *og, sf_count_t readmax, sf_count_t *offset)
+{	OGG_PRIVATE* odata = (OGG_PRIVATE *) psf->container_data ;
+	sf_count_t position, nb_read, read_ret ;
+	unsigned char *buffer ;
+	int synced ;
+	int report_hole = 0 ;
+
+	for (position = 0 ; readmax <= 0 || readmax > position ; )
+	{	synced = ogg_sync_pageseek (&odata->osync, og) ;
+		if (synced < 0)
+		{	/*
+			** Skipped -synced bytes before finding the start of a page.
+			** If seeking, we have just landed in the middle of a page.
+			** Otherwise, warn about junk in the bitstream.
+			** Page might not yet be ready, hence the continue.
+			*/
+			if (!offset)
+				report_hole = 1 ;
+			position -= synced ;
+			continue ;
+			} ;
+
+		if (report_hole)
+		{	psf_log_printf (psf, "Ogg : Skipped %d bytes looking for the next page. Corrupted bitstream?!\n", position) ;
+			report_hole = 0 ;
+			} ;
+
+		if (synced > 0)
+		{	/* Have a page */
+			if (offset)
+				*offset += position ;
+			return og->header_len + og->body_len ;
+			} ;
+
+		/*
+		** Else readmax == 0, Out of data. Try to read more in without
+		** invalidating our boundary (readmax) constraint.
+		*/
+		if (readmax == 0)
+			return 0 ;
+		if (readmax > 0)
+			nb_read = SF_MIN ((sf_count_t) OGG_SYNC_READ_SIZE, readmax - position) ;
+		else
+			nb_read = OGG_SYNC_READ_SIZE ;
+		buffer = (unsigned char *) ogg_sync_buffer (&odata->osync, nb_read) ;
+		if (buffer == NULL)
+		{	psf->error = SFE_MALLOC_FAILED ;
+			return -1 ;
+			}
+		read_ret = psf_fread (buffer, 1, nb_read, psf) ;
+		if (read_ret == 0)
+			return psf->error ? -1 : 0 ;
+		ogg_sync_wrote (&odata->osync, read_ret) ;
+		} ;
+	return 0 ;
+} /* ogg_sync_next_page */
+
+int
+ogg_stream_next_page (SF_PRIVATE *psf, OGG_PRIVATE *odata)
+{	int nn ;
+
+	if (odata->eos)
+		return 0 ;
+
+	for ( ; ; )
+	{	nn = ogg_sync_next_page (psf, &odata->opage, -1, NULL) ;
+		if (nn == 0)
+		{	psf_log_printf (psf, "Ogg : File ended unexpectedly without an End-Of-Stream flag set.\n") ;
+			odata->eos = 1 ;
+			}
+		if (nn <= 0)
+			return nn ;
+
+		if (ogg_page_serialno (&odata->opage) == odata->ostream.serialno)
+			break ;
+		} ;
+
+	if (ogg_page_eos (&odata->opage))
+		odata->eos = 1 ;
+
+	if (ogg_stream_pagein (&odata->ostream, &odata->opage) < 0)
+	{	psf->error = SFE_INTERNAL ;
+		return -1 ;
+		}
+
+	return 1 ;
+} /* ogg_stream_next_page */
+
+int
+ogg_stream_unpack_page (SF_PRIVATE *psf, OGG_PRIVATE *odata)
+{	int nn ;
+	int i ;
+	int found_hole = 0 ;
+	ogg_packet *ppkt = odata->pkt ;
+
+	odata->pkt_indx = 0 ;
+	nn = ogg_stream_packetout (&odata->ostream, ppkt) ;
+	if (nn == 0)
+	{	/*
+		** Steam is out of packets. Read in more pages until there is one, or
+		** the stream ends, or an error occurs.
+		*/
+		for ( ; nn == 0 ; nn = ogg_stream_packetout (&odata->ostream, ppkt))
+		{	nn = ogg_stream_next_page (psf, odata) ;
+			if (nn <= 0)
+			{	odata->pkt_len = 0 ;
+				return nn ;
+				}
+			}
+		/*
+		** In the case of the for loop exiting because
+		** ogg_stream_packetout() == -1, fall-through.
+		*/
+		}
+
+	if (nn == -1)
+	{	/*
+		** libOgg found a hole. That is, the next packet found was out of
+		** sequence. As such, "flush" the hole marker by removing the invalid
+		** packet, as the valid packets are queued behind it.
+		*/
+		psf_log_printf (psf, "Ogg : Warning, libogg reports a hole at %d bytes.\n", ogg_sync_ftell (psf)) ;
+		nn = ogg_stream_packetout (&odata->ostream, ppkt) ;
+		found_hole = 1 ;
+		}
+
+	/*
+	** Unpack all the packets on the page. It is undocumented (like much of
+	** libOgg behavior) but all packets from a page read into the stream are
+	** guarenteed to remain valid in memory until a new page is read into the
+	** stream.
+	*/
+	for (i = 1 ; ; i++)
+	{	/* Not an off-by-one, there are 255 not 256 packets max. */
+		if (i == 255)
+		{	if (ogg_stream_packetpeek (&odata->ostream, NULL) == 1)
+			{	psf->error = SFE_INTERNAL ;
+				return -1 ;
+				}
+			break ;
+			}
+		if (ogg_stream_packetout (&odata->ostream, ++ ppkt) != 1)
+			break ;
+		}
+	odata->pkt_len = i ;
+
+	/* 1 = ok, 2 = ok, and found a hole. */
+	return 1 + found_hole ;
+} /* ogg_stream_unpack_page */
+
+sf_count_t
+ogg_sync_last_page_before (SF_PRIVATE *psf, OGG_PRIVATE *odata, uint64_t *gp_out, sf_count_t offset, int32_t serialno)
+{	sf_count_t begin, end, original_end, chunk_size, ret ;
+	sf_count_t position = 0 ;
+	uint64_t gp = -1 ;
+	int left_link ;
+
+	/* Based on code from Xiph.org's Opusfile */
+
+	original_end = end = begin = offset ;
+	offset = -1 ;
+	chunk_size = OGG_CHUNK_SIZE ;
+	do
+	{	begin = SF_MAX (begin - chunk_size, (sf_count_t) 0) ;
+		position = ogg_sync_fseek (psf, begin, SEEK_SET) ;
+		if (position < 0)
+			return position ;
+		left_link = 0 ;
+		while (position < end)
+		{	ret = ogg_sync_next_page (psf, &odata->opage, end - position, &position) ;
+			if (ret < 0)
+				return -1 ;
+			else if (ret == 0)
+			{	// Hit EOF before EOS
+				break ;
+				}
+			if (ogg_page_serialno (&odata->opage) == serialno)
+			{	uint64_t page_gp = ogg_page_granulepos (&odata->opage) ;
+				if (page_gp != (uint64_t) -1)
+				{	offset = position ;
+					gp = page_gp ;
+					}
+				}
+			else
+				left_link = 1 ;
+			position += ret ;
+			}
+
+		if ((left_link || !begin) && offset < 0)
+		{	psf->error = SFE_MALFORMED_FILE ;
+			return -1 ;
+			}
+
+		chunk_size = SF_MIN (2 * chunk_size, (sf_count_t) OGG_CHUNK_SIZE_MAX) ;
+		end = SF_MIN (begin + OGG_PAGE_SIZE_MAX - 1, original_end) ;
+		}
+	while (offset < 0) ;
+
+	*gp_out = gp ;
+	return offset ;
+} /* ogg_sync_last_page_before */
+
+int
+ogg_stream_seek_page_search (SF_PRIVATE *psf, OGG_PRIVATE *odata,
+	uint64_t target_gp, uint64_t pcm_start, uint64_t pcm_end, uint64_t *best_gp,
+	sf_count_t begin, sf_count_t end, uint64_t gp_rate)
+{	ogg_page page ;
+	uint64_t gp ;
+	sf_count_t d0, d1, d2 ;
+	sf_count_t best ;
+	sf_count_t best_start ;
+	sf_count_t boundary ;
+	sf_count_t next_boundary ;
+	sf_count_t page_offset = -1 ;
+	sf_count_t seek_pos = -1 ;
+	sf_count_t bisect ;
+	sf_count_t chunk_size ;
+	int buffering = SF_FALSE ;
+	int force_bisect = SF_FALSE ;
+	int ret ;
+	int has_packets ;
+
+	*best_gp = pcm_start ;
+	best = best_start = begin ;
+	boundary = end ;
+
+	ogg_stream_reset_serialno (&odata->ostream, odata->ostream.serialno) ;
+
+	/*
+	** This code is based on op_pcm_seek_page() from Opusfile, which is in turn
+	** based on "new search algorithm by Nicholas Vinen" from libvorbisfile.
+	*/
+
+	d2 = d1 = d0 = end - begin ;
+	while (begin < end)
+	{	/*
+		** Figure out if and where to try and seek in the file.
+		*/
+		if (end - begin < OGG_CHUNK_SIZE)
+			bisect = begin ;
+		else
+		{	/* Update the interval size history */
+			d0 = d1 >> 1 ;
+			d1 = d2 >> 1 ;
+			d2 = (end - begin) >> 1 ;
+			if (force_bisect == SF_TRUE)
+				bisect = begin + ((end - begin) >> 1) ;
+			else
+			{	/* Take a decent guess. */
+				bisect = begin + ogg_page_search_do_rescale (target_gp - pcm_start, pcm_end - pcm_start, end - begin) ;
+				}
+			if (bisect - OGG_CHUNK_SIZE < begin)
+				bisect = begin ;
+			else
+				bisect -= OGG_CHUNK_SIZE ;
+			force_bisect = SF_FALSE ;
+			}
+
+		/*
+		** Avoid an actual fseek if we can (common for final iterations.)
+		*/
+		if (seek_pos != bisect)
+		{	if (buffering == SF_TRUE)
+				ogg_stream_reset (&odata->ostream) ;
+			buffering = SF_FALSE ;
+			page_offset = -1 ;
+			seek_pos = ogg_sync_fseek (psf, bisect, SEEK_SET) ;
+			if (seek_pos < 0)
+				return seek_pos ;
+			}
+
+		chunk_size = OGG_CHUNK_SIZE ;
+		next_boundary = boundary ;
+
+		/*
+		** Scan forward, figure out where we landed.
+		** The ideal case is we see a page that ends before our target followed
+		** by a page that ends after our target.
+		** If we are too far before or after, breaking out will bisect what we
+		** have found so far.
+		*/
+		while (begin < end)
+		{	ret = ogg_sync_next_page (psf, &page, boundary - seek_pos, &seek_pos) ;
+			if (ret <= 0)
+				return ret ;
+			page_offset = seek_pos ;
+			if (ret == 0)
+			{	/*
+				** There are no more pages in this interval from our stream
+				** with a granulepos less than our target.
+				*/
+				if (bisect <= begin + 1)
+				{	/* Scanned the whole interval, so we are done. */
+					end = begin ;
+					}
+				else
+				{	/*
+					** Otherwise, back up one chunk. First discard any data
+					** from a continued packet.
+					*/
+					if (buffering)
+						ogg_stream_reset (&odata->ostream) ;
+					buffering = SF_FALSE ;
+					bisect = SF_MAX (bisect - chunk_size, begin) ;
+					seek_pos = ogg_sync_fseek (psf, bisect, SEEK_SET) ;
+					if (seek_pos < 0)
+						return seek_pos ;
+					/* Bump up the chunk size. */
+					chunk_size = SF_MIN (2 * chunk_size, (sf_count_t) OGG_CHUNK_SIZE_MAX) ;
+					/*
+					** If we did find a page from another stream or without a
+					** timestamp, don't read past it.
+					*/
+					boundary = next_boundary ;
+					}
+				continue ;
+				}
+
+			/* Found a page. Advance seek_pos past it */
+			seek_pos += page.header_len + page.body_len ;
+			/*
+			** Save the offset of the first page we found after the seek,
+			** regardless of the stream it came from or whether or not it has a
+			** timestamp.
+			*/
+			next_boundary = SF_MIN (page_offset, next_boundary) ;
+
+			/* If not from our stream, continue. */
+			if (odata->ostream.serialno != ogg_page_serialno (&page))
+				continue ;
+
+			/*
+			** The Ogg spec says that a page with a granule pos of -1 must not
+			** contain and packets which complete, but the lack of biconditional
+			** wording means that /technically/ a packet which does not complete
+			** any packets can have a granule pos other than -1. To make matters
+			** worse, older versions of libogg did just that.
+			*/
+			has_packets = ogg_page_packets (&page) > 0 ;
+			gp = has_packets ? ogg_page_granulepos (&page) : -1 ;
+			if (gp == (uint64_t) -1)
+			{	if (buffering == SF_TRUE)
+				{	if (!has_packets)
+						ogg_stream_pagein (&odata->ostream, &page) ;
+					else
+					{	/*
+						** If packets did end on this page, but we still didn't
+						** have a valid granule position (in violation of the
+						** spec!), stop buffering continued packet data.
+						** Otherwise we might continue past the packet we
+						** actually wanted.
+						*/
+						ogg_stream_reset (&odata->ostream) ;
+						buffering = SF_FALSE ;
+						}
+					}
+				continue ;
+				}
+
+			if (gp < target_gp)
+			{	/*
+				** We found a page that ends before our target. Advance to
+				** the raw offset of the next page.
+				*/
+				begin = seek_pos ;
+				if (pcm_start > gp || pcm_end < gp)
+					break ;
+				/* Save the byte offset of after this page. */
+				best = best_start = begin ;
+				if (buffering)
+					ogg_stream_reset (&odata->ostream) ;
+				/* Check to see if the last packet continues. */
+				if (ogg_page_continues (&page))
+				{	ogg_page_search_continued_data (odata, &page) ;
+					/*
+					** If we have a continued packet, remember the offset of
+					** this page's start, so that if we do wind up having to
+					** seek back here later, we can prime the stream with the
+					** continued packet data. With no continued packet, we
+					** remember the end of the page.
+					*/
+					best_start = page_offset ;
+					/*
+					** Then force buffering on, so that if a packet starts (but
+					** does not end) on the next page, we still avoid the extra
+					** seek back.
+					*/
+					buffering = SF_TRUE ;
+				} ;
+				*best_gp = pcm_start = gp ;
+				if (target_gp - gp > gp_rate)
+				{	/* Out by over a second. Try another bisection. */
+					break ;
+					}
+				/* Otherwise, keep scanning forward (do NOT use begin+1). */
+				bisect = begin ;
+				}
+			else
+			{	/*
+				** Found a page that ends after our target. If we had just
+				** scanned the whole interval before we found it, we're good.
+				*/
+				if (bisect <= begin + 1)
+					end = begin ;
+				else
+				{	end = bisect ;
+					/*
+					** In later iterations, don't read past the first page we
+					** found.
+					*/
+					boundary = next_boundary ;
+					/*
+					** If we're not making much progress shrinking the interval
+					** size, start forcing straight bisection to limit the
+					** worst case.
+					*/
+					force_bisect = end - begin > d0 * 2 ? SF_TRUE : SF_FALSE ;
+					/*
+					** Don't let pcm_end get out of range! That could happen
+					** with an invalid timestamp.
+					*/
+					if (pcm_end > gp && pcm_start <= gp)
+						pcm_end = gp ;
+					}
+				break ;
+				}
+			}
+		}
+
+	/*
+	** If we are buffering, the page we want is currently buffered in the
+	** Ogg stream structure, or in the Ogg page which has not been submitted.
+	** If not, we need to seek back and load it again.
+	*/
+	if (buffering == SF_FALSE)
+	{	if (best_start != page_offset)
+		{	page_offset = -1 ;
+			seek_pos = ogg_sync_fseek (psf, best_start, SEEK_SET) ;
+			if (seek_pos < 0)
+				return seek_pos ;
+			}
+		if (best_start < best)
+		{	if (page_offset < 0)
+			{	ret = ogg_sync_next_page (psf, &page, -1, &seek_pos) ;
+				if (seek_pos != best_start)
+					return -1 ;
+				}
+			ogg_page_search_continued_data (odata, &page) ;
+			page_offset = -1 ;
+			}
+		} ;
+
+	if (page_offset >= 0)
+		ogg_stream_pagein (&odata->ostream, &page) ;
+
+	return 0 ;
+} /* ogg_stream_seek_page_search */
+
+int
+ogg_open (SF_PRIVATE *psf)
+{	OGG_PRIVATE* odata = calloc (1, sizeof (OGG_PRIVATE)) ;
+	sf_count_t pos = psf_ftell (psf) ;
+	int	error = 0 ;
+
+	psf->container_data = odata ;
+	psf->container_close = ogg_close ;
+
+	if (psf->file.mode == SFM_RDWR)
+		return SFE_BAD_MODE_RW ;
+
+	if (psf->file.mode == SFM_READ)
+		if ((error = ogg_stream_classify (psf, odata)) != 0)
+			return error ;
+
+	if (SF_ENDIAN (psf->sf.format) != 0)
+		return SFE_BAD_ENDIAN ;
+
+	switch (psf->sf.format)
+	{	case SF_FORMAT_OGG | SF_FORMAT_VORBIS :
+			return ogg_vorbis_open (psf) ;
+
+		case SF_FORMAT_OGGFLAC :
+			/* Reset everything to an initial state. */
+			ogg_sync_clear (&odata->osync) ;
+			ogg_stream_clear (&odata->ostream) ;
+			psf_fseek (psf, pos, SEEK_SET) ;
+			free (psf->container_data) ;
+			psf->container_data = NULL ;
+			psf->container_close = NULL ;
+			return flac_open (psf) ;
+
+		case SF_FORMAT_OGG | SF_FORMAT_OPUS :
+			return ogg_opus_open (psf) ;
+
+#if ENABLE_EXPERIMENTAL_CODE
+		case SF_FORMAT_OGG | SF_FORMAT_SPEEX :
+			return ogg_speex_open (psf) ;
+
+		case SF_FORMAT_OGG | SF_FORMAT_PCM_16 :
+		case SF_FORMAT_OGG | SF_FORMAT_PCM_24 :
+			return ogg_pcm_open (psf) ;
+#endif
+
+		default :
+			break ;
+		} ;
+
+	psf_log_printf (psf, "%s : bad psf->sf.format 0x%x.\n", __func__, psf->sf.format) ;
+	return SFE_INTERNAL ;
+} /* ogg_open */
+
+/*==============================================================================
+** Private functions.
+*/
+
+static int
+ogg_close (SF_PRIVATE *psf)
+{	OGG_PRIVATE* odata = psf->container_data ;
+
+	ogg_sync_clear (&odata->osync) ;
+	ogg_stream_clear (&odata->ostream) ;
+
+	return 0 ;
+} /* ogg_close */
+
+static int
+ogg_stream_classify (SF_PRIVATE *psf, OGG_PRIVATE* odata)
+{	int error ;
+
+	/* Call this here so it only gets called once, so no memory is leaked. */
+	ogg_sync_init (&odata->osync) ;
+	ogg_stream_init (&odata->ostream, 0) ;
+
+	/* Load the first page in the physical bitstream. */
+	if ((error = ogg_read_first_page (psf, odata)) != 0)
+		return error ;
+
+	odata->codec = ogg_page_classify (psf, &odata->opage) ;
+
+	switch (odata->codec)
+	{	case OGG_VORBIS :
+			psf->sf.format = SF_FORMAT_OGG | SF_FORMAT_VORBIS ;
+			return 0 ;
+
+		case OGG_FLAC :
+		case OGG_FLAC0 :
+			psf->sf.format = SF_FORMAT_OGGFLAC ;
+			return 0 ;
+
+		case OGG_SPEEX :
+			psf->sf.format = SF_FORMAT_OGG | SF_FORMAT_SPEEX ;
+			return 0 ;
+
+		case OGG_OPUS :
+			psf->sf.format = SF_FORMAT_OGG | SF_FORMAT_OPUS ;
+			return 0 ;
+
+		case OGG_PCM :
+			psf_log_printf (psf, "Detected Ogg/PCM data. This is not supported yet.\n") ;
+			return SFE_UNIMPLEMENTED ;
+
+		default :
+			break ;
+		} ;
+
+	psf_log_printf (psf, "This Ogg bitstream contains some uknown data type.\n") ;
+	return SFE_UNIMPLEMENTED ;
+} /* ogg_stream_classify */
+
+/*==============================================================================
+*/
+
+static struct
+{	const char *str, *name ;
+	int len, codec ;
+} codec_lookup [] =
+{	{	"Annodex",		"Annodex",	8, OGG_ANNODEX },
+	{	"AnxData",		"AnxData",	7, OGG_ANXDATA },
+	{	"\177FLAC",		"Flac1",	5, OGG_FLAC },
+	{	"fLaC",			"Flac0",	4, OGG_FLAC0 },
+	{	"PCM     ",		"PCM",		8, OGG_PCM },
+	{	"Speex",		"Speex",	5, OGG_SPEEX },
+	{	"\001vorbis",	"Vorbis",	7, OGG_VORBIS },
+	{	"OpusHead",		"Opus",		8, OGG_OPUS },
+} ;
+
+static int
+ogg_page_classify (SF_PRIVATE * psf, const ogg_page * og)
+{	int k, len ;
+
+	for (k = 0 ; k < ARRAY_LEN (codec_lookup) ; k++)
+	{	if (codec_lookup [k].len > og->body_len)
+			continue ;
+
+		if (memcmp (og->body, codec_lookup [k].str, codec_lookup [k].len) == 0)
+		{	psf_log_printf (psf, "Ogg stream data : %s\n", codec_lookup [k].name) ;
+			psf_log_printf (psf, "Stream serialno : %u\n", (uint32_t) ogg_page_serialno (og)) ;
+			return codec_lookup [k].codec ;
+			} ;
+		} ;
+
+	len = og->body_len < 8 ? og->body_len : 8 ;
+
+	psf_log_printf (psf, "Ogg_stream data : '") ;
+	for (k = 0 ; k < len ; k++)
+		psf_log_printf (psf, "%c", isprint (og->body [k]) ? og->body [k] : '.') ;
+	psf_log_printf (psf, "'     ") ;
+	for (k = 0 ; k < len ; k++)
+		psf_log_printf (psf, " %02x", og->body [k] & 0xff) ;
+	psf_log_printf (psf, "\n") ;
+
+	return 0 ;
+} /* ogg_page_classify */
+
+/*
+** Scale x from the range [0, from] to the range [0, to]
+*/
+static uint64_t
+ogg_page_search_do_rescale (uint64_t x, uint64_t from, uint64_t to)
+{	uint64_t frac ;
+	uint64_t ret ;
+	int i ;
+
+	/* I should have paid more attention in CSc 349A: Numerical Analysis */
+	if (x >= from)
+		return to ;
+	if (x == 0)
+		return 0 ;
+	frac = 0 ;
+	for (i = 0 ; i < 63 ; i++)
+	{	frac <<= 1 ;
+		if (x >= from >> 1)
+		{	x -= from - x ;
+			frac |= 1 ;
+			}
+		else
+			x <<= 1 ;
+		}
+	ret = 0 ;
+	for (i = 0 ; i < 63 ; i++)
+	{	if (frac & 1)
+			ret = (ret & to & 1) + (ret >> 1) + (to >> 1) ;
+		else
+			ret >>= 1 ;
+		frac >>= 1 ;
+		}
+	return ret ;
+} /* ogg_page_search_do_rescale */
+
+static void
+ogg_page_search_continued_data (OGG_PRIVATE *odata, ogg_page *page)
+{	ogg_stream_pagein (&odata->ostream, page) ;
+	while (ogg_stream_packetout (&odata->ostream, &odata->opacket)) ;
+} /* ogg_page_search_continued_data */
+
+#else /* HAVE_EXTERNAL_XIPH_LIBS */
+
+int
+ogg_open	(SF_PRIVATE *psf)
+{
+	psf_log_printf (psf, "This version of libsndfile was compiled without Ogg/Vorbis support.\n") ;
+	return SFE_UNIMPLEMENTED ;
+} /* ogg_open */
+
+#endif
diff --git a/src/ogg.h b/src/ogg.h
new file mode 100644
index 0000000..8b2927a
--- /dev/null
+++ b/src/ogg.h
@@ -0,0 +1,169 @@
+/*
+** Copyright (C) 2008-2011 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2018 Arthur Taylor <art@ified.ca>
+**
+** This program is free software ; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation ; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program ; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef SF_SRC_OGG_H
+#define SF_SRC_OGG_H
+
+enum
+{	OGG_ANNODEX = 300,
+	OGG_ANXDATA,
+	OGG_FLAC,
+	OGG_FLAC0,
+	OGG_PCM,
+	OGG_SPEEX,
+	OGG_VORBIS,
+	OGG_OPUS,
+} ;
+
+typedef struct
+{	/* Sync and verify incoming physical bitstream */
+	ogg_sync_state osync ;
+	/* Take physical pages, weld into a logical stream of packets */
+	ogg_stream_state ostream ;
+	/* One Ogg bitstream page. Codec packets are inside */
+	ogg_page opage ;
+	/* One raw packet of data for decode */
+	ogg_packet opacket ;
+
+	/* Unpacked packets. 255 is max there can ever be in one page. */
+	ogg_packet pkt [255] ;
+	/* How many packets */
+	int pkt_len ;
+	/* Current packet */
+	int pkt_indx ;
+
+	int eos ;
+	int codec ;
+} OGG_PRIVATE ;
+
+
+#define readint(buf, base) (((buf [base + 3] << 24) & 0xff000000) | \
+								((buf [base + 2] <<16) & 0xff0000) | \
+								((buf [base + 1] << 8) & 0xff00) | \
+								(buf [base] & 0xff))
+/*-----------------------------------------------------------------------------------------------
+** Inline functions.
+*/
+
+/*
+** LibOgg documentation is noted as being bad by it's author.
+** Add some useful utility inline functions for introspecting Ogg pages.
+*/
+
+/* ogg_page_segments returns how many segments are in this page. */
+static inline int
+ogg_page_segments (ogg_page *pg)
+{	return (int) (pg->header [26]) ; }
+
+/* ogg_page_continues returns true if this page ends in a continued packet. */
+static inline int
+ogg_page_continues (ogg_page *pg)
+{	return pg->header [27 + pg->header [26] - 1] == 255 ;
+}
+
+/*-----------------------------------------------------------------------------------------------
+** Exported functions.
+*/
+
+/*
+** ogg_read_first_page loads the first Ogg page found in the file, and sets the
+** OGG_PRIVATE serialno to match the logical stream of the page. Data is read
+** without seeking backwards, loading any data present from psf->header into
+** the ogg_sync state first, so that this function works with pipes.
+*/
+int	ogg_read_first_page	(SF_PRIVATE *, OGG_PRIVATE *) ;
+
+/*
+** Write the whole Ogg page out. Convenience function as the ogg_page struct
+** splits header and body data into separate buffers.
+*/
+int	ogg_write_page	(SF_PRIVATE *, ogg_page *) ;
+
+/*
+** Wrapper around psf_ftell() that returns the current offset in the file after
+** the most recent page that has been returned by ogg_sync_pageout().
+*/
+sf_count_t ogg_sync_ftell (SF_PRIVATE *) ;
+
+/*
+** Wrapper around psf_fseek() that on success resets the ogg_sync_state struct
+** so that it doesn't get corrupted.
+*/
+sf_count_t ogg_sync_fseek (SF_PRIVATE *, sf_count_t offset, int whence) ;
+
+/*
+** Get the next page from the physical bitstream, reading in data as necessary.
+** Pays no attention to Ogg BOS/EOS markers or stream serial numbers.
+** The page is buffered in the ogg_sync_state struct, (replacing any other
+** buffered there) and also returned in *og. readmax sets a boundary for how
+** many bytes more may be read from the file, use already buffered only, or
+** unlimited reading in the case of a positive, zero or negative argument
+** respectively. If a pointer to a sf_count_t is passed in offset, then it will
+** be incremented by how many bytes were skipped to find the next page header.
+** (Useful for seeking, normally zero.) Returns the page size in bytes on
+** success, 0 on out-of-data (be it end of file or readmax reached) and -1 on
+** error with psf->error set appropriately.
+*/
+int	ogg_sync_next_page (SF_PRIVATE * psf, ogg_page *og, sf_count_t readmax, sf_count_t *offset) ;
+
+/*
+** Load the last page of a stream before the provided file offset. Searches the
+** physical bitstream, and selects a page of the passed serialno. The page
+** found is loaded in the sync buffer and exposed in odata->opage, and not
+** loaded into the ogg_stream_state. If found, the granulepos is returned in
+** *gp_out. Returns the file offset *before* the last page on success, or -1 on
+** error, setting psf->error as appropriate.
+*/
+sf_count_t ogg_sync_last_page_before (SF_PRIVATE *psf, OGG_PRIVATE *odata, uint64_t *gp_out, sf_count_t offset, int32_t serialno) ;
+
+/*
+** Load the next page from the virtual bitstream, reading data as necessary.
+** Reads in pages from the physical bitstream, skipping pages until one of the
+** virtual bitstream of interest is found, and then feeds it into the
+** ogg_stream_state of odata->ostream, where it is buffered. Heeds EOS markers.
+** Returns 1 on success, 0 on end of stream, and -1 on fatal error.
+*/
+int ogg_stream_next_page (SF_PRIVATE * psf, OGG_PRIVATE *odata) ;
+
+/*
+** Loads the next page using ogg_stream_next_page() and unpacks all packets
+** into the array odata->pkt, updating odata->pkt_len and setting
+** odata->pkt_indx to 0. Returns 1 if okay, 2 if okay but a hole was found
+** in the bitstream, 0 if on end of stream, and -1 on fatal error.
+*/
+int ogg_stream_unpack_page (SF_PRIVATE *psf, OGG_PRIVATE *odata) ;
+
+/*
+** Seek within the Ogg virtual bitstream for a page containing target_gp.
+** Preforms a bisection search. If not found exactly, the best result is
+** returned in *best_gp. Found page is loaded into the virtual bitstream,
+** ready for unpacking. Arguments pcm_start and pcm_end are the highest and
+** lowest granule positions of the file. begin and end are the file offset
+** range to search. gp_rate is an information hint so granule positions can
+** be correlated to playback time, so the search can figure out how close it
+** is, should be granule positions per second.
+*/
+int ogg_stream_seek_page_search (SF_PRIVATE *psf, OGG_PRIVATE *odata,
+								uint64_t target_gp,
+								uint64_t pcm_start, uint64_t pcm_end,
+								uint64_t *best_gp,
+								sf_count_t begin, sf_count_t end,
+								uint64_t gp_rate) ;
+
+#endif /* SF_SRC_OGG_H */
diff --git a/src/ogg_opus.c b/src/ogg_opus.c
new file mode 100644
index 0000000..c938362
--- /dev/null
+++ b/src/ogg_opus.c
@@ -0,0 +1,1823 @@
+/*
+** Copyright (C) 2013-2020 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2018 Arthur Taylor <art@ified.ca>
+**
+** This program is free software ; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation ; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program ; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*
+** This file contains code based on OpusFile and Opus-Tools, both by
+** Xiph.Org. COPYING from each is identical and is as follows:
+**
+** Copyright (c) 1994-2013 Xiph.Org Foundation and contributors
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+**
+** - Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+**
+** - Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** - Neither the name of the Xiph.Org Foundation nor the names of its
+** contributors may be used to endorse or promote products derived from
+** this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION
+** OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*
+** TODO:
+**  - Channel mapping modification / reporting
+**	 - connect psf->channel_map and Opus channel mapping somehow?
+**  - Gain parameters and their mappings
+*/
+
+/*
+** Opus Sample, Frame, and Samples/Channel Terminology
+**
+** libsndfile refers to one PCM value as a 'sample,' and a group of samples of
+** the same sample time, one for each channel, as a 'frame.' This differs from
+** Opus, which has no corresponding name for sample, and refers to a group of
+** PCM values, one per channel (aka libsndfile frames) as 'samples.'
+** Further, Opus has an object called a 'frame' that is made up of multiple
+** Opus-samples.
+** All this means that one has to be careful with what is meant by each term.
+** In an attempt to avoid ambiguity, this file adopts the following terms:
+**  - Samples shall refer to discrete PCM values, regardless of any channel
+**	considerations. This is the same as what libsndfile calls samples.
+**  - Samples/channel shall refer to groups of samples, one for each channel.
+**	This is what Opus calles samples, and what libsndfile calles frames. It
+**	has the advantage that its name is also the formula to calculate it.
+**
+**
+** Opus vs OggOpus
+**
+** In this file a distinction is made between Opus and OggOpus. Opus refers to
+** the codec alone, support for which is by libopus. OggOpus refers to an Opus
+** payload encapsulated in an Ogg stream. This is also know as an "Opus file."
+** The OggOpus spec includes information on header and granule position
+** interpretation, which is outside of the scope of the Opus spec. As such, an
+** attempt here is made to refer to either Opus or OggOpus depending on which
+** spec is being referenced. See https://wiki.xiph.org/OggOpus
+**
+**
+** Opus Sample Rates
+**
+** Opus only supports a fixed number of sample rates: 48kHz, 24kHz, 16kHz,
+** 12kHz, 8kHz. Audio may be decoded or encoded at any of these rates,
+** independent of the rate it was encoded at or to be decoded at respectively.
+** Other sample rates must be converted to one of these rates.
+**
+** As 44.1kHz (CD sample rate) and 22.5kHz are popular sample rates, and to
+** support any other sample rate there may be, the Opus header includes a field
+** to save the input (original) sample rate before converting it to a supported
+** one. Implementations are recommended by the Opus spec to do a sample rate
+** conversion at encode, but decode at 48kHz if outputting to hardware, or do
+** the reverse sample rate conversion if outputting to file.
+**
+** Heretofore libsndfile does not contain a sample rate converter, so doing the
+** sample rate conversion is not supported. Instead audio must be provided by
+** the user at a supported rate. However, the input sample rate field can be
+** set and retrieved by the user using sf_command(). At decode we choose to
+** decode at the lowest valid rate that is greater than or equal to the input
+** sample rate.
+**
+**
+** OggOpus Granule Positions
+**
+** Ogg streams include a strictly increasing granule position value. The
+** interpretation of this value is dependent on the payload type. For Opus
+** streams the granule position is the count of samples in the stream when
+** encoding/decoding at 48kHz. Note that the actual position of the output
+** sample relative to the granule position is offset by the preskip amount.
+** That is, if a packet ends with a granule position of x, the last sample
+** output when decoding is actually sample (x - preskip).
+**
+** Further, to allow for clipping off of the front of a stream without
+** rewriting all following granule positions, an Opus stream granule position
+** may be offset by a constant amount. This amount is evident by comparing the
+** granule position of the first page of an Opus stream on which an audio
+** packet completes is greater than the sum of the samples of all audio
+** packets completed on the page. Only the first such page is allows to have an
+** 'excessive' granule position, and only if it is not also the last page of
+** the stream (e_o_s bit is not set.)
+**
+** The granule position is an unsigned 64-bit integer, with the special value
+** of UINT64_MAX/-1 being treated as invalid. However, as not all platforms
+** support unsigned 64-bit integers, libOgg uses signed 64-bit integers for the
+** granule position.
+**
+** Remembering that signed integer overflow/underflow is explicitly undefined
+** in C, and as we already assume support for unsigned 64-bit integers, the
+** easiest way to deal with this problem is to modify granule positions as
+** unsigned integers.
+*/
+
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include <math.h>
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#else
+#include "sf_unistd.h"
+#endif
+
+#include "sndfile.h"
+#include "sfendian.h"
+#include "common.h"
+
+#if HAVE_EXTERNAL_XIPH_LIBS
+
+#include <ogg/ogg.h>
+#include <opus/opus.h>
+#include <opus/opus_multistream.h>
+
+#include "ogg.h"
+#include "ogg_vcomment.h"
+
+#define OGG_OPUS_COMMENT_PAD (512) /* Same as oggenc default */
+
+/*
+** When encoding, we can choose the size of the Opus frames.
+** Valid values are 2.5, 5, 10, 20, 40, and 60 milliseconds.
+**
+** Frames smaller than 10ms can't use CELT (MDCT) mode.
+** Frames larger than 20ms "are only interesting at fairly low bitrates."
+**
+** We choose the suggested default of 20ms for high-fidelity audio, however,
+** maybe this could be user-selected, or triggered by bitrate command.
+** default for non-realtime of 20ms. While longer packets reduce the overhead
+** data somewhat, it also decreases the quality.
+*/
+#define OGG_OPUS_ENCODE_PACKET_LEN(samplerate) ((20 * (samplerate)) / 1000)
+
+/*
+** The pre-roll is how long it takes for the decoder to converge. It converges
+** pretty quickly, to within -40db within 80ms. However, this also depends on
+** the signal. From experimentation, use the conservative pre-roll amount of
+** 660ms after which the output is 32-bit-exact with high probability.
+*/
+#define OGG_OPUS_PREROLL (660 * 48) /* 660 milliseconds (33 packets of 20ms) */
+
+typedef struct
+{	uint8_t version ;
+
+	/* Number of channels, 1...255 */
+	uint8_t channels ;
+
+	/* Encoder latency, the amount to skip before valid data comes out. */
+	uint16_t preskip ;
+
+	/* The sample rate of a the encoded source, as it may have been converted. */
+	int32_t input_samplerate ;
+
+	/* 'baked-in' gain to apply, dB S7.8 format. Should be zero when possible. */
+	int16_t gain ;
+
+	/* Channel mapping type. See OggOpus spec */
+	uint8_t channel_mapping ;
+
+	/* The rest is only used if channel_mapping != 0 */
+	/* How many streams are there? */
+	uint8_t nb_streams ;
+
+	/* How man of those streams are coupled? (aka stereo) */
+	uint8_t nb_coupled ;
+
+	/* Mapping of opus streams to output channels */
+	uint8_t stream_map [255] ;
+} OpusHeader ;
+
+typedef struct
+{	uint32_t serialno ;
+	OpusHeader header ;
+
+	/* Encode: Granule position after the previous packet.
+	 * Decode: Granule position after the current packet */
+	uint64_t pkt_pos ;
+
+	/* Encode: Granule position at the end of the previous page.
+	 * Decode: Granule position at the end of the current page. */
+	uint64_t pg_pos ;
+
+	/* integer coefficient of (current sample rate) / 48000Hz */
+	int sr_factor ;
+
+	/* Current position in buffer expressed as samples/channel */
+	int loc ;
+
+	/* Current data fill (decode) or target (encode) of buffer expressed in samples/channel */
+	int len ;
+
+	/* Size of the buffer storage, in sizeof (float) * channels */
+	int buffersize ;
+
+	/* Samples, either decoded from a packet, or assembling for encode. */
+	float *buffer ;
+
+	union {
+		/* decode only members */
+		struct {
+			OpusMSDecoder *state ;
+			uint64_t gp_start ;
+			uint64_t gp_end ;
+			sf_count_t last_offset ;
+		} decode ;
+
+		/* encode only members */
+		struct {
+			OpusMSEncoder *state ;
+
+			/* How many Ogg page segments are in Ogg page currently being assembled. */
+			int last_segments ;
+
+			int bitrate ;
+			unsigned long latency ;
+
+			/* Least significant bit of the source (aka bitwidth) */
+			int lsb ;
+			int lsb_last ;
+		} encode ;
+	} u ;
+} OPUS_PRIVATE ;
+
+/*-----------------------------------------------------------------------------------------------
+** Private function prototypes.
+*/
+
+static int			ogg_opus_close (SF_PRIVATE *psf) ;
+static void			opus_print_header (SF_PRIVATE *psf, OpusHeader *h) ;
+static int			opus_read_header_packet (SF_PRIVATE *psf, OpusHeader *h, ogg_packet *opacket) ;
+static int			ogg_opus_read_header (SF_PRIVATE * psf) ;
+static int			ogg_opus_setup_decoder (SF_PRIVATE *psf, int input_samplerate) ;
+
+static int			ogg_opus_setup_encoder (SF_PRIVATE *psf, OGG_PRIVATE *odata, OPUS_PRIVATE *oopus) ;
+static int			ogg_opus_write_header (SF_PRIVATE * psf, int calc_length) ;
+static void			ogg_opus_flush (SF_PRIVATE *psf) ;
+static int			ogg_opus_unpack_next_page (SF_PRIVATE *psf, OGG_PRIVATE *odata, OPUS_PRIVATE *oopus) ;
+static int			ogg_opus_calculate_page_duration (OGG_PRIVATE *odata) ;
+static int			ogg_opus_read_refill (SF_PRIVATE *psf, OGG_PRIVATE *odata, OPUS_PRIVATE *oopus) ;
+static int			ogg_opus_write_out (SF_PRIVATE *psf, OGG_PRIVATE *odata, OPUS_PRIVATE *oopus) ;
+
+static sf_count_t	ogg_opus_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
+static sf_count_t	ogg_opus_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
+static sf_count_t	ogg_opus_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
+static sf_count_t	ogg_opus_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
+
+static sf_count_t	ogg_opus_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
+static sf_count_t	ogg_opus_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
+static sf_count_t	ogg_opus_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
+static sf_count_t	ogg_opus_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
+
+static sf_count_t	ogg_opus_seek (SF_PRIVATE *psf, int mode, sf_count_t offset) ;
+static sf_count_t	ogg_opus_null_read (SF_PRIVATE *psf, sf_count_t offset) ;
+static sf_count_t	ogg_opus_page_seek_manual (SF_PRIVATE *psf, uint64_t target_gp) ;
+static int			ogg_opus_page_seek_search (SF_PRIVATE *psf, uint64_t target_gp) ;
+
+static int			ogg_opus_analyze_file (SF_PRIVATE *psf) ;
+static int			ogg_opus_command (SF_PRIVATE *psf, int command, void *data, int datasize) ;
+static int			ogg_opus_byterate (SF_PRIVATE *psf) ;
+
+/*-----------------------------------------------------------------------------------------------
+*/
+
+static vorbiscomment_ident opustags_ident =	{ "OpusTags", 8 } ;
+
+/*-----------------------------------------------------------------------------------------------
+** Exported functions.
+*/
+
+int
+ogg_opus_open (SF_PRIVATE *psf)
+{	OGG_PRIVATE* odata = psf->container_data ;
+	OPUS_PRIVATE* oopus = calloc (1, sizeof (OPUS_PRIVATE)) ;
+	int	error = 0 ;
+
+	if (odata == NULL)
+	{	psf_log_printf (psf, "%s : odata is NULL???\n", __func__) ;
+		free (oopus) ;
+		return SFE_INTERNAL ;
+		} ;
+
+	psf->codec_data = oopus ;
+	if (oopus == NULL)
+		return SFE_MALLOC_FAILED ;
+
+	if (psf->file.mode == SFM_RDWR)
+		return SFE_BAD_MODE_RW ;
+
+	psf_log_printf (psf, "Opus library version: %s\n", opus_get_version_string ()) ;
+
+	psf->codec_close = ogg_opus_close ;
+	if (psf->file.mode == SFM_READ)
+	{	if ((error = ogg_opus_read_header (psf)))
+			return error ;
+		if ((error = ogg_opus_analyze_file (psf)))
+			return error ;
+
+		psf->read_short		= ogg_opus_read_s ;
+		psf->read_int		= ogg_opus_read_i ;
+		psf->read_float		= ogg_opus_read_f ;
+		psf->read_double	= ogg_opus_read_d ;
+		} ;
+
+	if (psf->file.mode == SFM_WRITE)
+	{	if ((error = ogg_opus_setup_encoder (psf, odata, oopus)))
+			return error ;
+
+		psf->write_header	= ogg_opus_write_header ;
+		psf->write_short	= ogg_opus_write_s ;
+		psf->write_int		= ogg_opus_write_i ;
+		psf->write_float	= ogg_opus_write_f ;
+		psf->write_double	= ogg_opus_write_d ;
+
+		psf->sf.frames = SF_COUNT_MAX ; /* Unknown really */
+		psf->strings.flags = SF_STR_ALLOW_START ;
+		psf->datalength = 0 ;
+		psf->dataoffset = 0 ; /* will be updated */
+		} ;
+
+	psf->seek = ogg_opus_seek ;
+	psf->command = ogg_opus_command ;
+	psf->byterate = ogg_opus_byterate ;
+	psf->sf.format = SF_FORMAT_OGG | SF_FORMAT_OPUS ;
+
+	return error ;
+} /* ogg_opus_open */
+
+/*==============================================================================
+** Private functions.
+*/
+
+static int
+ogg_opus_close (SF_PRIVATE *psf)
+{	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+	OPUS_PRIVATE *oopus = (OPUS_PRIVATE *) psf->codec_data ;
+
+	if (!oopus)
+		return 0 ;
+
+	if (psf->file.mode == SFM_WRITE)
+	{	if (psf->have_written)
+			ogg_opus_flush (psf) ;
+		else {
+			/* Write a header... it is expected. */
+			ogg_opus_write_header (psf, 0) ;
+			} ;
+		ogg_packet_clear (&odata->opacket) ;
+		if (oopus->u.encode.state)
+		{	opus_multistream_encoder_destroy (oopus->u.encode.state) ;
+			oopus->u.encode.state = NULL ;
+			} ;
+		}
+	else if (psf->file.mode == SFM_READ)
+	{	if (oopus->u.decode.state)
+		{	opus_multistream_decoder_destroy (oopus->u.decode.state) ;
+			oopus->u.decode.state = NULL ;
+			} ;
+		} ;
+
+	psf->codec_data = NULL ;
+	if (oopus->buffer)
+		free (oopus->buffer) ;
+	free (oopus) ;
+
+	return 0 ;
+} /* ogg_opus_close */
+
+static void
+opus_print_header (SF_PRIVATE *psf, OpusHeader *h)
+{	psf_log_printf (psf, "Opus Header Metadata\n") ;
+	psf_log_printf (psf, "  OggOpus version  : %d\n", (int) h->version) ;
+	psf_log_printf (psf, "  Channels         : %d\n", (int) h->channels) ;
+	psf_log_printf (psf, "  Preskip          : %d samples @48kHz\n", (int) h->preskip) ;
+	psf_log_printf (psf, "  Input Samplerate : %d Hz\n", (int) h->input_samplerate) ;
+	psf_log_printf (psf, "  Gain             : %d.%d\n", (int) arith_shift_right (h->gain & 0xF0, 8), h->gain & 0x0F) ;
+	psf_log_printf (psf, "  Channel Mapping  : ") ;
+	switch (h->channel_mapping)
+	{	case 0 :	psf_log_printf (psf, "0 (mono or stereo)\n") ; break ;
+		case 1 :	psf_log_printf (psf, "1 (surround, AC3 channel order)\n") ; break ;
+		case 255 :	psf_log_printf (psf, "255 (no channel order)\n") ; break ;
+		default :	psf_log_printf (psf, "%d (unknown or unsupported)\n", (int) h->channel_mapping) ; break ;
+		} ;
+
+	if (h->channel_mapping > 0)
+	{	int i ;
+		psf_log_printf (psf, "   streams total   : %d\n", (int) h->nb_streams) ;
+		psf_log_printf (psf, "   streams coupled : %d\n", (int) h->nb_coupled) ;
+		psf_log_printf (psf, "   stream mapping : [") ;
+		for (i = 0 ; i < h->channels - 1 ; i++)
+			psf_log_printf (psf, "%d,", (int) (h->stream_map [i])) ;
+		psf_log_printf (psf, "%d]\n", (int) (h->stream_map [i])) ;
+		} ;
+} /* opus_print_header */
+
+static int
+opus_read_header_packet (SF_PRIVATE *psf, OpusHeader *h, ogg_packet *opacket)
+{	int count, i ;
+
+	/*
+	** Opus headers are 19 bytes, in the case of type 0 channel mapping,
+	** or 19 + 2 + (1 * channel count) bytes for other channel mappings, to a
+	** maximum of 276 (255 channels).
+	*/
+
+	if (opacket->bytes < 19 || opacket->bytes > 276)
+		return SFE_MALFORMED_FILE ;
+
+	if (memcmp (opacket->packet, "OpusHead", 8) != 0)
+		return SFE_MALFORMED_FILE ;
+
+	/*
+	** Copy the header page into the binheader so we can use binheader
+	** functions to safely unpack it.
+	*/
+	count = psf_binheader_writef (psf, "ob", BHWo (0), BHWv (opacket->packet), BHWz (opacket->bytes)) ;
+	psf->header.end = count ;
+
+	count = psf_binheader_readf (psf, "ep1", 8, &h->version) ;
+	if (! (h->version == 1 || h->version == 0))
+	{	psf_log_printf (psf, "Opus : Unknown / unsupported embedding scheme version: %d.\n", (int) h->version) ;
+		return SFE_UNIMPLEMENTED ;
+		} ;
+
+	count += psf_binheader_readf (psf, "e12421", &h->channels, &h->preskip,
+		&h->input_samplerate, &h->gain, &h->channel_mapping) ;
+
+	if (h->channel_mapping == 0)
+	{	if (h->channels > 2)
+			return SFE_MALFORMED_FILE ;
+
+		/*
+		** Setup the stream mapping, so we can use the multistream decoder,
+		** rather than have to deal with two decoder pointer types
+		*/
+		h->nb_streams = 1 ;
+		h->nb_coupled = h->channels - 1 ;
+		h->stream_map [0] = 0 ;
+		h->stream_map [1] = 1 ;
+		}
+	else
+	{	if (opacket->bytes < 19 + 2 + h->channels)
+			return SFE_MALFORMED_FILE ;
+
+		if (h->channel_mapping == 1 && h->channels > 8)
+			return SFE_MALFORMED_FILE ;
+
+		count += psf_binheader_readf (psf, "11", &h->nb_streams, &h->nb_coupled) ;
+
+		if (h->nb_streams < 1 ||
+			h->nb_coupled > h->nb_streams ||
+			h->nb_coupled + h->nb_streams > 255)
+			return SFE_MALFORMED_FILE ;
+
+		for (i = 0 ; i < h->channels ; i++)
+		{	count += psf_binheader_readf (psf, "1", &(h->stream_map [i])) ;
+			if (h->stream_map [i] > h->nb_streams + h->nb_coupled && h->stream_map [i] != 255)
+				return SFE_MALFORMED_FILE ;
+			} ;
+		} ;
+
+	if (count != opacket->bytes)
+	{	/* OggOpus spec mandates that this is a hard error. */
+		psf_log_printf (psf, "Opus : Error, extra data in Ogg Opus header.\n") ;
+		return SFE_MALFORMED_FILE ;
+		} ;
+
+	opus_print_header (psf, h) ;
+
+	return 0 ;
+} /* ogg_opus_read_header_packet */
+
+static int
+ogg_opus_read_header (SF_PRIVATE *psf)
+{	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+	OPUS_PRIVATE *oopus = (OPUS_PRIVATE *) psf->codec_data ;
+	int error ;
+
+	/*
+	** First page is already loaded by the ogg container code when it
+	** classified the stream, no need to re-load it now.
+	*/
+
+	if (ogg_page_packets (&odata->opage) != 1 || !ogg_page_bos (&odata->opage))
+		return SFE_MALFORMED_FILE ;
+
+	oopus->serialno = ogg_page_serialno (&odata->opage) ;
+	if ((error = opus_read_header_packet (psf, &oopus->header, &odata->opacket)))
+		return error ;
+
+	/*
+	** The comment header MUST be next. It is one packet, that packet MUST begin
+	** on the second page of the stream, but it MAY span multiple pages.
+	*/
+
+	while (ogg_stream_packetout (&odata->ostream, &odata->opacket) != 1)
+	{	if (ogg_stream_next_page (psf, odata) != 1)
+		{	/* out of data... technically that's malformed. */
+			return psf->error ? psf->error : SFE_MALFORMED_FILE ;
+			} ;
+		} ;
+
+	if ((error = vorbiscomment_read_tags (psf, &odata->opacket, &opustags_ident)))
+		return error ;
+
+	return ogg_opus_setup_decoder (psf, oopus->header.input_samplerate) ;
+} /* ogg_opus_read_header */
+
+static int
+ogg_opus_setup_decoder (SF_PRIVATE *psf, int input_samplerate)
+{	OPUS_PRIVATE *oopus = (OPUS_PRIVATE *) psf->codec_data ;
+	OpusMSDecoder *decoder ;
+	int sr_factor ;
+	int error ;
+
+	/*
+	** Decide what sample rate to decode at. We choose the lowest valid rate
+	** that is greater or equal to the original rate.
+	**
+	** Opus documentation recommends always decoding at 48000Hz if the file is
+	** being decoded for playback, since most hardware will resample it back to
+	** 48000Hz anyways. We don't know if that's true, maybe the user is
+	** decoding for editing or transcoding purposes.
+	*/
+	if (input_samplerate > 24000)
+		sr_factor = 1 ;
+	else if (input_samplerate > 16000)
+		sr_factor = 2 ;
+	else if (input_samplerate > 12000)
+		sr_factor = 3 ;
+	else if (input_samplerate > 8000)
+		sr_factor = 4 ;
+	else
+		sr_factor = 6 ;
+
+	decoder = opus_multistream_decoder_create (
+		48000 / sr_factor,
+		oopus->header.channels,
+		oopus->header.nb_streams,
+		oopus->header.nb_coupled,
+		oopus->header.stream_map,
+		&error) ;
+
+	if (error != OPUS_OK)
+	{	psf_log_printf (psf, "Opus : Failed to create multistream decoder: %s\n",
+			opus_strerror (error)) ;
+		return SFE_INTERNAL ;
+		}
+
+	/*
+	** Replace the decoder, if one was already initialized (see
+	** SFC_GET_ORIGINAL_SAMPLERATE)
+	*/
+	if (oopus->u.decode.state)
+		opus_multistream_decoder_destroy (oopus->u.decode.state) ;
+	oopus->u.decode.state = decoder ;
+
+	oopus->sr_factor = sr_factor ;
+	psf->sf.samplerate = 48000 / sr_factor ;
+	psf->sf.channels = oopus->header.channels ;
+	oopus->loc = oopus->len = 0 ;
+
+	/*
+	** The Opus decoder can do our gain for us. The OggOpus header contains a
+	** gain field. This field, unlike various gain-related tags, is intended to
+	** be a perminent baked-in gain applied before any user-configurable gain
+	** (eg replay-gain.) This is so the gain of track can be set without having
+	** to re-encode.
+	**
+	** Both the header.gain field and the parameter are in the Q7.8 format.
+	**
+	** TODO: Make this configurable? Include other gain sources too?
+	*/
+	opus_multistream_decoder_ctl (oopus->u.decode.state, OPUS_SET_GAIN (oopus->header.gain)) ;
+
+	/*
+	** Opus packets can vary in length, with the legal values being 2.5, 5, 10,
+	** 20, 40 or 60ms. The recommended default for non-realtime is 20ms. As
+	** such, allocate a buffer of that size now, we'll realloc later if a
+	** larger one is needed.
+	**
+	** buffersize is expressed in samples/channel, as that is what opus_decode
+	** expects.
+	*/
+	if (oopus->buffer)
+	{	free (oopus->buffer) ;
+		oopus->buffer = NULL ;
+		} ;
+	oopus->buffersize = 20 * psf->sf.samplerate / 1000 ;
+	oopus->buffer = malloc (sizeof (float) * psf->sf.channels * oopus->buffersize) ;
+	if (oopus->buffer == NULL)
+		return SFE_MALLOC_FAILED ;
+
+	return 0 ;
+} /* ogg_opus_setup_decoder */
+
+static int
+ogg_opus_setup_encoder (SF_PRIVATE *psf, OGG_PRIVATE *odata, OPUS_PRIVATE *oopus)
+{	int error ;
+	int lookahead ;
+	int nb_streams ;
+	int nb_coupled ;
+
+	/* default page latency value (1000ms) */
+	oopus->u.encode.latency = 1000 * 48 ;
+
+	switch (psf->sf.samplerate)
+	{	case 8000 :
+		case 12000 :
+		case 16000 :
+		case 24000 :
+		case 48000 :
+			oopus->sr_factor = 48000 / psf->sf.samplerate ;
+			break ;
+		default :
+			return SFE_OPUS_BAD_SAMPLERATE ;
+		} ;
+
+	if (psf->sf.channels <= 2)
+	{	oopus->header.channel_mapping = 0 ;
+		nb_streams = 1 ;
+		nb_coupled = psf->sf.channels - 1 ;
+		oopus->header.stream_map [0] = 0 ;
+		oopus->header.stream_map [1] = 1 ;
+
+		oopus->u.encode.state = opus_multistream_encoder_create (
+			psf->sf.samplerate,
+			psf->sf.channels,
+			nb_streams,
+			nb_coupled,
+			oopus->header.stream_map,
+			OPUS_APPLICATION_AUDIO,
+			&error) ;
+		}
+	else
+	{	if (psf->sf.channels <= 8)
+		{	/* Use Vorbis/AC3 channel mappings for surround. */
+			oopus->header.channel_mapping = 1 ;
+			}
+		else
+		{	/* There is no channel mapping, just audio, in parallel, good luck */
+			oopus->header.channel_mapping = 255 ;
+			}
+
+		oopus->u.encode.state = opus_multistream_surround_encoder_create (
+			psf->sf.samplerate,
+			psf->sf.channels,
+			oopus->header.channel_mapping,
+			&nb_streams,
+			&nb_coupled,
+			oopus->header.stream_map,
+			OPUS_APPLICATION_AUDIO,
+			&error) ;
+
+		}
+
+	if (error != OPUS_OK)
+	{	psf_log_printf (psf, "Opus : Error, opus_multistream_encoder_create returned %s\n", opus_strerror (error)) ;
+		return SFE_BAD_OPEN_FORMAT ;
+		} ;
+	oopus->header.nb_streams = nb_streams ;
+	oopus->header.nb_coupled = nb_coupled ;
+
+	opus_multistream_encoder_ctl (oopus->u.encode.state, OPUS_GET_BITRATE (&oopus->u.encode.bitrate)) ;
+	psf_log_printf (psf, "Encoding at target bitrate of %dbps\n", oopus->u.encode.bitrate) ;
+
+	/* TODO: Make configurable? */
+	error = opus_multistream_encoder_ctl (oopus->u.encode.state, OPUS_SET_COMPLEXITY (10)) ;
+	if (error != OPUS_OK)
+	{	/* Non-fatal */
+		psf_log_printf (psf, "Opus : OPUS_SET_COMPLEXITY returned: %s\n", opus_strerror (error)) ;
+		}
+
+	/*
+	** Get the encoder delay. This can vary depending on implementation and
+	** encoder configuration.
+	** GOTCHA: This returns the preskip at the encoder samplerate, not the
+	** granulepos rate of 48000Hz needed for header.preskip.
+	*/
+	error = opus_multistream_encoder_ctl (oopus->u.encode.state, OPUS_GET_LOOKAHEAD (&lookahead)) ;
+	if (error != OPUS_OK)
+	{	psf_log_printf (psf, "Opus : OPUS_GET_LOOKAHEAD returned: %s\n", opus_strerror (error)) ;
+		return SFE_BAD_OPEN_FORMAT ;
+		} ;
+	oopus->header.preskip = lookahead * oopus->sr_factor ;
+
+	oopus->len = OGG_OPUS_ENCODE_PACKET_LEN (psf->sf.samplerate) ;
+	oopus->buffer = malloc (sizeof (float) * psf->sf.channels * oopus->len) ;
+	if (oopus->buffer == NULL)
+		return SFE_MALLOC_FAILED ;
+
+	/*
+	** Set up the resident ogg packet structure, ready for writing into.
+	** 1275 * 3 + 7 bytes of packet per stream is from opusenc from opus-tools
+	*/
+	ogg_packet_clear (&odata->opacket) ;
+	oopus->buffersize = (1275 * 3 + 7) * oopus->header.nb_streams ;
+	odata->opacket.packet = malloc (oopus->buffersize) ;
+	odata->opacket.packetno = 2 ;
+	if (odata->opacket.packet == NULL)
+		return SFE_MALLOC_FAILED ;
+
+	oopus->serialno = psf_rand_int32 () ;
+	ogg_stream_init (&odata->ostream, oopus->serialno) ;
+
+	return 0 ;
+} /* ogg_opus_setup_encoder */
+
+static int
+ogg_opus_write_header (SF_PRIVATE *psf, int UNUSED (calc_length))
+{	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+	OPUS_PRIVATE *oopus = (OPUS_PRIVATE *) psf->codec_data ;
+	int nn ;
+	ogg_packet op ;
+
+	oopus->header.version = 1 ;
+	oopus->header.channels = psf->sf.channels ;
+
+	/* FIXME: Allow the user to set this ?! */
+	oopus->header.gain = 0 ;
+
+	if (psf->dataoffset > 0)
+	{	if (psf->have_written)
+		{	/*
+			** Might be possible to deal with this, but it's difficult as we
+			** have to take Ogg Page header sizes in to account, not just
+			** packet sizes.
+			*/
+			return SFE_UNIMPLEMENTED ;
+			}
+		if (psf_is_pipe (psf))
+			return SFE_NOT_SEEKABLE ;
+		if (psf_fseek (psf, 0, SEEK_SET) < 0)
+			return SFE_SEEK_FAILED ;
+		ogg_stream_reset_serialno (&odata->ostream, oopus->serialno) ;
+		psf->dataoffset = 0 ;
+		}
+	else
+		opus_print_header (psf, &oopus->header) ;
+
+	psf->header.ptr [0] = 0 ;
+	psf->header.indx = 0 ;
+
+	/* Opus Header Marker */
+	psf_binheader_writef (psf, "eb", BHWv ("OpusHead"), BHWz (8)) ;
+
+	/* Ogg Embedding scheme version, Channel Count, Preskip Samples */
+	psf_binheader_writef (psf, "e112", BHW1 (oopus->header.version), BHW1 (psf->sf.channels), BHW2 (oopus->header.preskip)) ;
+
+	/*
+	** If an original samplerate has not been set by the user command
+	** SFC_SET_ORIGINAL_SAMPLERATE, write the current samplerate.
+	*/
+	if (oopus->header.input_samplerate)
+		psf_binheader_writef (psf, "e4", BHW4 (oopus->header.input_samplerate)) ;
+	else
+		psf_binheader_writef (psf, "e4", BHW4 (psf->sf.samplerate)) ;
+
+	/* Input Sample Rate, Gain (S7.8 format), Channel Mapping Type */
+	psf_binheader_writef (psf, "e21", BHW2 (oopus->header.gain), BHW1 (oopus->header.channel_mapping)) ;
+
+	/* Channel mappings, required if not using type 0 (mono/stereo) */
+	if (oopus->header.channel_mapping > 0)
+	{	psf_binheader_writef (psf, "11", BHW1 (oopus->header.nb_streams), BHW1 (oopus->header.nb_coupled)) ;
+		for (nn = 0 ; nn < oopus->header.channels ; nn++)
+			psf_binheader_writef (psf, "1", BHW1 (oopus->header.stream_map [nn])) ;
+		} ;
+
+	op.packet = psf->header.ptr ;
+	op.bytes = psf->header.indx ;
+	op.b_o_s = 1 ;
+	op.e_o_s = 0 ;
+	op.granulepos = 0 ;
+	op.packetno = 1 ;
+
+	/* The first page MUST only contain the header, so flush it out now */
+	ogg_stream_packetin (&odata->ostream, &op) ;
+	for ( ; (nn = ogg_stream_flush (&odata->ostream, &odata->opage)) ; )
+	{	if (! (nn = ogg_write_page (psf, &odata->opage)))
+		{	psf_log_printf (psf, "Opus : Failed to write header!\n") ;
+			if (psf->error)
+				return psf->error ;
+			return SFE_INTERNAL ;
+			} ;
+		psf->dataoffset += nn ;
+		}
+
+	/*
+	** Metadata Tags (manditory)
+	**
+	** All tags must be in one packet, which may span pages, and these pages
+	** must not contain any other packets, so flush. The vendor string should
+	** be the libopus library version, as it is doing the actual encoding. We
+	** put the libsndfile identifier in the ENCODER tag.
+	**
+	** See: https://wiki.xiph.org/VorbisComment#ENCODER
+	*/
+	vorbiscomment_write_tags (psf, &op, &opustags_ident, opus_get_version_string (), - (OGG_OPUS_COMMENT_PAD)) ;
+	op.packetno = 2 ;
+	ogg_stream_packetin (&odata->ostream, &op) ;
+	for ( ; (nn = ogg_stream_flush (&odata->ostream, &odata->opage)) ; )
+	{	if (! (nn = ogg_write_page (psf, &odata->opage)))
+		{	psf_log_printf (psf, "Opus : Failed to write comments!\n") ;
+			if (psf->error)
+				return psf->error ;
+			return SFE_INTERNAL ;
+			} ;
+		psf->dataoffset += nn ;
+		}
+
+	return 0 ;
+} /* ogg_opus_write_header */
+
+static void
+ogg_opus_flush (SF_PRIVATE *psf)
+{	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+	OPUS_PRIVATE *oopus = (OPUS_PRIVATE *) psf->codec_data ;
+	uint64_t last_granulepos ;
+	int nbytes ;
+	int len ;
+	int last_packet ;
+
+	/*
+	** Need to flush both samples waiting for a complete packet and samples
+	** currently 'inside' the encoder because of its latency. In the case of
+	** the latter, we need to encode an equivalent amount of silence to push
+	** them out.
+	**
+	** Note that the last packet's granule position might be less than the
+	** total number of samples completed in it. This is how Ogg embedded Opus
+	** encodes the amount of appended padding to truncate for gapless playback.
+	*/
+
+	last_granulepos = oopus->pkt_pos + (oopus->sr_factor * oopus->loc) + oopus->header.preskip ;
+	last_packet = SF_FALSE ;
+	memset (&(oopus->buffer [oopus->loc * psf->sf.channels]), 0, sizeof (float) * psf->sf.channels * (oopus->len - oopus->loc)) ;
+
+	for (last_packet = SF_FALSE ; last_packet == SF_FALSE ; )
+	{	oopus->pkt_pos += oopus->len * oopus->sr_factor ;
+		if (oopus->pkt_pos >= last_granulepos)
+		{	last_packet = SF_TRUE ;
+			/*
+			** Try to shorten the last packet to the smallest valid packet size
+			** to minimize padding samples.
+			*/
+			len = (oopus->len * oopus->sr_factor) - (oopus->pkt_pos - last_granulepos) ;
+			if (len <= 120) /* 2.5 ms */
+				len = 120 / oopus->sr_factor ;
+			else if (len <= 240) /* 5 ms */
+				len = 240 / oopus->sr_factor ;
+			else if (len <= 480) /* 10 ms */
+				len = 480 / oopus->sr_factor ;
+			else
+				len = oopus->len ;
+			}
+		else
+			len = oopus->len ;
+
+		nbytes = opus_multistream_encode_float (oopus->u.encode.state, oopus->buffer,
+					len, odata->opacket.packet, oopus->buffersize) ;
+
+		if (nbytes < 0)
+		{	psf_log_printf (psf, "Opus : opus_multistream_encode_float returned: %s\n", opus_strerror (nbytes)) ;
+			break ;
+			}
+
+		odata->opacket.bytes = nbytes ;
+		odata->opacket.packetno++ ;
+		if (last_packet)
+		{	odata->opacket.granulepos = (ogg_int64_t) last_granulepos ;
+			odata->opacket.e_o_s = 1 ;
+			}
+		else
+			odata->opacket.granulepos = (ogg_int64_t) oopus->pkt_pos ;
+
+		ogg_stream_packetin (&odata->ostream, &odata->opacket) ;
+		while (ogg_stream_pageout (&odata->ostream, &odata->opage))
+			ogg_write_page (psf, &odata->opage) ;
+		} ;
+
+	while (ogg_stream_flush (&odata->ostream, &odata->opage))
+		ogg_write_page (psf, &odata->opage) ;
+} /* ogg_opus_flush */
+
+static int
+ogg_opus_calculate_page_duration (OGG_PRIVATE *odata)
+{	int i, samples, duration ;
+	ogg_packet *ppkt ;
+
+	duration = 0 ;
+	for (i = 0 , ppkt = odata->pkt ; i < odata->pkt_len ; i++, ppkt++)
+	{	/* Use 48kHz to get the sample count for use with granule positions. */
+		samples = opus_packet_get_nb_samples (ppkt->packet, ppkt->bytes, 48000) ;
+		if (samples > 0)
+			duration += samples ;
+		} ;
+	return duration ;
+} /* ogg_opus_calculate_page_duration */
+
+static int
+ogg_opus_unpack_next_page (SF_PRIVATE *psf, OGG_PRIVATE *odata, OPUS_PRIVATE *oopus)
+{	int nn ;
+
+	nn = ogg_stream_unpack_page (psf, odata) ;
+
+	if (nn == 1)
+	{	oopus->pkt_pos = oopus->pg_pos ;
+		oopus->pg_pos = odata->pkt [odata->pkt_len - 1].granulepos ;
+		}
+	else if (nn == 2)
+	{	uint64_t gp, last_page ;
+
+		/* Found a hole. Need to recalculated pkt_pos from pg_pos */
+		last_page = oopus->pg_pos ;
+		oopus->pg_pos = odata->pkt [odata->pkt_len - 1].granulepos ;
+		gp = ogg_opus_calculate_page_duration (odata) ;
+		oopus->pkt_pos = oopus->pg_pos - gp ;
+		psf_log_printf (psf, "Opus : Hole found appears to be of length %D samples.\n",
+				(oopus->pkt_pos - last_page) / (uint64_t) oopus->sr_factor) ;
+		/*
+		** Could save the hole size here, and have ogg_opus_read_refill()
+		** do packet loss concealment until the hole is gone, but libopus does
+		** PLC by generating white-noise for the duration of the hole. That is
+		** the correct thing for use in telephony, but it isn't generally
+		** appropriate here. It actually sounds better with no PLC, as the
+		** lapped nature of full-width Opus means the two edges of the hole
+		** will be blended together.
+		*/
+		return 1 ;
+		}
+
+	return nn ;
+} /* ogg_opus_unpack_next_page */
+
+static int
+ogg_opus_read_refill (SF_PRIVATE *psf, OGG_PRIVATE *odata, OPUS_PRIVATE *oopus)
+{	uint64_t pkt_granulepos ;
+	int nn, nsamp ;
+	ogg_packet *ppkt ;
+
+	if (odata->pkt_indx == odata->pkt_len)
+	{	nn = ogg_opus_unpack_next_page (psf, odata, oopus) ;
+		if (nn <= 0)
+			return nn ;
+		}
+
+	if (odata->pkt_indx == odata->pkt_len)
+		return 0 ;
+
+	ppkt = odata->pkt + odata->pkt_indx ;
+	nsamp = opus_multistream_decode_float (oopus->u.decode.state,
+				ppkt->packet, ppkt->bytes, oopus->buffer, oopus->buffersize, 0) ;
+
+	if (nsamp == OPUS_BUFFER_TOO_SMALL)
+	{	nsamp = opus_packet_get_nb_samples (ppkt->packet, ppkt->bytes, psf->sf.samplerate) ;
+		psf_log_printf (psf, "Growing decode buffer to hold %d samples from %d\n",
+			nsamp, oopus->buffersize) ;
+		if (nsamp > 5760)
+		{	psf_log_printf (psf, "Packet is larger than maximum allowable of 120ms!? Skipping.\n") ;
+			return 0 ;
+			} ;
+		oopus->buffersize = nsamp ;
+
+		free (oopus->buffer) ;
+		oopus->buffer = NULL ;
+		oopus->buffer = malloc (sizeof (float) * oopus->buffersize * psf->sf.channels) ;
+		if (oopus->buffer == NULL)
+		{	psf->error = SFE_MALLOC_FAILED ;
+			oopus->buffersize = 0 ;
+			return -1 ;
+			} ;
+
+		nsamp = opus_multistream_decode_float (oopus->u.decode.state,
+				ppkt->packet, ppkt->bytes, oopus->buffer, oopus->buffersize, 0) ;
+		} ;
+	odata->pkt_indx ++ ;
+
+	if (nsamp < 0)
+	{	psf_log_printf (psf, "Opus : opus_multistream_decode returned: %s\n",
+			opus_strerror (nsamp)) ;
+		psf->error = SFE_INTERNAL ;
+		return nsamp ;
+		} ;
+
+	/*
+	** Check for if this decoded packet is the last of the stream, in
+	** which case a page granule position which is shorter than the
+	** sample count of all packets in the page indicates that the last
+	** samples are padding and should be dropped.
+	*/
+	pkt_granulepos = oopus->pkt_pos + (nsamp * oopus->sr_factor) ;
+	if (pkt_granulepos <= oopus->pg_pos)
+	{	oopus->len = nsamp ;
+		}
+	else
+	{	if (ogg_page_eos (&odata->opage))
+		{	/*
+			** Possible for pg_pos < pkt_pos if there is a trailing
+			** packet. It's not supposed to happen, but could.
+			*/
+			oopus->len = SF_MAX ((int) (oopus->pg_pos - oopus->pkt_pos) / oopus->sr_factor, 0) ;
+			}
+		else
+		{	/*
+			** From https://wiki.xiph.org/OggOpus#Granule_Position
+			**	A decoder MUST reject as invalid any stream where the granule
+			**	position is smaller than the number of samples contained in
+			**	packets that complete on the first page with a completed
+			**	packet, unless that page has the 'end of stream' flag set. It
+			**	MAY defer this action until it decodes the last packet
+			**	completed on that page.
+			*/
+			psf_log_printf (psf, "Opus : Mid-stream page's granule position %D is less than total samples of %D\n", oopus->pg_pos, pkt_granulepos) ;
+			psf->error = SFE_MALFORMED_FILE ;
+			return -1 ;
+			} ;
+		} ;
+
+	if (oopus->len > oopus->buffersize)
+	{	free (oopus->buffer) ;
+		oopus->buffersize = oopus->len ;
+		oopus->buffer = malloc (sizeof (float) * oopus->buffersize * psf->sf.channels) ;
+		if (oopus->buffer == NULL)
+		{	psf->error = SFE_MALLOC_FAILED ;
+			oopus->buffersize = 0 ;
+			return -1 ;
+			} ;
+		} ;
+
+	/*
+	** Check for if this decoded packet contains samples from before the pre-
+	** skip point, indicating that these samples are padding to get the decoder
+	** to converge and should be dropped.
+	*/
+	if (oopus->pkt_pos < (unsigned) oopus->header.preskip)
+		oopus->loc = SF_MIN ((oopus->header.preskip - (int) oopus->pkt_pos) / oopus->sr_factor, oopus->len) ;
+	else
+		oopus->loc = 0 ;
+
+	oopus->pkt_pos = pkt_granulepos ;
+
+	return nsamp ;
+} /* ogg_opus_read_refill */
+
+static int
+ogg_opus_write_out (SF_PRIVATE *psf, OGG_PRIVATE *odata, OPUS_PRIVATE *oopus)
+{	int nbytes ;
+
+	if (oopus->u.encode.lsb != oopus->u.encode.lsb_last)
+		opus_multistream_encoder_ctl (oopus->u.encode.state, OPUS_SET_LSB_DEPTH (oopus->u.encode.lsb)) ;
+
+	nbytes = opus_multistream_encode_float (oopus->u.encode.state,
+		oopus->buffer, oopus->len,
+		odata->opacket.packet, oopus->buffersize) ;
+
+	if (nbytes < 0)
+	{	psf_log_printf (psf, "Opus : Error, opus_multistream_encode_float returned: %s\n", opus_strerror (nbytes)) ;
+		psf->error = SFE_INTERNAL ;
+		return nbytes ;
+		} ;
+
+	oopus->u.encode.last_segments += (nbytes + 255) / 255 ;
+	oopus->pkt_pos += oopus->len * oopus->sr_factor ;
+	odata->opacket.bytes = nbytes ;
+	odata->opacket.granulepos = oopus->pkt_pos ;
+	odata->opacket.packetno++ ;
+
+	/*
+	** Decide whether to flush the Ogg page *before* adding the new packet to
+	** it. Check both for if there is more than 1 second of audio (our default
+	** Ogg page latency, this latency can be modified using sf_command())
+	** or if adding the packet would cause a continued page,
+	** in which case we might as well make a new page anyways.
+	*/
+	for ( ; ; )
+	{	if (oopus->pkt_pos - oopus->pg_pos >= oopus->u.encode.latency || oopus->u.encode.last_segments >= 255)
+			nbytes = ogg_stream_flush_fill (&odata->ostream, &odata->opage, 255 * 255) ;
+		else
+			nbytes = ogg_stream_pageout_fill (&odata->ostream, &odata->opage, 255 * 255) ;
+		if (nbytes > 0)
+		{	oopus->u.encode.last_segments -= ogg_page_segments (&odata->opage) ;
+			oopus->pg_pos = oopus->pkt_pos ;
+			ogg_write_page (psf, &odata->opage) ;
+			}
+		else
+			break ;
+		} ;
+
+	ogg_stream_packetin (&odata->ostream, &odata->opacket) ;
+	oopus->loc = 0 ;
+	oopus->u.encode.lsb_last = oopus->u.encode.lsb ;
+	oopus->u.encode.lsb = 0 ;
+
+	return 1 ;
+} /* ogg_opus_write_out */
+
+static sf_count_t
+ogg_opus_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len)
+{	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+	OPUS_PRIVATE *oopus = (OPUS_PRIVATE *) psf->codec_data ;
+	sf_count_t total = 0 ;
+	sf_count_t readlen, i ;
+	float *iptr ;
+
+	while (total < len)
+	{	if (oopus->loc == oopus->len)
+		{	if (ogg_opus_read_refill (psf, odata, oopus) <= 0)
+				return total ;
+			} ;
+
+		readlen = SF_MIN (len - total, (sf_count_t) (oopus->len - oopus->loc) * psf->sf.channels) ;
+		if (readlen > 0)
+		{	iptr = oopus->buffer + oopus->loc * psf->sf.channels ;
+			i = total ;
+			total += readlen ;
+
+			if (psf->float_int_mult)
+			{	float inverse = 1.0 / psf->float_max ;
+				for ( ; i < total ; i++)
+				{	ptr [i] = psf_lrintf (((*(iptr++)) * inverse) * 32767.0f) ;
+					} ;
+				}
+			else
+			{	for ( ; i < total ; i++)
+				{	ptr [i] = psf_lrintf ((*(iptr++)) * 32767.0f) ;
+					} ;
+				} ;
+			oopus->loc += (readlen / psf->sf.channels) ;
+			} ;
+		} ;
+	return total ;
+} /* ogg_opus_read_s */
+
+static sf_count_t
+ogg_opus_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len)
+{	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+	OPUS_PRIVATE *oopus = (OPUS_PRIVATE *) psf->codec_data ;
+	sf_count_t total = 0 ;
+	sf_count_t readlen, i ;
+	float *iptr ;
+
+	while (total < len)
+	{	if (oopus->loc == oopus->len)
+		{	if (ogg_opus_read_refill (psf, odata, oopus) <= 0)
+				return total ;
+			} ;
+
+		readlen = SF_MIN (len - total, (sf_count_t) (oopus->len - oopus->loc) * psf->sf.channels) ;
+		if (readlen > 0)
+		{	iptr = oopus->buffer + oopus->loc * psf->sf.channels ;
+			i = total ;
+			total += readlen ;
+
+			if (psf->float_int_mult)
+			{	float inverse = 1.0 / psf->float_max ;
+				for ( ; i < total ; i++)
+				{	ptr [i] = psf_lrintf (((*(iptr++)) * inverse) * 2147483647.0f) ;
+					}
+				}
+			else
+			{	for ( ; i < total ; i++)
+				{	ptr [i] = psf_lrintf ((*(iptr++)) * 2147483647.0f) ;
+					}
+				} ;
+			oopus->loc += (readlen / psf->sf.channels) ;
+			} ;
+		} ;
+	return total ;
+} /* ogg_opus_read_i */
+
+static sf_count_t
+ogg_opus_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len)
+{	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+	OPUS_PRIVATE *oopus = (OPUS_PRIVATE *) psf->codec_data ;
+	sf_count_t total = 0 ;
+	sf_count_t readlen ;
+
+	while (total < len)
+	{	if (oopus->loc == oopus->len)
+		{	if (ogg_opus_read_refill (psf, odata, oopus) <= 0)
+				return total ;
+			} ;
+
+		readlen = SF_MIN (len - total, (sf_count_t) (oopus->len - oopus->loc) * psf->sf.channels) ;
+		if (readlen > 0)
+		{	memcpy (&(ptr [total]), &(oopus->buffer [oopus->loc * psf->sf.channels]), sizeof (float) * readlen) ;
+			total += readlen ;
+			oopus->loc += (readlen / psf->sf.channels) ;
+			} ;
+		} ;
+	return total ;
+} /* ogg_opus_read_f */
+
+static sf_count_t
+ogg_opus_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len)
+{	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+	OPUS_PRIVATE *oopus = (OPUS_PRIVATE *) psf->codec_data ;
+	sf_count_t total = 0 ;
+	sf_count_t readlen, i ;
+	float *fptr ;
+
+	while (total < len)
+	{	if (oopus->loc >= oopus->len)
+		{	if (ogg_opus_read_refill (psf, odata, oopus) <= 0)
+				return total ;
+			} ;
+
+		readlen = SF_MIN (len - total, (sf_count_t) (oopus->len - oopus->loc) * psf->sf.channels) ;
+
+		if (readlen > 0)
+		{	fptr = oopus->buffer + oopus->loc * psf->sf.channels ;
+			i = total ;
+			total += readlen ;
+			for ( ; i < total ; i++)
+			{	ptr [i] = *fptr++ ;
+				} ;
+			oopus->loc += readlen / psf->sf.channels ;
+			} ;
+		} ;
+	return total ;
+} /* ogg_opus_read_d */
+
+static sf_count_t
+ogg_opus_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len)
+{	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+	OPUS_PRIVATE *oopus = (OPUS_PRIVATE *) psf->codec_data ;
+	sf_count_t total, i ;
+	int writelen ;
+	float *optr ;
+
+	if (oopus->u.encode.lsb < 16)
+		oopus->u.encode.lsb = 16 ;
+
+	for (total = 0 ; total < len ; )
+	{	if (oopus->loc >= oopus->len)
+		{	/* Need to encode the buffer */
+			if (ogg_opus_write_out (psf, odata, oopus) <= 0)
+				return total ;
+			} ;
+
+		writelen = SF_MIN (len - total, (sf_count_t) (oopus->len - oopus->loc) * psf->sf.channels) ;
+		if (writelen)
+		{	optr = oopus->buffer + oopus->loc * psf->sf.channels ;
+			i = total ;
+			total += writelen ;
+			for ( ; i < total ; i++)
+			{	*optr++ = (float) (ptr [i]) / 32767.0f ;
+				}
+			oopus->loc += (writelen / psf->sf.channels) ;
+			} ;
+		} ;
+	return total ;
+} /* ogg_opus_write_s */
+
+static sf_count_t
+ogg_opus_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len)
+{	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+	OPUS_PRIVATE *oopus = (OPUS_PRIVATE *) psf->codec_data ;
+	sf_count_t total, i ;
+	int writelen ;
+	float *optr ;
+
+	if (oopus->u.encode.lsb < 24)
+		oopus->u.encode.lsb = 24 ;
+
+	for (total = 0 ; total < len ; )
+	{	if (oopus->loc >= oopus->len)
+		{	/* Need to encode the buffer */
+			if (ogg_opus_write_out (psf, odata, oopus) <= 0)
+				return total ;
+			} ;
+
+		writelen = SF_MIN (len - total, (sf_count_t) (oopus->len - oopus->loc) * psf->sf.channels) ;
+		if (writelen)
+		{	optr = oopus->buffer + oopus->loc * psf->sf.channels ;
+			i = total ;
+			total += writelen ;
+			for ( ; i < total ; i++)
+			{	*optr++ = (float) (ptr [i]) / 2147483647.0f ;
+				} ;
+			oopus->loc += (writelen / psf->sf.channels) ;
+			} ;
+		} ;
+	return total ;
+} /* ogg_opus_write_i */
+
+static sf_count_t
+ogg_opus_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len)
+{	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+	OPUS_PRIVATE *oopus = (OPUS_PRIVATE *) psf->codec_data ;
+	sf_count_t total ;
+	int writelen ;
+
+	if (oopus->u.encode.lsb < 24)
+		oopus->u.encode.lsb = 24 ;
+
+	for (total = 0 ; total < len ; )
+	{	if (oopus->loc >= oopus->len)
+		{	/* Need to encode the buffer */
+			if (ogg_opus_write_out (psf, odata, oopus) <= 0)
+				return total ;
+			} ;
+
+		writelen = SF_MIN (len - total, (sf_count_t) (oopus->len - oopus->loc) * psf->sf.channels) ;
+		if (writelen)
+		{	memcpy (&(oopus->buffer [oopus->loc * psf->sf.channels]), &(ptr [total]), sizeof (float) * writelen) ;
+			total += writelen ;
+			oopus->loc += (writelen / psf->sf.channels) ;
+			} ;
+		} ;
+	return total ;
+} /* ogg_opus_write_f */
+
+static sf_count_t
+ogg_opus_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len)
+{	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+	OPUS_PRIVATE *oopus = (OPUS_PRIVATE *) psf->codec_data ;
+	sf_count_t total, i ;
+	int writelen ;
+	float *optr ;
+
+	if (oopus->u.encode.lsb < 24)
+		oopus->u.encode.lsb = 24 ;
+
+	for (total = 0 ; total < len ; )
+	{	if (oopus->loc >= oopus->len)
+		{	/* Need to encode the buffer */
+			if (ogg_opus_write_out (psf, odata, oopus) <= 0)
+				return total ;
+			} ;
+
+		writelen = SF_MIN (len - total, (sf_count_t) (oopus->len - oopus->loc) * psf->sf.channels) ;
+		if (writelen)
+		{	optr = oopus->buffer + oopus->loc * psf->sf.channels ;
+			i = total ;
+			total += writelen ;
+			for ( ; i < total ; i++)
+			{	*optr++ = (float) (ptr [i]) ;
+				} ;
+			oopus->loc += (writelen / psf->sf.channels) ;
+			} ;
+		} ;
+	return total ;
+} /* ogg_opus_write_d */
+
+static int
+ogg_opus_analyze_file (SF_PRIVATE *psf)
+{	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+	OPUS_PRIVATE *oopus = (OPUS_PRIVATE *) psf->codec_data ;
+	uint64_t gp ;
+	sf_count_t saved_offset, last_page ;
+	int error ;
+
+	psf->sf.sections = 1 ;
+	psf->sf.frames	= SF_COUNT_MAX ;
+	oopus->u.decode.gp_end = (uint64_t) -1 ;
+	oopus->u.decode.last_offset = SF_COUNT_MAX ;
+
+	psf->dataoffset = ogg_sync_ftell (psf) ;
+	if (psf->filelength != SF_COUNT_MAX)
+		psf->datalength = psf->filelength - psf->dataoffset ;
+	else
+		psf->datalength = SF_COUNT_MAX ;
+
+	/*
+	** Calculate the start granule position offset
+	**
+	** OggOpus streams are allowed to start with a granule position other than
+	** zero. This allows for cutting the beginning off of streams without
+	** having to modify all following granule positions, or for recording/
+	** joining a live stream in the middle. To figure out the offset, we need
+	** to sum up how many samples are in all the packets that complete in the
+	** page and subtract it from the page granule position.
+	**
+	** If this is the last page of the steam (EOS set), this is not possible,
+	** as the granule position may be /less/ than the number of samples, to
+	** indicate how many samples are end-padding. In this case the granule
+	** position offset of the file must be 0, as otherwise it is considered
+	** malformed.
+	*/
+	error = ogg_opus_unpack_next_page (psf, odata, oopus) ;
+	if (error < 0 && psf->error)
+		return psf->error ;
+
+	gp = ogg_opus_calculate_page_duration (odata) ;
+	if (gp <= 0)
+	{	psf_log_printf (psf, "Opus : Page duration of zero!\n") ;
+		return SFE_MALFORMED_FILE ;
+		} ;
+
+	if (!ogg_page_eos (&odata->opage))
+	{	if (gp > oopus->pg_pos)
+		{	psf_log_printf (psf, "Opus : First data page's granule position is less than total number of samples on the page!\n") ;
+			return SFE_MALFORMED_FILE ;
+			}
+		oopus->pkt_pos = oopus->pg_pos - gp ;
+		}
+	else if (gp < oopus->pg_pos)
+	{	psf_log_printf (psf, "Opus : First data page is also the last, and granule position has an (ambigious) offset.\n") ;
+		return SFE_MALFORMED_FILE ;
+		} ;
+	oopus->u.decode.gp_start = oopus->pkt_pos ;
+
+	if (!psf->sf.seekable)
+		return 0 ;
+
+	/*
+	** Find the last page and fetch the last granule position.
+	** First, save were we are now.
+	*/
+	saved_offset = ogg_sync_ftell (psf) ;
+
+	/* This uses the sync page buffer, the stream page buffer is untouched. */
+	last_page = ogg_sync_last_page_before (psf, odata, &oopus->u.decode.gp_end, psf->filelength, oopus->serialno) ;
+	if (last_page > 0)
+	{	if (!ogg_page_eos (&odata->opage))
+			psf_log_printf (psf, "Ogg : Last page lacks an end-of-stream bit.\n") ;
+		if (last_page + odata->opage.header_len + odata->opage.body_len < psf->filelength)
+			psf_log_printf (psf, "Ogg : Junk after the last page.\n") ;
+		oopus->u.decode.last_offset = last_page ;
+
+		if (oopus->u.decode.gp_end != (uint64_t) -1)
+		{	psf->sf.frames = (oopus->u.decode.gp_end - oopus->u.decode.gp_start
+				- oopus->header.preskip) / oopus->sr_factor ;
+			} ;
+		} ;
+
+
+	psf_log_printf (psf, "  Granule pos offset  : %D\n", oopus->u.decode.gp_start) ;
+	if (oopus->u.decode.gp_end != (uint64_t) -1)
+		psf_log_printf (psf, "  Last Granule pos : %D\n", oopus->u.decode.gp_end) ;
+
+	/* Go back to where we left off. */
+	ogg_sync_fseek (psf, saved_offset, SEEK_SET) ;
+	return 0 ;
+} /* ogg_opus_analyze_file */
+
+/*
+** ogg_opus_null_read
+**
+** Decode samples, doing nothing with them, until the desired granule position
+** is reached.
+*/
+static sf_count_t
+ogg_opus_null_read (SF_PRIVATE *psf, sf_count_t offset)
+{	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+	OPUS_PRIVATE *oopus = (OPUS_PRIVATE *) psf->codec_data ;
+	sf_count_t total ;
+
+	total = (oopus->pkt_pos / oopus->sr_factor) - (oopus->len - oopus->loc) ;
+	for ( ; total < offset ; )
+	{	sf_count_t readlen = SF_MIN ((int) (offset - total), (oopus->len - oopus->loc)) ;
+		if (readlen > 0)
+		{	total += readlen ;
+			oopus->loc += readlen ;
+			} ;
+		if (oopus->loc == oopus->len)
+		{	if (ogg_opus_read_refill (psf, odata, oopus) <= 0)
+				return total ;
+			/*
+			** Ignore pre-skip skipping. The preskip was accounted for in the
+			** arugment to offset, so we need to count it.
+			*/
+			oopus->loc = 0 ;
+			} ;
+		} ;
+	return total ;
+} /* ogg_opus_null_read */
+
+/*
+** ogg_opus_page_seek_search
+**
+** Search within the file for the page with the highest granule position at or
+** before our target.
+*/
+static int
+ogg_opus_page_seek_search (SF_PRIVATE *psf, uint64_t target_gp)
+{	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+	OPUS_PRIVATE *oopus = (OPUS_PRIVATE *) psf->codec_data ;
+	uint64_t pcm_start ;
+	uint64_t pcm_end ;
+	uint64_t best_gp ;
+	sf_count_t begin ;
+	sf_count_t end ;
+	sf_count_t old_pos ;
+	int ret ;
+
+	best_gp = pcm_start = oopus->u.decode.gp_start ;
+	pcm_end = oopus->u.decode.gp_end ;
+	begin = psf->dataoffset ;
+	end = oopus->u.decode.last_offset ;
+
+	/* Search the Ogg stream for such a page */
+	old_pos = ogg_sync_ftell (psf) ;
+	ret = ogg_stream_seek_page_search (psf, odata, target_gp, pcm_start, pcm_end, &best_gp, begin, end, 48000) ;
+	if (ret != 0)
+	{	ogg_sync_fseek (psf, old_pos, SEEK_SET) ;
+		return ret ;
+		} ;
+
+	/* Load the page that contains our pre-roll target */
+	oopus->loc = 0 ;
+	oopus->len = 0 ;
+	if ((ret = ogg_opus_unpack_next_page (psf, odata, oopus)) != 1)
+		return ret ;
+	oopus->pkt_pos = best_gp ;
+
+	/* Reset the decoder (gain settings survive the reset) */
+	opus_multistream_decoder_ctl (oopus->u.decode.state, OPUS_RESET_STATE) ;
+
+	return 0 ;
+} /* ogg_opus_page_seek_search */
+
+/*
+** ogg_opus_page_seek_manual
+**
+** Seek to the beginning of the Ogg stream and read pages until we find one with
+** a granule position at or before our target.
+*/
+static sf_count_t
+ogg_opus_page_seek_manual (SF_PRIVATE *psf, uint64_t target_gp)
+{	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+	OPUS_PRIVATE *oopus = (OPUS_PRIVATE *) psf->codec_data ;
+	sf_count_t pos ;
+	int nn ;
+
+	if (oopus->pg_pos > target_gp)
+	{	ogg_stream_reset (&odata->ostream) ;
+		pos = ogg_sync_fseek (psf, psf->dataoffset, SEEK_SET) ;
+		if (pos < 0)
+			return pos ;
+		oopus->pg_pos = oopus->u.decode.gp_start ;
+		opus_multistream_decoder_ctl (oopus->u.decode.state, OPUS_RESET_STATE) ;
+		} ;
+
+	while (oopus->pg_pos < target_gp)
+	{	nn = ogg_opus_unpack_next_page (psf, odata, oopus) ;
+		if (nn <= 0)
+			return nn ;
+		} ;
+
+	return 1 ;
+} /* ogg_opus_page_seek_manual */
+
+static sf_count_t
+ogg_opus_seek (SF_PRIVATE *psf, int mode, sf_count_t offset)
+{	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+	OPUS_PRIVATE *oopus = (OPUS_PRIVATE *) psf->codec_data ;
+	uint64_t target_gp, current_gp ;
+	int ret ;
+
+	/* Only support seeking in read mode. */
+	if (mode != SFM_READ || psf->file.mode != SFM_READ)
+	{	psf->error = SFE_BAD_SEEK ;
+		return PSF_SEEK_ERROR ;
+		} ;
+
+	/* Figure out the current position granule pos. Use the start of the
+	 * current buffer, to avoid backwards seeking if the target is on the page
+	 * but before the current locaiton. */
+	oopus->loc = 0 ;
+	current_gp = oopus->pkt_pos - (uint64_t) (oopus->len * oopus->sr_factor) ;
+
+	/* Calculate the target granule pos. This includes the decoder delay and
+	 * the file granule position offset. */
+	target_gp = offset * oopus->sr_factor ;
+	target_gp += oopus->u.decode.gp_start ;
+	target_gp += oopus->header.preskip ;
+
+	/* Check if we need to do a page seek. */
+	if (target_gp < current_gp || target_gp - current_gp > OGG_OPUS_PREROLL)
+	{	uint64_t preroll_gp ;
+
+		/* For a page seek, use an earlier target granule pos, giving the
+		 * decoder samples to converge before the actual target. */
+		if (target_gp >= OGG_OPUS_PREROLL + oopus->u.decode.gp_start + (uint64_t) oopus->header.preskip)
+		{	preroll_gp = target_gp - OGG_OPUS_PREROLL ;
+			}
+		else
+		{	preroll_gp = oopus->u.decode.gp_start + (uint64_t) oopus->header.preskip ;
+			} ;
+
+		if (oopus->u.decode.gp_end == (uint64_t) -1)
+		{	/*
+			** Don't know the end of the file. Could be a chained file we don't yet
+			** support. Oh well, just do it manually.
+			*/
+			ogg_opus_page_seek_manual (psf, preroll_gp) ;
+			}
+		else
+		{	ret = ogg_opus_page_seek_search (psf, preroll_gp) ;
+			if (ret < 0)
+			{	/*
+				** Page seek failed, what to do? Could be bad data. We can
+				** either fall-back to manual seeking or bail. Manaul seeking
+				** from the beginning has the advantage of finding where the
+				** file goes bad.
+				*/
+				ret = ogg_opus_page_seek_manual (psf, preroll_gp) ;
+				if (ret < 0)
+				{	/*
+					** If were here, and there is no error, we can be pretty
+					** sure that it's the file that is to blame.
+					*/
+					if (!psf->error)
+						psf->error = SFE_MALFORMED_FILE ;
+					return ret ;
+					} ;
+				} ;
+			} ;
+
+		/*
+		** Skip over packets on the found page that are before our pre-roll
+		** target to avoid unnecessary decoding, and make decoder convergence
+		** independent of page boundaries for more visible errors.
+		*/
+		for ( ; odata->pkt_indx != odata->pkt_len ; )
+		{	ogg_packet *ppkt = &odata->pkt [odata->pkt_indx] ;
+			int nsamp = opus_packet_get_nb_samples (ppkt->packet, ppkt->bytes, 48000) ;
+			if (oopus->pkt_pos + nsamp < preroll_gp)
+			{	oopus->pkt_pos += nsamp ;
+				odata->pkt_indx++ ;
+				}
+			else
+				break ;
+			} ;
+		} ;
+
+	/*
+	** We've seeked or skipped through pages until just before our target,
+	** now decode until we hit it.
+	*/
+	offset = ogg_opus_null_read (psf, target_gp / oopus->sr_factor) ;
+	return offset - ((oopus->header.preskip + oopus->u.decode.gp_start) / oopus->sr_factor) ;
+
+} /* ogg_opus_seek */
+
+static int
+ogg_opus_command (SF_PRIVATE *psf, int command, void *data, int datasize)
+{	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+	OPUS_PRIVATE *oopus = (OPUS_PRIVATE *) psf->codec_data ;
+	double quality ;
+	double latency ;
+	int error ;
+
+	switch (command)
+	{	case SFC_SET_CHANNEL_MAP_INFO :
+			/* TODO: figure this out */
+			break ;
+
+		case SFC_SET_OGG_PAGE_LATENCY :
+			/*
+			** Argument: double, range 50 to 1600.
+			** Average length of OGG page in ms.
+			** This length drive the flush of pages.
+			*/
+			if (data == NULL || datasize != SIGNED_SIZEOF (double))
+				return SFE_BAD_COMMAND_PARAM ;
+
+			latency = *((double *) data) ;
+			if (latency < 50)
+				latency = 50 ;
+
+			if (latency > 1600)
+				latency = 1600 ;
+
+			oopus->u.encode.latency = ((unsigned long) latency) * 48 ;
+			break ;
+
+		case SFC_SET_COMPRESSION_LEVEL :
+			/*
+			** Argument: double, range 0.0 (lest compressed, best quality) to
+			** 1.0 (most compressed, worst quality)
+			*/
+			if (data == NULL || datasize != SIGNED_SIZEOF (double))
+				return SFE_BAD_COMMAND_PARAM ;
+
+			/* Usable bitrate range is  [6, 256] kbps per channel. */
+			quality = *((double *) data) ;
+			oopus->u.encode.bitrate = (int) (((1.0 - quality) * (250000.0)) + 6000.0) * psf->sf.channels ;
+			if (opus_multistream_encoder_ctl (oopus->u.encode.state, OPUS_SET_BITRATE (oopus->u.encode.bitrate)) == OPUS_OK)
+			{	psf_log_printf (psf, "User changed encoding target bitrate to %dbps\n", oopus->u.encode.bitrate) ;
+				return SF_TRUE ;
+				}
+			psf_log_printf (psf, "Failed to set user encoding target bitrate of %dbps\n", oopus->u.encode.bitrate) ;
+			return SF_FALSE ;
+			break ;
+
+		case SFC_SET_ORIGINAL_SAMPLERATE :
+			if (data == NULL || datasize != SIGNED_SIZEOF (int))
+				return SFE_BAD_COMMAND_PARAM ;
+			/*
+			** Only allow changing the input samplerate if at the beginning
+			** of the stream, because while it might be possible to change
+			** samplerate mid-decode, or to re-write the header for encode,
+			** ain't nobody got time to implement and test that.
+			*/
+			if (psf->file.mode == SFM_WRITE)
+			{	if (psf->have_written)
+					return SF_FALSE ;
+				oopus->header.input_samplerate = *((int *) data) ;
+				}
+			else {
+				if (oopus->pkt_pos > oopus->u.decode.gp_start || oopus->loc > 0)
+					return SF_FALSE ;
+				if ((error = ogg_opus_setup_decoder (psf, *((int *) data))))
+					return error ;
+				odata->pkt_indx = 0 ;
+				/* Adjust file frames count. */
+				if (oopus->u.decode.gp_end != (uint64_t) -1)
+					psf->sf.frames = (oopus->u.decode.gp_end - oopus->u.decode.gp_start
+						- oopus->header.preskip) / oopus->sr_factor ;
+				} ;
+			return SF_TRUE ;
+
+		case SFC_GET_ORIGINAL_SAMPLERATE :
+			if (data == NULL || datasize != SIGNED_SIZEOF (int))
+				return SFE_BAD_COMMAND_PARAM ;
+			*((int *) data) = oopus->header.input_samplerate ;
+			return SF_TRUE ;
+
+		case SFC_GET_OGG_STREAM_SERIALNO :
+			if (data == NULL || datasize != sizeof (int32_t))
+				return SF_FALSE ;
+
+			*((int32_t *) data) = odata->ostream.serialno ;
+			return SF_TRUE ;
+
+		default :
+			break ;
+	}
+
+	return SF_FALSE ;
+} /* ogg_opus_command */
+
+static int
+ogg_opus_byterate (SF_PRIVATE *psf)
+{	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+	OPUS_PRIVATE *oopus = (OPUS_PRIVATE *) psf->codec_data ;
+
+	if (psf->file.mode == SFM_READ)
+	{	if (odata->pkt_indx == odata->pkt_len)
+		{	if (ogg_opus_unpack_next_page (psf, odata, oopus) < 0)
+				return -1 ;
+			} ;
+
+		if (odata->pkt_indx < odata->pkt_len)
+		{	ogg_packet *ppkt = &odata->pkt [odata->pkt_indx] ;
+			return (ppkt->bytes * 8000) / opus_packet_get_nb_samples (ppkt->packet, ppkt->bytes, 8000) ;
+			} ;
+
+		if (psf->datalength != SF_COUNT_MAX)
+			return (psf->datalength * psf->sf.samplerate) / psf->sf.frames ;
+		} ;
+
+	if (psf->file.mode == SFM_WRITE && oopus->u.encode.state != NULL)
+		return (oopus->u.encode.bitrate + 7) / 8 ;
+
+	return -1 ;
+} /* ogg_opus_byterate */
+
+#else /* HAVE_EXTERNAL_XIPH_LIBS */
+
+int
+ogg_opus_open (SF_PRIVATE *psf)
+{
+	psf_log_printf (psf, "This version of libsndfile was compiled without Ogg/Opus support.\n") ;
+	return SFE_UNIMPLEMENTED ;
+} /* ogg_opus_open */
+
+#endif
diff --git a/src/ogg_pcm.c b/src/ogg_pcm.c
new file mode 100644
index 0000000..f105214
--- /dev/null
+++ b/src/ogg_pcm.c
@@ -0,0 +1,167 @@
+/*
+** Copyright (C) 2008-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software ; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation ; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program ; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include <math.h>
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#else
+#include "sf_unistd.h"
+#endif
+
+#include "sndfile.h"
+#include "sfendian.h"
+#include "common.h"
+
+#if (ENABLE_EXPERIMENTAL_CODE && HAVE_EXTERNAL_XIPH_LIBS)
+
+#include <ogg/ogg.h>
+
+#include "ogg.h"
+
+typedef struct
+{	int32_t serialno ;
+
+
+	void * state ;
+} OPCM_PRIVATE ;
+
+static int	opcm_read_header (SF_PRIVATE * psf) ;
+static int	opcm_close (SF_PRIVATE *psf) ;
+
+int
+ogg_pcm_open (SF_PRIVATE *psf)
+{	OGG_PRIVATE* odata = psf->container_data ;
+	OPCM_PRIVATE* opcm = calloc (1, sizeof (OPCM_PRIVATE)) ;
+	int	error = 0 ;
+
+	if (odata == NULL)
+	{	psf_log_printf (psf, "%s : odata is NULL???\n", __func__) ;
+		free (opcm) ;
+		return SFE_INTERNAL ;
+		} ;
+
+	psf->codec_data = opcm ;
+	if (opcm == NULL)
+		return SFE_MALLOC_FAILED ;
+
+	if (psf->file.mode == SFM_RDWR)
+		return SFE_BAD_MODE_RW ;
+
+	if (psf->file.mode == SFM_READ)
+	{	/* Call this here so it only gets called once, so no memory is leaked. */
+		ogg_sync_init (&odata->osync) ;
+
+		if ((error = opcm_read_header (psf)))
+			return error ;
+
+#if 0
+		psf->read_short		= opcm_read_s ;
+		psf->read_int		= opcm_read_i ;
+		psf->read_float		= opcm_read_f ;
+		psf->read_double	= opcm_read_d ;
+		psf->sf.frames		= opcm_length (psf) ;
+#endif
+		} ;
+
+	psf->codec_close = opcm_close ;
+
+	if (psf->file.mode == SFM_WRITE)
+	{
+#if 0
+		/* Set the default opcm quality here. */
+		vdata->quality = 0.4 ;
+
+		psf->write_header	= opcm_write_header ;
+		psf->write_short	= opcm_write_s ;
+		psf->write_int		= opcm_write_i ;
+		psf->write_float	= opcm_write_f ;
+		psf->write_double	= opcm_write_d ;
+#endif
+
+		psf->sf.frames = SF_COUNT_MAX ; /* Unknown really */
+		psf->strings.flags = SF_STR_ALLOW_START ;
+		} ;
+
+	psf->bytewidth = 1 ;
+	psf->blockwidth = psf->bytewidth * psf->sf.channels ;
+
+#if 0
+	psf->seek = opcm_seek ;
+	psf->command = opcm_command ;
+#endif
+
+	/* FIXME, FIXME, FIXME : Hack these here for now and correct later. */
+	psf->sf.format = SF_FORMAT_OGG | SF_FORMAT_SPEEX ;
+	psf->sf.sections = 1 ;
+
+	psf->datalength = 1 ;
+	psf->dataoffset = 0 ;
+	/* End FIXME. */
+
+	return error ;
+} /* ogg_pcm_open */
+
+static int
+opcm_read_header (SF_PRIVATE * UNUSED (psf))
+{
+	return 0 ;
+} /* opcm_read_header */
+
+static int
+opcm_close (SF_PRIVATE * UNUSED (psf))
+{
+
+
+	return 0 ;
+} /* opcm_close */
+
+
+
+/*
+encoded_speex_frames = (frames_per_packet * Packets)
+                     = 1 * 272
+                     = 272
+
+audio_samples = encoded_speex_frames * frame_size
+              = 272 * 640
+              = 174080
+
+duration = audio_samples / rate
+         = 174080 / 44100
+         = 3.947
+*/
+
+#else /* ENABLE_EXPERIMENTAL_CODE && HAVE_EXTERNAL_XIPH_LIBS */
+
+int
+ogg_pcm_open (SF_PRIVATE *psf)
+{
+	psf_log_printf (psf, "This version of libsndfile was compiled without Ogg/Speex support.\n") ;
+	return SFE_UNIMPLEMENTED ;
+} /* ogg_pcm_open */
+
+#endif
diff --git a/src/ogg_speex.c b/src/ogg_speex.c
new file mode 100644
index 0000000..546cf35
--- /dev/null
+++ b/src/ogg_speex.c
@@ -0,0 +1,428 @@
+/*
+** Copyright (C) 2008-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software ; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation ; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program ; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include <math.h>
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#else
+#include "sf_unistd.h"
+#endif
+
+#include "sndfile.h"
+#include "sfendian.h"
+#include "common.h"
+
+#if (ENABLE_EXPERIMENTAL_CODE && HAVE_EXTERNAL_XIPH_LIBS)
+
+#include <ogg/ogg.h>
+
+#include <speex/speex.h>
+#include <speex/speex_stereo.h>
+#include <speex/speex_header.h>
+#include <speex/speex_callbacks.h>
+
+#include "ogg.h"
+
+#define	OGG_SPX_READ_SIZE	200
+
+typedef struct
+{	SpeexBits bits ;
+
+	int32_t serialno ;
+
+	int frame_size, granule_frame_size, nframes ;
+	int force_mode ;
+
+	SpeexStereoState stereo ;
+	SpeexHeader header ;
+
+	void * state ;
+} SPX_PRIVATE ;
+
+static int	spx_read_header (SF_PRIVATE * psf) ;
+static int	spx_close (SF_PRIVATE *psf) ;
+static void *spx_header_read (SF_PRIVATE * psf, ogg_packet *op, spx_int32_t enh_enabled, int force_mode) ;
+static void spx_print_comments (const char *comments, int length) ;
+
+int
+ogg_speex_open (SF_PRIVATE *psf)
+{	OGG_PRIVATE* odata = psf->container_data ;
+	SPX_PRIVATE* spx = calloc (1, sizeof (SPX_PRIVATE)) ;
+	int	error = 0 ;
+
+	if (odata == NULL)
+	{	psf_log_printf (psf, "%s : odata is NULL???\n", __func__) ;
+		free (spx) ;
+		return SFE_INTERNAL ;
+		} ;
+
+	psf->codec_data = spx ;
+	if (spx == NULL)
+		return SFE_MALLOC_FAILED ;
+
+	if (psf->file.mode == SFM_RDWR)
+		return SFE_BAD_MODE_RW ;
+
+	if (psf->file.mode == SFM_READ)
+	{	/* Call this here so it only gets called once, so no memory is leaked. */
+		ogg_sync_init (&odata->osync) ;
+
+		if ((error = spx_read_header (psf)))
+			return error ;
+
+#if 0
+		psf->read_short		= spx_read_s ;
+		psf->read_int		= spx_read_i ;
+		psf->read_float		= spx_read_f ;
+		psf->read_double	= spx_read_d ;
+		psf->sf.frames		= spx_length (psf) ;
+#endif
+		} ;
+
+	psf->codec_close = spx_close ;
+
+	if (psf->file.mode == SFM_WRITE)
+	{
+#if 0
+		/* Set the default spx quality here. */
+		vdata->quality = 0.4 ;
+
+		psf->write_header	= spx_write_header ;
+		psf->write_short	= spx_write_s ;
+		psf->write_int		= spx_write_i ;
+		psf->write_float	= spx_write_f ;
+		psf->write_double	= spx_write_d ;
+#endif
+
+		psf->sf.frames = SF_COUNT_MAX ; /* Unknown really */
+		psf->strings.flags = SF_STR_ALLOW_START ;
+		} ;
+
+	psf->bytewidth = 1 ;
+	psf->blockwidth = psf->bytewidth * psf->sf.channels ;
+
+#if 0
+	psf->seek = spx_seek ;
+	psf->command = spx_command ;
+#endif
+
+	/* FIXME, FIXME, FIXME : Hack these here for now and correct later. */
+	psf->sf.format = SF_FORMAT_OGG | SF_FORMAT_SPEEX ;
+	psf->sf.sections = 1 ;
+
+	psf->datalength = 1 ;
+	psf->dataoffset = 0 ;
+	/* End FIXME. */
+
+	return error ;
+} /* ogg_speex_open */
+
+#define le_short (x)	(x)
+
+static int
+spx_read_header (SF_PRIVATE * psf)
+{	static SpeexStereoState STEREO_INIT = SPEEX_STEREO_STATE_INIT ;
+
+	OGG_PRIVATE* odata = psf->container_data ;
+	SPX_PRIVATE* spx = psf->codec_data ;
+
+	ogg_int64_t page_granule = 0 ;
+	int stream_init = 0 ;
+	int	page_nb_packets = 0 ;
+	int packet_count = 0 ;
+	int enh_enabled = 1 ;
+	int force_mode = -1 ;
+	char * data ;
+	int nb_read ;
+	int lookahead ;
+
+printf ("%s %d\n", __func__, __LINE__) ;
+
+	psf_log_printf (psf, "Speex header\n") ;
+	odata->eos = 0 ;
+
+	/* Reset ogg stuff which has already been used in src/ogg.c. */
+	ogg_stream_reset (&odata->ostream) ;
+	ogg_sync_reset (&odata->osync) ;
+
+	/* Seek to start of stream. */
+	psf_fseek (psf, 0, SEEK_SET) ;
+
+	/* Initialize. */
+	ogg_sync_init (&odata->osync) ;
+	speex_bits_init (&spx->bits) ;
+
+	/* Set defaults. */
+	psf->sf.channels = -1 ;
+	psf->sf.samplerate = 0 ;
+	spx->stereo = STEREO_INIT ;
+
+	/* Get a pointer to the ogg buffer and read data into it. */
+	data = ogg_sync_buffer (&odata->osync, OGG_SPX_READ_SIZE) ;
+	nb_read = psf_fread (data, 1, OGG_SPX_READ_SIZE, psf) ;
+	ogg_sync_wrote (&odata->osync, nb_read) ;
+
+	/* Now we chew on Ogg packets. */
+	while (ogg_sync_pageout (&odata->osync, &odata->opage) == 1)
+	{	if (stream_init == 0)
+		{	ogg_stream_init (&odata->ostream, ogg_page_serialno (&odata->opage)) ;
+			stream_init = 1 ;
+			} ;
+
+		if (ogg_page_serialno (&odata->opage) != odata->ostream.serialno)
+		{	/* so all streams are read. */
+			ogg_stream_reset_serialno (&odata->ostream, ogg_page_serialno (&odata->opage)) ;
+			} ;
+
+		/*Add page to the bitstream*/
+		ogg_stream_pagein (&odata->ostream, &odata->opage) ;
+		page_granule = ogg_page_granulepos (&odata->opage) ;
+		page_nb_packets = ogg_page_packets (&odata->opage) ;
+
+		/*Extract all available packets*/
+		while (odata->eos == 0 && ogg_stream_packetout (&odata->ostream, &odata->opacket) == 1)
+		{	if (odata->opacket.bytes >= 8 && memcmp (odata->opacket.packet, "Speex   ", 8) == 0)
+			{	spx->serialno = odata->ostream.serialno ;
+				} ;
+
+			if (spx->serialno == -1 || odata->ostream.serialno != spx->serialno)
+				break ;
+
+			if (packet_count == 0)
+			{	spx->state = spx_header_read (psf, &odata->opacket, enh_enabled, force_mode) ;
+				if (! spx->state)
+					break ;
+
+				speex_decoder_ctl (spx->state, SPEEX_GET_LOOKAHEAD, &lookahead) ;
+				if (spx->nframes == 0)
+					spx->nframes = 1 ;
+				}
+			else if (packet_count == 1)
+			{	spx_print_comments ((const char*) odata->opacket.packet, odata->opacket.bytes) ;
+				}
+			else if (packet_count < 2 + spx->header.extra_headers)
+			{	/* Ignore extra headers */
+				}
+			packet_count ++ ;
+			} ;
+		} ;
+
+	psf_log_printf (psf, "End\n") ;
+
+	psf_log_printf (psf, "packet_count %d\n", packet_count) ;
+	psf_log_printf (psf, "page_nb_packets %d\n", page_nb_packets) ;
+	psf_log_printf (psf, "page_granule %lld\n", page_granule) ;
+
+	return 0 ;
+} /* spx_read_header */
+
+static int
+spx_close (SF_PRIVATE *psf)
+{	SPX_PRIVATE* spx = psf->codec_data ;
+
+	if (spx->state)
+		speex_decoder_destroy (spx->state) ;
+
+	if (spx)
+		speex_bits_destroy (&spx->bits) ;
+
+	return 0 ;
+} /* spx_close */
+
+
+
+static void *
+spx_header_read (SF_PRIVATE * psf, ogg_packet *op, spx_int32_t enh_enabled, int force_mode)
+{	SPX_PRIVATE* spx = psf->codec_data ;
+	void *st ;
+	const SpeexMode *mode ;
+	SpeexHeader *tmp_header ;
+	int modeID ;
+	SpeexCallback callback ;
+
+	tmp_header = speex_packet_to_header ((char*) op->packet, op->bytes) ;
+	if (tmp_header == NULL)
+	{	psf_log_printf (psf, "Cannot read Speex header\n") ;
+		return NULL ;
+		} ;
+
+	memcpy (&spx->header, tmp_header, sizeof (spx->header)) ;
+	free (tmp_header) ;
+	tmp_header = NULL ;
+
+	if (spx->header.mode >= SPEEX_NB_MODES || spx->header.mode < 0)
+	{	psf_log_printf (psf, "Mode number %d does not (yet/any longer) exist in this version\n", spx->header.mode) ;
+		return NULL ;
+		} ;
+
+	modeID = spx->header.mode ;
+	if (force_mode != -1)
+		modeID = force_mode ;
+
+	mode = speex_lib_get_mode (modeID) ;
+
+	if (spx->header.speex_version_id > 1)
+	{	psf_log_printf (psf, "This file was encoded with Speex bit-stream version %d, which I don't know how to decode\n", spx->header.speex_version_id) ;
+		return NULL ;
+		} ;
+
+	if (mode->bitstream_version < spx->header.mode_bitstream_version)
+	{	psf_log_printf (psf, "The file was encoded with a newer version of Speex. You need to upgrade in order to play it.\n") ;
+		return NULL ;
+		} ;
+
+	if (mode->bitstream_version > spx->header.mode_bitstream_version)
+	{	psf_log_printf (psf, "The file was encoded with an older version of Speex. You would need to downgrade the version in order to play it.\n") ;
+		return NULL ;
+		} ;
+
+	st = speex_decoder_init (mode) ;
+	if (!st)
+	{	psf_log_printf (psf, "Decoder initialization failed.\n") ;
+		return NULL ;
+		} ;
+
+	speex_decoder_ctl (st, SPEEX_SET_ENH, &enh_enabled) ;
+	speex_decoder_ctl (st, SPEEX_GET_FRAME_SIZE, &spx->frame_size) ;
+	spx->granule_frame_size = spx->frame_size ;
+
+	if (!psf->sf.samplerate)
+		psf->sf.samplerate = spx->header.rate ;
+	/* Adjust rate if --force-* options are used */
+	if (force_mode != -1)
+	{	if (spx->header.mode < force_mode)
+		{	psf->sf.samplerate <<= (force_mode - spx->header.mode) ;
+			spx->granule_frame_size >>= (force_mode - spx->header.mode) ;
+			} ;
+		if (spx->header.mode > force_mode)
+		{	psf->sf.samplerate >>= (spx->header.mode - force_mode) ;
+			spx->granule_frame_size <<= (spx->header.mode - force_mode) ;
+			} ;
+		} ;
+
+	speex_decoder_ctl (st, SPEEX_SET_SAMPLING_RATE, &psf->sf.samplerate) ;
+
+	spx->nframes = spx->header.frames_per_packet ;
+
+	if (psf->sf.channels == -1)
+		psf->sf.channels = spx->header.nb_channels ;
+
+	if (! (psf->sf.channels == 1))
+	{	psf->sf.channels = 2 ;
+		callback.callback_id = SPEEX_INBAND_STEREO ;
+		callback.func = speex_std_stereo_request_handler ;
+		callback.data = &spx->stereo ;
+		speex_decoder_ctl (st, SPEEX_SET_HANDLER, &callback) ;
+		} ;
+
+	spx->header.speex_version [sizeof (spx->header.speex_version) - 1] = 0 ;
+
+	psf_log_printf (psf, "  Encoder ver   : %s\n  Frames/packet : %d\n",
+					spx->header.speex_version, spx->header.frames_per_packet) ;
+
+	if (spx->header.bitrate > 0)
+		psf_log_printf (psf, "  Bit rate	  : %d\n", spx->header.bitrate) ;
+
+	psf_log_printf (psf, "  Sample rate   : %d\n  Mode		  : %s\n  VBR		   : %s\n  Channels	  : %d\n",
+					psf->sf.samplerate, mode->modeName, (spx->header.vbr ? "yes" : "no"), psf->sf.channels) ;
+
+	psf_log_printf (psf, "  Extra headers : %d\n", spx->header.extra_headers) ;
+
+	return st ;
+} /* spx_header_read */
+
+
+static void
+spx_print_comments (const char *c, int length)
+{
+	const char *end ;
+	int len, i, nb_fields ;
+
+printf ("%s %d\n", __func__, __LINE__) ;
+	if (length < 8)
+	{	fprintf (stderr, "Invalid/corrupted comments\n") ;
+		return ;
+		}
+	end = c + length ;
+	len = readint (c, 0) ;
+	c += 4 ;
+	if (len < 0 || c + len > end)
+	{	fprintf (stderr, "Invalid/corrupted comments\n") ;
+		return ;
+		}
+	(void) fwrite (c, 1, len, stderr) ;
+	c += len ;
+	fprintf (stderr, "\n") ;
+	if (c + 4 > end)
+	{	fprintf (stderr, "Invalid/corrupted comments\n") ;
+		return ;
+		}
+	nb_fields = readint (c, 0) ;
+	c += 4 ;
+	for (i = 0 ; i < nb_fields ; i++)
+	{	if (c + 4 > end)
+		{	fprintf (stderr, "Invalid/corrupted comments\n") ;
+			return ;
+			} ;
+		len = readint (c, 0) ;
+		c += 4 ;
+		if (len < 0 || c + len > end)
+		{	fprintf (stderr, "Invalid/corrupted comments\n") ;
+			return ;
+			}
+		(void) fwrite (c, 1, len, stderr) ;
+		c += len ;
+		fprintf (stderr, "\n") ;
+		} ;
+	return ;
+} /* spx_print_comments */
+
+
+/*
+encoded_speex_frames = (frames_per_packet * Packets)
+					 = 1 * 272
+					 = 272
+
+audio_samples = encoded_speex_frames * frame_size
+			  = 272 * 640
+			  = 174080
+
+duration = audio_samples / rate
+		 = 174080 / 44100
+		 = 3.947
+*/
+
+#else /* ENABLE_EXPERIMENTAL_CODE && HAVE_EXTERNAL_XIPH_LIBS */
+
+int
+ogg_speex_open (SF_PRIVATE *psf)
+{
+	psf_log_printf (psf, "This version of libsndfile was compiled without Ogg/Speex support.\n") ;
+	return SFE_UNIMPLEMENTED ;
+} /* ogg_speex_open */
+
+#endif
diff --git a/src/ogg_vcomment.c b/src/ogg_vcomment.c
new file mode 100644
index 0000000..252ed61
--- /dev/null
+++ b/src/ogg_vcomment.c
@@ -0,0 +1,277 @@
+/*
+** Copyright (C) 2008-2019 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2018 Arthur Taylor <art@ified.ca>
+**
+** This program is free software ; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation ; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program ; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "sndfile.h"
+#include "sfendian.h"
+#include "common.h"
+
+#if HAVE_EXTERNAL_XIPH_LIBS
+
+#include <ogg/ogg.h>
+
+#include "ogg_vcomment.h"
+
+typedef struct
+{	int id ;
+	const char *name ;
+} STR_PAIR ;
+
+/* See https://xiph.org/vorbis/doc/v-comment.html */
+static STR_PAIR vorbiscomment_mapping [] =
+{	{	SF_STR_TITLE,		"TITLE"			},
+	{	SF_STR_COPYRIGHT,	"COPYRIGHT",	},
+	{	SF_STR_SOFTWARE,	"ENCODER",		},
+	{	SF_STR_ARTIST,		"ARTIST"		},
+	{	SF_STR_COMMENT,		"COMMENT"		},
+	{	SF_STR_DATE,		"DATE",			},
+	{	SF_STR_ALBUM,		"ALBUM"			},
+	{	SF_STR_LICENSE,		"LICENSE",		},
+	{	SF_STR_TRACKNUMBER,	"TRACKNUMBER",	},
+	{	SF_STR_GENRE,		"GENRE",		},
+	{	0,					NULL,			},
+} ;
+
+/*-----------------------------------------------------------------------------------------------
+** Private function prototypes.
+*/
+
+static int	vorbiscomment_lookup_id (const char *name) ;
+static const char *	vorbiscomment_lookup_name (int id) ;
+
+static inline size_t read_32bit_size_t (const unsigned char * ptr)
+{	/* Read a 32 bit positive value from the provided pointer. */
+	return LE2H_32_PTR (ptr) & 0x7fffffff ;
+} /* read_32bit_size_t */
+
+/*-----------------------------------------------------------------------------------------------
+** Exported functions.
+*/
+
+int
+vorbiscomment_read_tags (SF_PRIVATE *psf, ogg_packet *packet, vorbiscomment_ident *ident)
+{	unsigned char *p, *ep ;
+	char *tag, *c ;
+	size_t tag_size, tag_len = 0 ;
+	unsigned int ntags, i = 0 ;
+	int id, ret = 0 ;
+
+	/*
+	** The smallest possible header is the ident string length plus two 4-byte
+	** integers, (vender string length, tags count.)
+	*/
+	if (packet->bytes < (ident ? ident->length : 0) + 4 + 4)
+		return SFE_MALFORMED_FILE ;
+
+	/* Our working pointer. */
+	p = packet->packet ;
+	/* Our end pointer for bound checking. */
+	ep = p + packet->bytes ;
+
+	if (ident)
+	{	if (memcmp (p, ident->ident, ident->length) != 0)
+		{	psf_log_printf (psf, "Expected comment packet identifier missing.\n") ;
+			return SFE_MALFORMED_FILE ;
+			} ;
+		p += ident->length ;
+		} ;
+
+	tag_size = 1024 ;
+	tag = malloc (tag_size) ;
+	/* Unlikely */
+	if (!tag)
+		return SFE_MALLOC_FAILED ;
+
+	psf_log_printf (psf, "VorbisComment Metadata\n") ;
+
+	/*
+	** Vendor tag, manditory, no field name.
+	*/
+	tag_len = read_32bit_size_t (p) ;
+	p += 4 ;
+	if (tag_len > 0)
+	{	/* Bound checking. 4 bytes for remaining manditory fields. */
+		if (p + tag_len + 4 > ep)
+		{	ret = SFE_MALFORMED_FILE ;
+			goto free_tag_out ;
+			} ;
+		if (tag_len > tag_size - 1)
+		{	free (tag) ;
+			tag_size = tag_len + 1 ;
+			tag = malloc (tag_size) ;
+			/* Unlikely */
+			if (!tag)
+				return SFE_MALLOC_FAILED ;
+			} ;
+		memcpy (tag, p, tag_len) ; p += tag_len ;
+		tag [tag_len] = '\0' ;
+		psf_log_printf (psf, "  Vendor: %s\n", tag) ;
+		} ;
+
+	/*
+	** List of tags of the form NAME=value
+	** Allowable characters for NAME are the same as shell variable names.
+	*/
+	ntags = read_32bit_size_t (p) ;
+	p += 4 ;
+	for (i = 0 ; i < ntags ; i++)
+	{	if (p + 4 > ep)
+		{	ret = SFE_MALFORMED_FILE ;
+			goto free_tag_out ;
+			} ;
+		tag_len = read_32bit_size_t (p) ;
+		p += 4 ;
+		if (p + tag_len > ep)
+		{	ret = SFE_MALFORMED_FILE ;
+			goto free_tag_out ;
+			} ;
+		if (tag_len > tag_size - 1)
+		{	free (tag) ;
+			tag_size = tag_len + 1 ;
+			tag = malloc (tag_size) ;
+			/* Unlikely */
+			if (!tag)
+				return SFE_MALLOC_FAILED ;
+			} ;
+		memcpy (tag, p, tag_len) ; p += tag_len ;
+		tag [tag_len] = '\0' ;
+		psf_log_printf (psf, "  %s\n", tag) ;
+		for (c = tag ; *c ; c++)
+		{	if (*c == '=')
+				break ;
+			*c = toupper (*c) ;
+			} ;
+		if (!c)
+		{	psf_log_printf (psf, "Malformed Vorbis comment, no '=' found.\n") ;
+			continue ;
+			} ;
+		*c = '\0' ;
+		if ((id = vorbiscomment_lookup_id (tag)) != 0)
+			psf_store_string (psf, id, c + 1) ;
+		} ;
+
+free_tag_out:
+	if (tag != NULL)
+		free (tag) ;
+	return ret ;
+} /* vorbiscomment_read_tags */
+
+int
+vorbiscomment_write_tags (SF_PRIVATE *psf, ogg_packet *packet, vorbiscomment_ident *ident, const char *vendor, int targetsize)
+{	int i, ntags ;
+	int tags_start ;
+	const char *tag_name ;
+	int tag_name_len, tag_body_len ;
+
+	psf->header.ptr [0] = 0 ;
+	psf->header.indx = 0 ;
+
+	/* Packet identifier */
+	if (ident)
+		psf_binheader_writef (psf, "eb", BHWv (ident->ident), BHWz (ident->length)) ;
+
+	/* Manditory Vendor Tag */
+	tag_name_len = vendor ? strlen (vendor) : 0 ;
+	psf_binheader_writef (psf, "e4b", BHW4 (tag_name_len), BHWv (vendor), BHWz (tag_name_len)) ;
+
+	/* Tags Count. Skip for now, write after. */
+	tags_start = psf->header.indx ;
+	psf_binheader_writef (psf, "j", BHWj (4)) ;
+
+	ntags = 0 ;
+	/* Write each tag */
+	for (i = 0 ; i < SF_MAX_STRINGS ; i++)
+	{	if (psf->strings.data [i].type == 0)
+			continue ;
+
+		tag_name = vorbiscomment_lookup_name (psf->strings.data [i].type) ;
+		if (tag_name == NULL)
+			continue ;
+
+		tag_name_len = strlen (tag_name) ;
+		tag_body_len = strlen (psf->strings.storage + psf->strings.data [i].offset) ;
+		if (targetsize > 0 && tag_name_len + tag_body_len + psf->header.indx > targetsize)
+		{	/* If we are out of space, stop now. */
+			return SFE_STR_MAX_DATA ;
+			}
+		psf_binheader_writef (psf, "e4b1b",
+			BHW4 (tag_name_len + 1 + tag_body_len),
+			BHWv (tag_name), BHWz (tag_name_len),
+			BHW1 ('='),
+			BHWv (psf->strings.storage + psf->strings.data [i].offset), BHWz (tag_body_len)) ;
+		ntags++ ;
+		} ;
+
+	if (targetsize < 0)
+	{	/*
+		** Padding.
+		**
+		** Pad to a minimum of -targetsize, but make sure length % 255
+		** = 254 so that we get the most out of the ogg segment lacing.
+		*/
+		psf_binheader_writef (psf, "z", BHWz ((psf->header.indx + -targetsize + 255) / 255 * 255 - 1)) ;
+		}
+	else if (targetsize > 0)
+		psf_binheader_writef (psf, "z", BHWz (targetsize - psf->header.indx)) ;
+
+	packet->packet = psf->header.ptr ;
+	packet->bytes = psf->header.indx ;
+	packet->b_o_s = 0 ;
+	packet->e_o_s = 0 ;
+
+	/* Seek back and write the tag count. */
+	psf_binheader_writef (psf, "eo4", BHWo (tags_start), BHW4 (ntags)) ;
+
+	return 0 ;
+} /* vorbiscomment_write_tags */
+
+/*==============================================================================
+** Private functions.
+*/
+
+static int
+vorbiscomment_lookup_id (const char * name)
+{	STR_PAIR *p ;
+
+	for (p = vorbiscomment_mapping ; p->id ; p++)
+	{	if (!strcmp (name, p->name))
+			return p->id ;
+		} ;
+
+	return 0 ;
+} /* vorbiscomment_lookup_id */
+
+static const char *
+vorbiscomment_lookup_name (int id)
+{	STR_PAIR *p ;
+
+	for (p = vorbiscomment_mapping ; p->id ; p++)
+	{	if (p->id == id)
+			return p->name ;
+		} ;
+
+	return NULL ;
+} /* vorbiscomment_lookup_name */
+
+#endif /* HAVE_EXTERNAL_XIPH_LIBS */
diff --git a/src/ogg_vcomment.h b/src/ogg_vcomment.h
new file mode 100644
index 0000000..c25d900
--- /dev/null
+++ b/src/ogg_vcomment.h
@@ -0,0 +1,45 @@
+/*
+** Copyright (C) 2008-2018 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2018 Arthur Taylor <art@ified.ca>
+**
+** This program is free software ; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation ; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program ; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef SF_SRC_OGG_VCOMMENT_H
+#define SF_SRC_OGG_VCOMMENT_H
+
+/*
+** Voriscomment identifier. Some Ogg stream embedding schemes require it.
+*/
+typedef struct
+{	const char *ident ;
+	int length ;
+} vorbiscomment_ident ;
+
+/*
+** Read all vorbiscomment tags from *packet. Tags which match ones used
+** by libsndfile strings are loaded into *psf. Ogg streams which require an
+** identifier for the tags packet should pass it in *ident.
+*/
+int vorbiscomment_read_tags (SF_PRIVATE *psf, ogg_packet *packet, vorbiscomment_ident *ident) ;
+
+/*
+** Write metadata strings stored in *psf to *packet. The packet is optionally
+** prefixed with *ident. The always-present vendor field should be the library
+** used for encoding the audio data.
+*/
+int vorbiscomment_write_tags (SF_PRIVATE *psf, ogg_packet *packet, vorbiscomment_ident *ident, const char *vendor, int targetsize) ;
+
+#endif /* SF_SRC_OGG_VCOMMENT_H */
diff --git a/src/ogg_vorbis.c b/src/ogg_vorbis.c
new file mode 100644
index 0000000..f9428ed
--- /dev/null
+++ b/src/ogg_vorbis.c
@@ -0,0 +1,1057 @@
+/*
+** Copyright (C) 2018-2021 Arthur Taylor <art@ified.ca>
+** Copyright (C) 2002-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2002-2005 Michael Smith <msmith@xiph.org>
+** Copyright (C) 2007 John ffitch
+**
+** This program is free software ; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation ; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program ; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*
+** Much of this code is based on the examples in libvorbis from the
+** XIPHOPHORUS Company http://www.xiph.org/ which has a BSD-style Licence
+** Copyright (c) 2002, Xiph.org Foundation
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+**
+** - Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+**
+** - Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** - Neither the name of the Xiph.org Foundation nor the names of its
+** contributors may be used to endorse or promote products derived from
+** this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION
+** OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE,
+** DATA, OR PROFITS ; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include <math.h>
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#else
+#include "sf_unistd.h"
+#endif
+
+#include "sndfile.h"
+#include "sfendian.h"
+#include "common.h"
+
+#if HAVE_EXTERNAL_XIPH_LIBS
+
+#include <ogg/ogg.h>
+#include <vorbis/codec.h>
+#include <vorbis/vorbisenc.h>
+
+#include "ogg.h"
+
+/* How many seconds in the future to not bother bisection searching for. */
+#define VORBIS_SEEK_THRESHOLD 2
+
+typedef int convert_func (SF_PRIVATE *psf, int, void *, int, int, float **) ;
+
+static int	vorbis_read_header (SF_PRIVATE *psf) ;
+static int	vorbis_write_header (SF_PRIVATE *psf, int calc_length) ;
+static int	vorbis_close (SF_PRIVATE *psf) ;
+static int	vorbis_command (SF_PRIVATE *psf, int command, void *data, int datasize) ;
+static int	vorbis_byterate (SF_PRIVATE *psf) ;
+static int	vorbis_calculate_granulepos (SF_PRIVATE *psf, uint64_t *gp_out) ;
+static int	vorbis_skip (SF_PRIVATE *psf, uint64_t target_gp) ;
+static int	vorbis_seek_trysearch (SF_PRIVATE *psf, uint64_t target_gp) ;
+static sf_count_t	vorbis_seek (SF_PRIVATE *psf, int mode, sf_count_t offset) ;
+static sf_count_t	vorbis_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
+static sf_count_t	vorbis_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
+static sf_count_t	vorbis_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
+static sf_count_t	vorbis_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
+static sf_count_t	vorbis_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
+static sf_count_t	vorbis_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
+static sf_count_t	vorbis_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
+static sf_count_t	vorbis_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
+static sf_count_t	vorbis_read_sample (SF_PRIVATE *psf, void *ptr, sf_count_t lens, convert_func *transfn) ;
+static int	vorbis_rnull (SF_PRIVATE *psf, int samples, void *vptr, int off , int channels, float **pcm) ;
+
+typedef struct
+{	int id ;
+	const char *name ;
+} STR_PAIRS ;
+
+
+/* See https://xiph.org/vorbis/doc/v-comment.html */
+static STR_PAIRS vorbis_metatypes [] =
+{	{	SF_STR_TITLE,		"Title" },
+	{	SF_STR_COPYRIGHT,	"Copyright" },
+	{	SF_STR_SOFTWARE,	"Software" },
+	{	SF_STR_ARTIST,		"Artist" },
+	{	SF_STR_COMMENT,		"Comment" },
+	{	SF_STR_DATE,		"Date" },
+	{	SF_STR_ALBUM,		"Album" },
+	{	SF_STR_LICENSE,		"License" },
+	{	SF_STR_TRACKNUMBER,	"Tracknumber" },
+	{	SF_STR_GENRE,		"Genre" },
+} ;
+
+typedef struct
+{	/* Current granule position. */
+	uint64_t gp ;
+	/* Struct that stores all the static vorbis bitstream settings */
+	vorbis_info	vinfo ;
+	/* Struct that stores all the bitstream user comments */
+	vorbis_comment vcomment ;
+	/* Central working state for the packet->PCM decoder */
+	vorbis_dsp_state vdsp ;
+	/* Local working space for packet->PCM decode */
+	vorbis_block vblock ;
+	/* Encoding quality in range [0.0, 1.0]. */
+	double quality ;
+	/* Offset of the first samples' granule position. */
+	uint64_t pcm_start ;
+	/* Last valid samples' granule position. */
+	uint64_t pcm_end ;
+	/* File offset of the start of the last page. */
+	sf_count_t last_page ;
+} VORBIS_PRIVATE ;
+
+static int
+vorbis_read_header (SF_PRIVATE *psf)
+{	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+	VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ;
+	int printed_metadata_msg = 0 ;
+	int i, nn ;
+	sf_count_t last_page ;
+	sf_count_t saved_offset ;
+
+	/*
+	**  The first page of the Ogg stream we are told to try and open as Vorbis
+	**  has already been loaded into odata->ostream by ogg_open().
+	**
+	**	Extract the initial header from the first page and verify that the
+	**	Ogg bitstream is in fact Vorbis data.
+	*/
+
+	vorbis_info_init (&vdata->vinfo) ;
+	vorbis_comment_init (&vdata->vcomment) ;
+
+	if (!odata->opacket.b_o_s)
+	{	psf_log_printf (psf, "Vorbis: First packet does not have a beginning-of-stream bit.\n") ;
+		return SFE_MALFORMED_FILE ;
+		}
+
+	if (ogg_stream_packetpeek (&odata->ostream, NULL))
+	{	psf_log_printf (psf, "Vorbis: First page contains extraneous packets!\n") ;
+		return SFE_MALFORMED_FILE ;
+		}
+
+	if (vorbis_synthesis_headerin (&vdata->vinfo, &vdata->vcomment, &odata->opacket) < 0)
+	{	/* Error case ; not a vorbis header. */
+		psf_log_printf (psf, "Found Vorbis in stream header, but vorbis_synthesis_headerin failed.\n") ;
+		return SFE_MALFORMED_FILE ;
+		} ;
+
+	/*
+	**	At this point, we're sure we're Vorbis.	We've set up the logical (Ogg)
+	**	bitstream decoder. Get the comment and codebook headers and set up the
+	**	Vorbis decoder.
+	**
+	**	The next two packets in order are the comment and codebook headers.
+	**	They're likely large and may span multiple pages.  Thus we read
+	**	and submit data until we get our two packets, watching that no
+	**	pages are missing.  If a page is missing, error out ; losing a
+	**	header page is the only place where missing data is fatal.
+	*/
+
+	i = 0 ;			/* Count of number of packets read */
+	while (i < 2)
+	{	nn = ogg_stream_packetout (&odata->ostream, &odata->opacket) ;
+
+		if (nn == 0)
+		{	nn = ogg_stream_next_page (psf, odata) ;
+			if (nn == 0)
+			{	psf_log_printf (psf, "End of file before finding all Vorbis headers!\n") ;
+				return SFE_MALFORMED_FILE ;
+				} ;
+			if (nn == -1)
+			{	psf_log_printf (psf, "Error reading file while finding Vorbis headers!\n") ;
+				return psf->error ;
+				} ;
+			continue ;
+			}
+
+		if (nn < 0)
+		{	/* A hole while reading headers. This could be bad. */
+			psf_log_printf (psf, "Corrupt secondary header.	Exiting.\n") ;
+			return SFE_MALFORMED_FILE ;
+			} ;
+
+		vorbis_synthesis_headerin (&vdata->vinfo, &vdata->vcomment, &odata->opacket) ;
+		i++ ;
+		} ;
+
+	/* Check for extraneous packets in the last headers page. */
+	while (ogg_stream_packetout (&odata->ostream, &odata->opacket) == 1)
+	{	i++ ;
+		}
+	if (i > 2)
+		psf_log_printf (psf, "Vorbis: stream has extraneous header packets.\n") ;
+
+	psf_log_printf (psf, "Bitstream is %d channel, %D Hz\n", vdata->vinfo.channels, vdata->vinfo.rate) ;
+	psf_log_printf (psf, "Encoded by : %s\n", vdata->vcomment.vendor) ;
+
+	/* Save the offset of the first payload page */
+	psf->dataoffset	= ogg_sync_ftell (psf) ;
+
+	/*
+	**	Calculate the granule position offset. The first page with a payload
+	**	packet shouldn't end in a continued packet. The difference between the
+	**	page's granule position and the sum of frames on the page tells us the
+	**	granule position offset.
+	**	See https://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-132000A.2
+	*/
+	ogg_stream_unpack_page (psf, odata) ;
+	vorbis_calculate_granulepos (psf, &vdata->pcm_start) ;
+	vdata->gp = vdata->pcm_start ;
+
+	/*
+	**	Find the end of the stream, save it. Only works if the file is seekable.
+	*/
+	vdata->pcm_end = (uint64_t) -1 ;
+	psf->datalength = psf->filelength ;
+	if (!psf->is_pipe)
+	{	saved_offset = ogg_sync_ftell (psf) ;
+		last_page = ogg_sync_last_page_before (psf, odata, &vdata->pcm_end, psf->filelength, odata->ostream.serialno) ;
+		if (last_page > 0)
+		{	if (!ogg_page_eos (&odata->opage))
+				psf_log_printf (psf, "Ogg: Last page lacks an end-of-stream bit.\n") ;
+			psf->datalength = last_page + odata->opage.header_len + odata->opage.body_len - psf->dataoffset ;
+			if (psf->datalength + psf->dataoffset < psf->filelength)
+				psf_log_printf (psf, "Ogg: Junk after the last page.\n") ;
+			vdata->last_page = last_page ;
+			} ;
+
+		ogg_sync_fseek (psf, saved_offset, SEEK_SET) ;
+		}
+
+	psf_log_printf (psf, "PCM offset  : %D\n", vdata->pcm_start) ;
+	if (vdata->pcm_end != (uint64_t) -1)
+		psf_log_printf (psf, "PCM end     : %D\n", vdata->pcm_end) ;
+	else
+		psf_log_printf (psf, "PCM end     : unknown\n") ;
+
+	/* Throw the comments plus a few lines about the bitstream we're decoding. */
+	for (i = 0 ; i < ARRAY_LEN (vorbis_metatypes) ; i++)
+	{	char *dd ;
+
+		dd = vorbis_comment_query (&vdata->vcomment, vorbis_metatypes [i].name, 0) ;
+		if (dd == NULL)
+			continue ;
+
+		if (printed_metadata_msg == 0)
+		{	psf_log_printf (psf, "Metadata :\n") ;
+			printed_metadata_msg = 1 ;
+			} ;
+
+		psf_store_string (psf, vorbis_metatypes [i].id, dd) ;
+		psf_log_printf (psf, "  %-10s : %s\n", vorbis_metatypes [i].name, dd) ;
+		} ;
+	psf_log_printf (psf, "End\n") ;
+
+	psf->sf.samplerate	= vdata->vinfo.rate ;
+	psf->sf.channels	= vdata->vinfo.channels ;
+	psf->sf.format		= SF_FORMAT_OGG | SF_FORMAT_VORBIS ;
+	psf->sf.frames		= (vdata->pcm_end != (uint64_t) -1) ? vdata->pcm_end - vdata->pcm_start : SF_COUNT_MAX ;
+
+	/*	OK, got and parsed all three headers. Initialize the Vorbis
+	**	packet->PCM decoder.
+	**	Central decode state. */
+	vorbis_synthesis_init (&vdata->vdsp, &vdata->vinfo) ;
+
+	/*	Local state for most of the decode so multiple block decodes can
+	**	proceed in parallel. We could init multiple vorbis_block structures
+	**	for vdsp here. */
+	vorbis_block_init (&vdata->vdsp, &vdata->vblock) ;
+
+	return 0 ;
+} /* vorbis_read_header */
+
+static int
+vorbis_write_header (SF_PRIVATE *psf, int UNUSED (calc_length))
+{
+	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+	VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ;
+	int k, ret ;
+
+	vorbis_info_init (&vdata->vinfo) ;
+
+	/* The style of encoding should be selectable here, VBR quality mode. */
+	ret = vorbis_encode_init_vbr (&vdata->vinfo, psf->sf.channels, psf->sf.samplerate, vdata->quality) ;
+
+#if 0
+	ret = vorbis_encode_init (&vdata->vinfo, psf->sf.channels, psf->sf.samplerate, -1, 128000, -1) ; /* average bitrate mode */
+	ret = (	vorbis_encode_setup_managed (&vdata->vinfo, psf->sf.channels, psf->sf.samplerate, -1, 128000, -1)
+			|| vorbis_encode_ctl (&vdata->vinfo, OV_ECTL_RATEMANAGE_AVG, NULL)
+			|| vorbis_encode_setup_init (&vdata->vinfo)
+			) ;
+#endif
+	if (ret)
+		return SFE_BAD_OPEN_FORMAT ;
+
+	vdata->gp = 0 ;
+
+	/* add a comment */
+	vorbis_comment_init (&vdata->vcomment) ;
+
+	vorbis_comment_add_tag (&vdata->vcomment, "ENCODER", "libsndfile") ;
+	for (k = 0 ; k < SF_MAX_STRINGS ; k++)
+	{	const char * name ;
+
+		if (psf->strings.data [k].type == 0)
+			break ;
+
+		switch (psf->strings.data [k].type)
+		{	case SF_STR_TITLE :			name = "TITLE" ; break ;
+			case SF_STR_COPYRIGHT :		name = "COPYRIGHT" ; break ;
+			case SF_STR_SOFTWARE :		name = "SOFTWARE" ; break ;
+			case SF_STR_ARTIST :		name = "ARTIST" ; break ;
+			case SF_STR_COMMENT :		name = "COMMENT" ; break ;
+			case SF_STR_DATE :			name = "DATE" ; break ;
+			case SF_STR_ALBUM :			name = "ALBUM" ; break ;
+			case SF_STR_LICENSE :		name = "LICENSE" ; break ;
+			case SF_STR_TRACKNUMBER :	name = "Tracknumber" ; break ;
+			case SF_STR_GENRE :			name = "Genre" ; break ;
+
+			default : continue ;
+			} ;
+
+		vorbis_comment_add_tag (&vdata->vcomment, name, psf->strings.storage + psf->strings.data [k].offset) ;
+		} ;
+
+	/* set up the analysis state and auxiliary encoding storage */
+	vorbis_analysis_init (&vdata->vdsp, &vdata->vinfo) ;
+	vorbis_block_init (&vdata->vdsp, &vdata->vblock) ;
+
+	/*
+	**	Set up our packet->stream encoder.
+	**	Pick a random serial number ; that way we can more likely build
+	**	chained streams just by concatenation.
+	*/
+
+	ogg_stream_init (&odata->ostream, psf_rand_int32 ()) ;
+
+	/* Vorbis streams begin with three headers ; the initial header (with
+	   most of the codec setup parameters) which is mandated by the Ogg
+	   bitstream spec.  The second header holds any comment fields.	 The
+	   third header holds the bitstream codebook.  We merely need to
+	   make the headers, then pass them to libvorbis one at a time ;
+	   libvorbis handles the additional Ogg bitstream constraints */
+
+	{	ogg_packet header ;
+		ogg_packet header_comm ;
+		ogg_packet header_code ;
+		int result ;
+
+		vorbis_analysis_headerout (&vdata->vdsp, &vdata->vcomment, &header, &header_comm, &header_code) ;
+		ogg_stream_packetin (&odata->ostream, &header) ; /* automatically placed in its own page */
+		ogg_stream_packetin (&odata->ostream, &header_comm) ;
+		ogg_stream_packetin (&odata->ostream, &header_code) ;
+
+		/* This ensures the actual
+		 * audio data will start on a new page, as per spec
+		 */
+		while ((result = ogg_stream_flush (&odata->ostream, &odata->opage)) != 0)
+		{	ogg_write_page (psf, &odata->opage) ;
+			} ;
+	}
+
+	return 0 ;
+} /* vorbis_write_header */
+
+static int
+vorbis_close (SF_PRIVATE *psf)
+{	OGG_PRIVATE* odata = psf->container_data ;
+	VORBIS_PRIVATE *vdata = psf->codec_data ;
+
+	if (odata == NULL || vdata == NULL)
+		return 0 ;
+
+	/*	Clean up this logical bitstream ; before exit we shuld see if we're
+	**	followed by another [chained]. */
+
+	if (psf->file.mode == SFM_WRITE)
+	{
+		if (psf->write_current <= 0)
+			vorbis_write_header (psf, 0) ;
+
+		vorbis_analysis_wrote (&vdata->vdsp, 0) ;
+		while (vorbis_analysis_blockout (&vdata->vdsp, &vdata->vblock) == 1)
+		{
+
+		/* analysis, assume we want to use bitrate management */
+			vorbis_analysis (&vdata->vblock, NULL) ;
+			vorbis_bitrate_addblock (&vdata->vblock) ;
+
+			while (vorbis_bitrate_flushpacket (&vdata->vdsp, &odata->opacket))
+			{	/* weld the packet into the bitstream */
+				ogg_stream_packetin (&odata->ostream, &odata->opacket) ;
+
+				/* write out pages (if any) */
+				while (!odata->eos)
+				{	int result = ogg_stream_pageout (&odata->ostream, &odata->opage) ;
+					if (result == 0) break ;
+					ogg_write_page (psf, &odata->opage) ;
+
+		/* this could be set above, but for illustrative purposes, I do
+		   it here (to show that vorbis does know where the stream ends) */
+
+					if (ogg_page_eos (&odata->opage)) odata->eos = 1 ;
+				}
+			}
+		}
+	}
+
+	/* ogg_page and ogg_packet structs always point to storage in
+	   libvorbis.  They are never freed or manipulated directly */
+
+	vorbis_block_clear (&vdata->vblock) ;
+	vorbis_dsp_clear (&vdata->vdsp) ;
+	vorbis_comment_clear (&vdata->vcomment) ;
+	vorbis_info_clear (&vdata->vinfo) ;
+
+	return 0 ;
+} /* vorbis_close */
+
+int
+ogg_vorbis_open (SF_PRIVATE *psf)
+{	OGG_PRIVATE* odata = psf->container_data ;
+	VORBIS_PRIVATE* vdata ;
+	int	error = 0 ;
+
+	if (odata == NULL)
+	{	psf_log_printf (psf, "%s : odata is NULL???\n", __func__) ;
+		return SFE_INTERNAL ;
+		} ;
+
+	vdata = calloc (1, sizeof (VORBIS_PRIVATE)) ;
+	psf->codec_data = vdata ;
+
+	if (psf->file.mode == SFM_RDWR)
+		return SFE_BAD_MODE_RW ;
+
+	psf_log_printf (psf, "Vorbis library version : %s\n", vorbis_version_string ()) ;
+
+	if (psf->file.mode == SFM_READ)
+	{	if ((error = vorbis_read_header (psf)))
+			return error ;
+
+		psf->read_short		= vorbis_read_s ;
+		psf->read_int		= vorbis_read_i ;
+		psf->read_float		= vorbis_read_f ;
+		psf->read_double	= vorbis_read_d ;
+		} ;
+
+	psf->codec_close = vorbis_close ;
+	if (psf->file.mode == SFM_WRITE)
+	{
+		/* Set the default vorbis quality here. */
+		vdata->quality = 0.4 ;
+
+		psf->write_header	= vorbis_write_header ;
+		psf->write_short	= vorbis_write_s ;
+		psf->write_int		= vorbis_write_i ;
+		psf->write_float	= vorbis_write_f ;
+		psf->write_double	= vorbis_write_d ;
+
+		psf->sf.frames = 0 ;
+		psf->datalength = 0 ;
+		psf->filelength = 0 ;
+		psf->dataoffset = 0 ;
+		psf->strings.flags = SF_STR_ALLOW_START ;
+		} ;
+
+	psf->seek = vorbis_seek ;
+	psf->command = vorbis_command ;
+	psf->byterate = vorbis_byterate ;
+	psf->sf.format = SF_FORMAT_OGG | SF_FORMAT_VORBIS ;
+	psf->sf.sections = 1 ;
+
+	return error ;
+} /* ogg_vorbis_open */
+
+static int
+vorbis_command (SF_PRIVATE *psf, int command, void * data, int datasize)
+{	OGG_PRIVATE* odata = psf->container_data ;
+	VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ;
+
+	switch (command)
+	{	case SFC_SET_COMPRESSION_LEVEL :
+			if (data == NULL || datasize != sizeof (double))
+				return SF_FALSE ;
+
+			if (psf->have_written)
+				return SF_FALSE ;
+
+			vdata->quality = 1.0 - *((double *) data) ;
+
+			/* Clip range. */
+			vdata->quality = SF_MAX (0.0, SF_MIN (1.0, vdata->quality)) ;
+
+			psf_log_printf (psf, "%s : Setting SFC_SET_VBR_ENCODING_QUALITY to %f.\n", __func__, vdata->quality) ;
+			return SF_TRUE ;
+
+		case SFC_GET_OGG_STREAM_SERIALNO :
+			if (data == NULL || datasize != sizeof (int32_t))
+				return SF_FALSE ;
+
+			*((int32_t *) data) = odata->ostream.serialno ;
+			return SF_TRUE ;
+
+		default :
+			return SF_FALSE ;
+		} ;
+
+	return SF_FALSE ;
+} /* vorbis_command */
+
+static int
+vorbis_rnull (SF_PRIVATE *UNUSED (psf), int samples, void *UNUSED (vptr), int UNUSED (off) , int channels, float **UNUSED (pcm))
+{
+	return samples * channels ;
+} /* vorbis_rnull */
+
+static int
+vorbis_rshort (SF_PRIVATE *psf, int samples, void *vptr, int off, int channels, float **pcm)
+{
+	short *ptr = (short*) vptr + off ;
+	int i = 0, j, n ;
+	if (psf->float_int_mult)
+	{
+		float inverse = 1.0 / psf->float_max ;
+		for (j = 0 ; j < samples ; j++)
+			for (n = 0 ; n < channels ; n++)
+				ptr [i++] = psf_lrintf ((pcm [n][j] * inverse) * 32767.0f) ;
+	}
+	else
+	{
+		for (j = 0 ; j < samples ; j++)
+			for (n = 0 ; n < channels ; n++)
+				ptr [i++] = psf_lrintf (pcm [n][j] * 32767.0f) ;
+	}
+	return i ;
+} /* vorbis_rshort */
+
+static int
+vorbis_rint (SF_PRIVATE *psf, int samples, void *vptr, int off, int channels, float **pcm)
+{
+	int *ptr = (int*) vptr + off ;
+	int i = 0, j, n ;
+
+	if (psf->float_int_mult)
+	{
+		float inverse = 1.0 / psf->float_max ;
+		for (j = 0 ; j < samples ; j++)
+			for (n = 0 ; n < channels ; n++)
+				ptr [i++] = psf_lrintf ((pcm [n][j] * inverse) * 2147483647.0f) ;
+	}
+	else
+	{
+		for (j = 0 ; j < samples ; j++)
+			for (n = 0 ; n < channels ; n++)
+				ptr [i++] = psf_lrintf (pcm [n][j] * 2147483647.0f) ;
+	}
+	return i ;
+} /* vorbis_rint */
+
+static int
+vorbis_rfloat (SF_PRIVATE *UNUSED (psf), int samples, void *vptr, int off, int channels, float **pcm)
+{
+	float *ptr = (float*) vptr + off ;
+	int i = 0, j, n ;
+	for (j = 0 ; j < samples ; j++)
+		for (n = 0 ; n < channels ; n++)
+			ptr [i++] = pcm [n][j] ;
+	return i ;
+} /* vorbis_rfloat */
+
+static int
+vorbis_rdouble (SF_PRIVATE *UNUSED (psf), int samples, void *vptr, int off, int channels, float **pcm)
+{
+	double *ptr = (double*) vptr + off ;
+	int i = 0, j, n ;
+	for (j = 0 ; j < samples ; j++)
+		for (n = 0 ; n < channels ; n++)
+			ptr [i++] = pcm [n][j] ;
+	return i ;
+} /* vorbis_rdouble */
+
+
+static sf_count_t
+vorbis_read_sample (SF_PRIVATE *psf, void *ptr, sf_count_t lens, convert_func *transfn)
+{	VORBIS_PRIVATE *vdata = psf->codec_data ;
+	OGG_PRIVATE *odata = psf->container_data ;
+	int len, samples, i = 0 , nn ;
+	float **pcm ;
+
+	len = lens / psf->sf.channels ;
+
+	while (len > 0)
+	{	/*
+		** pcm is a multichannel float vector.	 In stereo, for
+		** example, pcm [0] is left, and pcm [1] is right.	 samples is
+		** the size of each channel.	 Convert the float values
+		** (-1.<=range<=1.) to whatever PCM format and write it out.
+		*/
+		while ((samples = vorbis_synthesis_pcmout (&vdata->vdsp, &pcm)) > 0)
+		{	if (samples > len) samples = len ;
+			i += transfn (psf, samples, ptr, i, psf->sf.channels, pcm) ;
+			len -= samples ;
+			/* tell libvorbis how many samples we actually consumed */
+			vorbis_synthesis_read (&vdata->vdsp, samples) ;
+			vdata->gp += samples ;
+			if (len == 0)
+				return i ;
+			} ;
+
+		/* Out of samples, load the next packet. */
+		if (odata->pkt_indx == odata->pkt_len)
+		{	/* Page out of packets, load and unpack the next page. */
+			nn = ogg_stream_unpack_page (psf, odata) ;
+			if (nn <= 0)
+				return i ;
+			if (nn == 2)
+			{	/* Ran over a hole. gp is now out of date, need to recalculate. */
+				vorbis_synthesis_restart (&vdata->vdsp) ;
+				vorbis_calculate_granulepos (psf, &vdata->gp) ;
+				}
+			} ;
+
+		/* Decode the packet */
+		if (vorbis_synthesis (&vdata->vblock, &(odata->pkt [odata->pkt_indx])) == 0) /* test for success! */
+			vorbis_synthesis_blockin (&vdata->vdsp, &vdata->vblock) ;
+		odata->pkt_indx++ ;
+		} ;
+
+	return i ;
+} /* vorbis_read_sample */
+
+static sf_count_t
+vorbis_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t lens)
+{	return vorbis_read_sample (psf, (void*) ptr, lens, vorbis_rshort) ;
+} /* vorbis_read_s */
+
+static sf_count_t
+vorbis_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t lens)
+{	return vorbis_read_sample (psf, (void*) ptr, lens, vorbis_rint) ;
+} /* vorbis_read_i */
+
+static sf_count_t
+vorbis_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t lens)
+{	return vorbis_read_sample (psf, (void*) ptr, lens, vorbis_rfloat) ;
+} /* vorbis_read_f */
+
+static sf_count_t
+vorbis_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t lens)
+{	return vorbis_read_sample (psf, (void*) ptr, lens, vorbis_rdouble) ;
+} /* vorbis_read_d */
+
+/*==============================================================================
+*/
+
+static void
+vorbis_write_samples (SF_PRIVATE *psf, OGG_PRIVATE *odata, VORBIS_PRIVATE *vdata, int in_frames)
+{
+	vorbis_analysis_wrote (&vdata->vdsp, in_frames) ;
+
+	/*
+	**	Vorbis does some data preanalysis, then divvies up blocks for
+	**	more involved (potentially parallel) processing. Get a single
+	**	block for encoding now.
+	*/
+	while (vorbis_analysis_blockout (&vdata->vdsp, &vdata->vblock) == 1)
+	{
+		/* analysis, assume we want to use bitrate management */
+		vorbis_analysis (&vdata->vblock, NULL) ;
+		vorbis_bitrate_addblock (&vdata->vblock) ;
+
+		while (vorbis_bitrate_flushpacket (&vdata->vdsp, &odata->opacket))
+		{
+			/* weld the packet into the bitstream */
+			ogg_stream_packetin (&odata->ostream, &odata->opacket) ;
+
+			/* write out pages (if any) */
+			while (!odata->eos)
+			{	int result = ogg_stream_pageout (&odata->ostream, &odata->opage) ;
+				if (result == 0)
+					break ;
+				ogg_write_page (psf, &odata->opage) ;
+
+				/*	This could be set above, but for illustrative purposes, I do
+				**	it here (to show that vorbis does know where the stream ends) */
+				if (ogg_page_eos (&odata->opage))
+					odata->eos = 1 ;
+				} ;
+			} ;
+		} ;
+
+	vdata->gp += in_frames ;
+} /* vorbis_write_data */
+
+
+static sf_count_t
+vorbis_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t lens)
+{
+	int i, m, j = 0 ;
+	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+	VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ;
+	int in_frames = lens / psf->sf.channels ;
+	float **buffer = vorbis_analysis_buffer (&vdata->vdsp, in_frames) ;
+	for (i = 0 ; i < in_frames ; i++)
+		for (m = 0 ; m < psf->sf.channels ; m++)
+			buffer [m][i] = (float) (ptr [j++]) / 32767.0f ;
+
+	vorbis_write_samples (psf, odata, vdata, in_frames) ;
+
+	return lens ;
+} /* vorbis_write_s */
+
+static sf_count_t
+vorbis_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t lens)
+{	int i, m, j = 0 ;
+	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+	VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ;
+	int in_frames = lens / psf->sf.channels ;
+	float **buffer = vorbis_analysis_buffer (&vdata->vdsp, in_frames) ;
+	for (i = 0 ; i < in_frames ; i++)
+		for (m = 0 ; m < psf->sf.channels ; m++)
+			buffer [m][i] = (float) (ptr [j++]) / 2147483647.0f ;
+
+	vorbis_write_samples (psf, odata, vdata, in_frames) ;
+
+	return lens ;
+} /* vorbis_write_i */
+
+static sf_count_t
+vorbis_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t lens)
+{	int i, m, j = 0 ;
+	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+	VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ;
+	int in_frames = lens / psf->sf.channels ;
+	float **buffer = vorbis_analysis_buffer (&vdata->vdsp, in_frames) ;
+	for (i = 0 ; i < in_frames ; i++)
+		for (m = 0 ; m < psf->sf.channels ; m++)
+			buffer [m][i] = ptr [j++] ;
+
+	vorbis_write_samples (psf, odata, vdata, in_frames) ;
+
+	return lens ;
+} /* vorbis_write_f */
+
+static sf_count_t
+vorbis_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t lens)
+{	int i, m, j = 0 ;
+	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+	VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ;
+	int in_frames = lens / psf->sf.channels ;
+	float **buffer = vorbis_analysis_buffer (&vdata->vdsp, in_frames) ;
+	for (i = 0 ; i < in_frames ; i++)
+		for (m = 0 ; m < psf->sf.channels ; m++)
+			buffer [m][i] = (float) ptr [j++] ;
+
+	vorbis_write_samples (psf, odata, vdata, in_frames) ;
+
+	return lens ;
+} /* vorbis_write_d */
+
+static int
+vorbis_skip (SF_PRIVATE *psf, uint64_t target)
+{	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+	VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ;
+	ogg_packet *pkt ;
+	int thisblock, lastblock, nn ;
+	const int blocksize = vorbis_info_blocksize (&vdata->vinfo, 1) ;
+
+	/*	Read out any samples that may be in the decoder from a seek without a
+	**	search. */
+	thisblock = vorbis_synthesis_pcmout (&vdata->vdsp, NULL) ;
+	if (thisblock > 0)
+	{	if ((uint64_t) thisblock + vdata->gp >= target)
+			thisblock = SF_MIN (thisblock, (int) (target - vdata->gp)) ;
+
+		vorbis_synthesis_read (&vdata->vdsp, thisblock) ;
+		vdata->gp += thisblock ;
+		if (vdata->gp == target)
+			return 0 ;
+		} ;
+
+	/* Read through packets that are before our target */
+	lastblock = 0 ;
+	for ( ; vdata->gp < target ; )
+	{	/* Ensure there are unpacked packets. */
+		if (odata->pkt_indx == odata->pkt_len)
+		{	/* Page out of packets, load and unpack the next page. */
+			nn = ogg_stream_unpack_page (psf, odata) ;
+			if (nn < 0)
+				return nn ;
+			if (nn == 0)
+				break ;
+			if (nn == 2)
+			{	/* Ran over a hole. gp is now out of date, need to recalculate. */
+				vorbis_synthesis_restart (&vdata->vdsp) ;
+				vorbis_calculate_granulepos (psf, &vdata->gp) ;
+				if (target < vdata->gp)
+				{	/* Our target is inside the hole :-( */
+					return 0 ;
+					} ;
+				} ;
+			} ;
+
+		pkt = &odata->pkt [odata->pkt_indx] ;
+		thisblock = vorbis_packet_blocksize (&vdata->vinfo, pkt) ;
+		if (thisblock < 0)
+		{	/* Not an audio packet */
+			odata->pkt_indx++ ;
+			continue ;
+			} ;
+
+		if (lastblock)
+		{	vdata->gp += ((lastblock + thisblock) / 4) ;
+			} ;
+
+		/* Check to see if the block contains our target */
+		if (vdata->gp + ((thisblock + blocksize) / 4) >= target)
+			break ;
+
+		/* Block is before the target. Track for state, but don't decode. */
+		odata->pkt_indx++ ;
+		vorbis_synthesis_trackonly (&vdata->vblock, pkt) ;
+		vorbis_synthesis_blockin (&vdata->vdsp, &vdata->vblock) ;
+		lastblock = thisblock ;
+		} ;
+
+	/*	We are at the correct block, but still need to consume samples to reach
+	**	our target. */
+	vorbis_read_sample (psf, (void *) NULL, (target - vdata->gp) * psf->sf.channels, vorbis_rnull) ;
+
+	return 0 ;
+} /* vorbis_skip */
+
+static int
+vorbis_seek_trysearch (SF_PRIVATE *psf, uint64_t target_gp)
+{	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+	VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ;
+	uint64_t best_gp, search_target_gp ;
+	int ret ;
+
+	/* Can't bisect a file we don't know the end of (cannot seek). */
+	if (vdata->pcm_end == (uint64_t) -1)
+		return 0 ;
+
+	/* If the target is for the near future, don't bother bisecting, just skip
+	** to it. */
+	if (target_gp >= vdata->gp &&
+		target_gp - vdata->gp < ((unsigned) (VORBIS_SEEK_THRESHOLD) * psf->sf.samplerate))
+		return 0 ;
+
+	/*	Search for a position a half large-block before our target. As Vorbis is
+	**	lapped, every sample position come from two blocks, the "left" half of
+	**	one block and the "right" half of the previous block.  The granule
+	**	position of an Ogg page of a Vorbis stream is the sample offset of the
+	**	last finished sample in the stream that can be decoded from a page.  A
+	**	page also contains another half-block of samples waiting to be lapped
+	**	with the first half-block of samples from the next page.
+	**
+	**	Searching for a sample one half of a large block before our target
+	**	guarantees we always load a page containing the previous half block
+	**	required to decode the target.  Downside is we can't use best_gp
+	**	parameter of the page seek function. */
+	search_target_gp = vorbis_info_blocksize (&vdata->vinfo, 1) / 2 ;
+	search_target_gp = search_target_gp < target_gp ? target_gp - search_target_gp : 0 ;
+
+	ret = ogg_stream_seek_page_search (psf, odata, search_target_gp, vdata->pcm_start,
+			vdata->pcm_end, &best_gp, psf->dataoffset, vdata->last_page, vdata->vinfo.rate) ;
+	if (ret < 0)
+		return ret ;
+
+	ret = ogg_stream_unpack_page (psf, odata) ;
+	if (ret > 0)
+	{	/* Reset the decoder, recalculate position */
+		vorbis_synthesis_restart (&vdata->vdsp) ;
+		ret = vorbis_calculate_granulepos (psf, &vdata->gp) ;
+		} ;
+
+	return ret ;
+} /* vorbis_seek_trysearch */
+
+static sf_count_t
+vorbis_seek (SF_PRIVATE *psf, int UNUSED (mode), sf_count_t offset)
+{	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+	VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ;
+	uint64_t target_gp ;
+	int ret ;
+
+	if (odata == NULL || vdata == NULL)
+		return 0 ;
+
+	if (offset < 0)
+	{	psf->error = SFE_BAD_SEEK ;
+		return ((sf_count_t) -1) ;
+		} ;
+
+	if (psf->file.mode == SFM_READ)
+	{	target_gp = (uint64_t) offset + vdata->pcm_start ;
+
+		ret = vorbis_seek_trysearch (psf, target_gp) ;
+
+		if (ret < 0 || vdata->gp > target_gp)
+		{	/* Search failed (bad data?), reset to the beginning of the stream. */
+			psf_log_printf (psf, "Vorbis: Seek search failed. Reading through stream from start.\n") ;
+			ogg_stream_reset_serialno (&odata->ostream, odata->ostream.serialno) ;
+			odata->pkt_len = 0 ;
+			odata->pkt_indx = 0 ;
+			ogg_sync_fseek (psf, psf->dataoffset, SEEK_SET) ;
+			vdata->gp = vdata->pcm_start ;
+			vorbis_synthesis_restart (&vdata->vdsp) ;
+			} ;
+
+		vorbis_skip (psf, target_gp) ;
+
+		return vdata->gp - vdata->pcm_start ;
+		} ;
+
+	psf->error = SFE_BAD_SEEK ;
+	return ((sf_count_t) -1) ;
+} /* vorbis_seek */
+
+static int
+vorbis_byterate (SF_PRIVATE *psf)
+{
+	if (psf->file.mode == SFM_READ)
+		return (psf->datalength * psf->sf.samplerate) / psf->sf.frames ;
+
+	return -1 ;
+} /* vorbis_byterate */
+
+static int
+vorbis_calculate_granulepos (SF_PRIVATE *psf, uint64_t *gp_out)
+{	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+	VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ;
+	ogg_packet *pkt ;
+	uint64_t last_gp ;
+	int thisblock, lastblock, i ;
+	unsigned duration ;
+
+	/*	Calculate the granule position when dropped into the middle of a stream
+	**	with an un-primed decoder.
+	**
+	**	Normally the last unpacked packet contains the granule position of the
+	**	last completed sample from decoding all the blocks in the page's
+	**	packets.  By calculating how many samples we can decode from the blocks
+	**	in the page's packets and subtracting it from the final packet's granule
+	**	position we get the position of the first sample to be output from the
+	**	decoder after it primes.  That is, the current granule position.
+	**
+	**	However, there is an ambiguity if this is the last page of a stream. The
+	**	last page of a stream may have a granule position of fewer samples than
+	**	the page actually contains.  The excess samples are padding leftovers
+	**	for and exact sample length file. */
+
+	if (odata->pkt_len > 0)
+	{	/* Calculate how many samples can be decoded from blocks in this page,
+		** accounting for the fact that blocks are 1/2 lapped. */
+		lastblock = -1 ;
+		duration = 0 ;
+		pkt = odata->pkt ;
+		for (i = 0 ; i < odata->pkt_len ; i++)
+		{	thisblock = vorbis_packet_blocksize (&vdata->vinfo, &pkt [i]) ;
+			if (thisblock >= 0)
+			{	if (lastblock != -1)
+					duration += (lastblock + thisblock) >> 2 ;
+				lastblock = thisblock ;
+				} ;
+			} ;
+
+		pkt = &odata->pkt [odata->pkt_len - 1] ;
+		last_gp = pkt->granulepos ;
+		if (last_gp == (uint64_t) -1)
+		{	psf_log_printf (psf, "Vorbis: Ogg page has no granule position, cannot calculate sample position!\n") ;
+			psf->error = SFE_MALFORMED_FILE ;
+			return -1 ;
+			} ;
+
+		if (pkt->e_o_s)
+		{	if (last_gp <= duration)
+			{	/*	Corner case: One page stream. Ogg/Vorbis spec dictates the
+				**	granule position offset MUST be zero, hence this first (and
+				**	only) page must start at 0. */
+				*gp_out = 0 ;
+				return 1 ;
+				} ;
+
+			/*	Otherwise, we cannot know where we are without looking at the
+			**	blocks of the previous page.  (The granule position of the
+			**	previous page is not enough, we need the block sizes.)
+			**
+			**	We avoid this case by never allowing a bisection search to seek
+			**	beyond the second-to-last page, so the last page is always
+			**	approached with a known location and never dropped into.
+			**
+			**	The only way we should be able to end up here is if there was a
+			**	hole in stream just before the last page, in which case all bets
+			**	are off anyways. */
+			psf_log_printf (psf, "Vorbis: Cannot calculate ambiguous last page duration. Sample count may be wrong.\n") ;
+			} ;
+
+		if (last_gp < duration)
+		{	psf_log_printf (psf, "Vorbis: Granule position is nonsensical! (Missing end-of-stream marker?)\n") ;
+			psf->error = SFE_MALFORMED_FILE ;
+			return -1 ;
+			} ;
+
+		*gp_out = last_gp - duration ;
+		return 1 ;
+		} ;
+
+	return 0 ;
+} /* vorbis_calculate_granulepos */
+
+#else /* HAVE_EXTERNAL_XIPH_LIBS */
+
+int
+ogg_vorbis_open	(SF_PRIVATE *psf)
+{
+	psf_log_printf (psf, "This version of libsndfile was compiled without Ogg/Vorbis support.\n") ;
+	return SFE_UNIMPLEMENTED ;
+} /* ogg_vorbis_open */
+
+#endif
diff --git a/src/paf.c b/src/paf.c
new file mode 100644
index 0000000..871931c
--- /dev/null
+++ b/src/paf.c
@@ -0,0 +1,818 @@
+/*
+** Copyright (C) 1999-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+
+#include "sndfile.h"
+#include "sfendian.h"
+#include "common.h"
+
+/*------------------------------------------------------------------------------
+** Macros to handle big/little endian issues.
+*/
+
+#define FAP_MARKER	(MAKE_MARKER ('f', 'a', 'p', ' '))
+#define PAF_MARKER	(MAKE_MARKER (' ', 'p', 'a', 'f'))
+
+/*------------------------------------------------------------------------------
+** Other defines.
+*/
+
+#define	PAF_HEADER_LENGTH 			2048
+
+#define	PAF24_SAMPLES_PER_BLOCK		10
+#define	PAF24_BLOCK_SIZE			32
+
+/*------------------------------------------------------------------------------
+** Typedefs.
+*/
+
+typedef	struct
+{	int	version ;
+	int	endianness ;
+	int	samplerate ;
+	int	format ;
+	int	channels ;
+	int	source ;
+} PAF_FMT ;
+
+typedef struct
+{	int				max_blocks, channels, blocksize ;
+	int				read_block, write_block, read_count, write_count ;
+	sf_count_t		sample_count ;
+	int				*samples ;
+	int				*block ;
+	int				data [] ; /* ISO C99 struct flexible array. */
+} PAF24_PRIVATE ;
+
+/*------------------------------------------------------------------------------
+** Private static functions.
+*/
+
+static int paf24_init (SF_PRIVATE *psf) ;
+
+static int	paf_read_header	(SF_PRIVATE *psf) ;
+static int	paf_write_header (SF_PRIVATE *psf, int calc_length) ;
+
+static sf_count_t paf24_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
+static sf_count_t paf24_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
+static sf_count_t paf24_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
+static sf_count_t paf24_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
+
+static sf_count_t paf24_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
+static sf_count_t paf24_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
+static sf_count_t paf24_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
+static sf_count_t paf24_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
+
+static sf_count_t paf24_seek (SF_PRIVATE *psf, int mode, sf_count_t offset) ;
+
+enum
+{	PAF_PCM_16 = 0,
+	PAF_PCM_24 = 1,
+	PAF_PCM_S8 = 2
+} ;
+
+/*------------------------------------------------------------------------------
+** Public function.
+*/
+
+int
+paf_open	(SF_PRIVATE *psf)
+{	int		subformat, error, endian ;
+
+ 	psf->dataoffset = PAF_HEADER_LENGTH ;
+
+	if (psf->file.mode == SFM_READ || (psf->file.mode == SFM_RDWR && psf->filelength > 0))
+	{	if ((error = paf_read_header (psf)))
+			return error ;
+		} ;
+
+	subformat = SF_CODEC (psf->sf.format) ;
+
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	if ((SF_CONTAINER (psf->sf.format)) != SF_FORMAT_PAF)
+			return	SFE_BAD_OPEN_FORMAT ;
+
+		endian = SF_ENDIAN (psf->sf.format) ;
+
+		/* PAF is by default big endian. */
+		psf->endian = SF_ENDIAN_BIG ;
+
+		if (endian == SF_ENDIAN_LITTLE || (CPU_IS_LITTLE_ENDIAN && (endian == SF_ENDIAN_CPU)))
+			psf->endian = SF_ENDIAN_LITTLE ;
+
+		if ((error = paf_write_header (psf, SF_FALSE)))
+			return error ;
+
+		psf->write_header = paf_write_header ;
+		} ;
+
+	switch (subformat)
+	{	case SF_FORMAT_PCM_S8 :
+					psf->bytewidth = 1 ;
+					error = pcm_init (psf) ;
+					break ;
+
+		case SF_FORMAT_PCM_16 :
+					psf->bytewidth = 2 ;
+					error = pcm_init (psf) ;
+					break ;
+
+		case SF_FORMAT_PCM_24 :
+					/* No bytewidth because of whacky 24 bit encoding. */
+					error = paf24_init (psf) ;
+					break ;
+
+		default : return SFE_PAF_UNKNOWN_FORMAT ;
+		} ;
+
+	return error ;
+} /* paf_open */
+
+/*------------------------------------------------------------------------------
+*/
+
+static int
+paf_read_header	(SF_PRIVATE *psf)
+{	PAF_FMT		paf_fmt ;
+	int			marker ;
+
+	if (psf->filelength < PAF_HEADER_LENGTH)
+		return SFE_PAF_SHORT_HEADER ;
+
+	memset (&paf_fmt, 0, sizeof (paf_fmt)) ;
+	psf_binheader_readf (psf, "pm", 0, &marker) ;
+
+	psf_log_printf (psf, "Signature   : '%M'\n", marker) ;
+
+	if (marker == PAF_MARKER)
+	{	psf_binheader_readf (psf, "E444444", &(paf_fmt.version), &(paf_fmt.endianness),
+			&(paf_fmt.samplerate), &(paf_fmt.format), &(paf_fmt.channels), &(paf_fmt.source)) ;
+		}
+	else if (marker == FAP_MARKER)
+	{	psf_binheader_readf (psf, "e444444", &(paf_fmt.version), &(paf_fmt.endianness),
+			&(paf_fmt.samplerate), &(paf_fmt.format), &(paf_fmt.channels), &(paf_fmt.source)) ;
+		}
+	else
+		return SFE_PAF_NO_MARKER ;
+
+	psf_log_printf (psf, "Version     : %d\n", paf_fmt.version) ;
+
+	if (paf_fmt.version != 0)
+	{	psf_log_printf (psf, "*** Bad version number. should be zero.\n") ;
+		return SFE_PAF_VERSION ;
+		} ;
+
+	psf_log_printf (psf, "Sample Rate : %d\n", paf_fmt.samplerate) ;
+	psf_log_printf (psf, "Channels    : %d\n", paf_fmt.channels) ;
+
+	psf_log_printf (psf, "Endianness  : %d => ", paf_fmt.endianness) ;
+	if (paf_fmt.endianness)
+	{	psf_log_printf (psf, "Little\n", paf_fmt.endianness) ;
+		psf->endian = SF_ENDIAN_LITTLE ;
+		}
+	else
+	{	psf_log_printf (psf, "Big\n", paf_fmt.endianness) ;
+		psf->endian = SF_ENDIAN_BIG ;
+		} ;
+
+	if (paf_fmt.channels < 1 || paf_fmt.channels > SF_MAX_CHANNELS)
+		return SFE_PAF_BAD_CHANNELS ;
+
+	psf->datalength = psf->filelength - psf->dataoffset ;
+
+	psf_binheader_readf (psf, "p", (int) psf->dataoffset) ;
+
+	psf->sf.samplerate	= paf_fmt.samplerate ;
+	psf->sf.channels 	= paf_fmt.channels ;
+
+	/* Only fill in type major. */
+	psf->sf.format = SF_FORMAT_PAF ;
+
+	psf_log_printf (psf, "Format      : %d => ", paf_fmt.format) ;
+
+	/* PAF is by default big endian. */
+	psf->sf.format |= paf_fmt.endianness ? SF_ENDIAN_LITTLE : SF_ENDIAN_BIG ;
+
+	switch (paf_fmt.format)
+	{	case PAF_PCM_S8 :
+					psf_log_printf (psf, "8 bit linear PCM\n") ;
+					psf->bytewidth = 1 ;
+
+					psf->sf.format |= SF_FORMAT_PCM_S8 ;
+
+					psf->blockwidth = psf->bytewidth * psf->sf.channels ;
+					psf->sf.frames = psf->datalength / psf->blockwidth ;
+					break ;
+
+		case PAF_PCM_16 :
+					psf_log_printf (psf, "16 bit linear PCM\n") ;
+					psf->bytewidth = 2 ;
+
+					psf->sf.format |= SF_FORMAT_PCM_16 ;
+
+					psf->blockwidth = psf->bytewidth * psf->sf.channels ;
+					psf->sf.frames = psf->datalength / psf->blockwidth ;
+					break ;
+
+		case PAF_PCM_24 :
+					psf_log_printf (psf, "24 bit linear PCM\n") ;
+					psf->bytewidth = 3 ;
+
+					psf->sf.format |= SF_FORMAT_PCM_24 ;
+
+					psf->blockwidth = 0 ;
+					psf->sf.frames = PAF24_SAMPLES_PER_BLOCK * psf->datalength /
+											(PAF24_BLOCK_SIZE * psf->sf.channels) ;
+					break ;
+
+		default :	psf_log_printf (psf, "Unknown\n") ;
+					return SFE_PAF_UNKNOWN_FORMAT ;
+					break ;
+		} ;
+
+	psf_log_printf (psf, "Source      : %d => ", paf_fmt.source) ;
+
+	switch (paf_fmt.source)
+	{	case 1 : psf_log_printf (psf, "Analog Recording\n") ;
+					break ;
+		case 2 : psf_log_printf (psf, "Digital Transfer\n") ;
+					break ;
+		case 3 : psf_log_printf (psf, "Multi-track Mixdown\n") ;
+					break ;
+		case 5 : psf_log_printf (psf, "Audio Resulting From DSP Processing\n") ;
+					break ;
+		default : psf_log_printf (psf, "Unknown\n") ;
+					break ;
+		} ;
+
+	return 0 ;
+} /* paf_read_header */
+
+static int
+paf_write_header (SF_PRIVATE *psf, int UNUSED (calc_length))
+{	int			paf_format ;
+
+	/* PAF header already written so no need to re-write. */
+	if (psf_ftell (psf) >= PAF_HEADER_LENGTH)
+		return 0 ;
+
+	psf->dataoffset = PAF_HEADER_LENGTH ;
+
+	switch (SF_CODEC (psf->sf.format))
+	{	case SF_FORMAT_PCM_S8 :
+					paf_format = PAF_PCM_S8 ;
+					break ;
+
+		case SF_FORMAT_PCM_16 :
+					paf_format = PAF_PCM_16 ;
+					break ;
+
+		case SF_FORMAT_PCM_24 :
+					paf_format = PAF_PCM_24 ;
+					break ;
+
+		default : return SFE_PAF_UNKNOWN_FORMAT ;
+		} ;
+
+	/* Reset the current header length to zero. */
+	psf->header.ptr [0] = 0 ;
+	psf->header.indx = 0 ;
+
+	if (psf->endian == SF_ENDIAN_BIG)
+	{	/* Marker, version, endianness, samplerate */
+		psf_binheader_writef (psf, "Em444", BHWm (PAF_MARKER), BHW4 (0), BHW4 (0), BHW4 (psf->sf.samplerate)) ;
+		/* format, channels, source */
+		psf_binheader_writef (psf, "E444", BHW4 (paf_format), BHW4 (psf->sf.channels), BHW4 (0)) ;
+		}
+	else if (psf->endian == SF_ENDIAN_LITTLE)
+	{	/* Marker, version, endianness, samplerate */
+		psf_binheader_writef (psf, "em444", BHWm (FAP_MARKER), BHW4 (0), BHW4 (1), BHW4 (psf->sf.samplerate)) ;
+		/* format, channels, source */
+		psf_binheader_writef (psf, "e444", BHW4 (paf_format), BHW4 (psf->sf.channels), BHW4 (0)) ;
+		} ;
+
+	/* Zero fill to dataoffset. */
+	psf_binheader_writef (psf, "z", BHWz ((size_t) (psf->dataoffset - psf->header.indx))) ;
+
+	psf_fwrite (psf->header.ptr, psf->header.indx, 1, psf) ;
+
+	return psf->error ;
+} /* paf_write_header */
+
+/*===============================================================================
+**	24 bit PAF files have a really weird encoding.
+**  For a mono file, 10 samples (each being 3 bytes) are packed into a 32 byte
+**	block. The 8 ints in this 32 byte block are then endian swapped (as ints)
+**	if necessary before being written to disk.
+**  For a stereo file, blocks of 10 samples from the same channel are encoded
+**  into 32 bytes as for the mono case. The 32 byte blocks are then interleaved
+**	on disk.
+**	Reading has to reverse the above process :-).
+**	Weird!!!
+**
+**	The code below attempts to gain efficiency while maintaining readability.
+*/
+
+static int paf24_read_block (SF_PRIVATE *psf, PAF24_PRIVATE *ppaf24) ;
+static int paf24_write_block (SF_PRIVATE *psf, PAF24_PRIVATE *ppaf24) ;
+static int paf24_close (SF_PRIVATE *psf) ;
+
+
+static int
+paf24_init (SF_PRIVATE *psf)
+{	PAF24_PRIVATE	*ppaf24 ;
+	int	paf24size ;
+
+	paf24size = sizeof (PAF24_PRIVATE) + psf->sf.channels *
+					(PAF24_BLOCK_SIZE + PAF24_SAMPLES_PER_BLOCK * sizeof (int)) ;
+
+	/*
+	**	Not exatly sure why this needs to be here but the tests
+	**	fail without it.
+	*/
+	psf->last_op = 0 ;
+
+	if (! (psf->codec_data = calloc (1, paf24size)))
+		return SFE_MALLOC_FAILED ;
+
+	ppaf24 = (PAF24_PRIVATE*) psf->codec_data ;
+
+	ppaf24->channels	= psf->sf.channels ;
+	ppaf24->samples		= ppaf24->data ;
+	ppaf24->block		= ppaf24->data + PAF24_SAMPLES_PER_BLOCK * ppaf24->channels ;
+
+	ppaf24->blocksize = PAF24_BLOCK_SIZE * ppaf24->channels ;
+
+	if (psf->file.mode == SFM_READ || psf->file.mode == SFM_RDWR)
+	{	paf24_read_block (psf, ppaf24) ;	/* Read first block. */
+
+		psf->read_short		= paf24_read_s ;
+		psf->read_int		= paf24_read_i ;
+		psf->read_float		= paf24_read_f ;
+		psf->read_double	= paf24_read_d ;
+		} ;
+
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	psf->write_short	= paf24_write_s ;
+		psf->write_int		= paf24_write_i ;
+		psf->write_float	= paf24_write_f ;
+		psf->write_double	= paf24_write_d ;
+		} ;
+
+	psf->seek	= paf24_seek ;
+	psf->container_close	= paf24_close ;
+
+	psf->filelength = psf_get_filelen (psf) ;
+	psf->datalength = psf->filelength - psf->dataoffset ;
+
+	if (psf->datalength % PAF24_BLOCK_SIZE)
+	{	if (psf->file.mode == SFM_READ)
+			psf_log_printf (psf, "*** Warning : file seems to be truncated.\n") ;
+		ppaf24->max_blocks = psf->datalength / ppaf24->blocksize + 1 ;
+		}
+	else
+		ppaf24->max_blocks = psf->datalength / ppaf24->blocksize ;
+
+	ppaf24->read_block = 0 ;
+	if (psf->file.mode == SFM_RDWR)
+		ppaf24->write_block = ppaf24->max_blocks ;
+	else
+		ppaf24->write_block = 0 ;
+
+	psf->sf.frames = PAF24_SAMPLES_PER_BLOCK * ppaf24->max_blocks ;
+	ppaf24->sample_count = psf->sf.frames ;
+
+	return 0 ;
+} /* paf24_init */
+
+static sf_count_t
+paf24_seek (SF_PRIVATE *psf, int mode, sf_count_t offset)
+{	PAF24_PRIVATE	*ppaf24 ;
+	int				newblock, newsample ;
+
+	if (psf->codec_data == NULL)
+	{	psf->error = SFE_INTERNAL ;
+		return PSF_SEEK_ERROR ;
+		} ;
+
+	ppaf24 = (PAF24_PRIVATE*) psf->codec_data ;
+
+	if (mode == SFM_READ && ppaf24->write_count > 0)
+		paf24_write_block (psf, ppaf24) ;
+
+	newblock	= offset / PAF24_SAMPLES_PER_BLOCK ;
+	newsample	= offset % PAF24_SAMPLES_PER_BLOCK ;
+
+	switch (mode)
+	{	case SFM_READ :
+				if (psf->last_op == SFM_WRITE && ppaf24->write_count)
+					paf24_write_block (psf, ppaf24) ;
+
+				psf_fseek (psf, psf->dataoffset + newblock * ppaf24->blocksize, SEEK_SET) ;
+				ppaf24->read_block = newblock ;
+				paf24_read_block (psf, ppaf24) ;
+				ppaf24->read_count = newsample ;
+				break ;
+
+		case SFM_WRITE :
+				if (offset > ppaf24->sample_count)
+				{	psf->error = SFE_BAD_SEEK ;
+					return PSF_SEEK_ERROR ;
+					} ;
+
+				if (psf->last_op == SFM_WRITE && ppaf24->write_count)
+					paf24_write_block (psf, ppaf24) ;
+
+				psf_fseek (psf, psf->dataoffset + newblock * ppaf24->blocksize, SEEK_SET) ;
+				ppaf24->write_block = newblock ;
+				paf24_read_block (psf, ppaf24) ;
+				ppaf24->write_count = newsample ;
+				break ;
+
+		default :
+				psf->error = SFE_BAD_SEEK ;
+				return PSF_SEEK_ERROR ;
+		} ;
+
+	return newblock * PAF24_SAMPLES_PER_BLOCK + newsample ;
+} /* paf24_seek */
+
+static int
+paf24_close (SF_PRIVATE *psf)
+{	PAF24_PRIVATE *ppaf24 ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+
+	ppaf24 = (PAF24_PRIVATE*) psf->codec_data ;
+
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	if (ppaf24->write_count > 0)
+			paf24_write_block (psf, ppaf24) ;
+		} ;
+
+	return 0 ;
+} /* paf24_close */
+
+/*---------------------------------------------------------------------------
+*/
+static int
+paf24_read_block (SF_PRIVATE *psf, PAF24_PRIVATE *ppaf24)
+{	int				k, channel ;
+	unsigned char	*cptr ;
+
+	ppaf24->read_block ++ ;
+	ppaf24->read_count = 0 ;
+
+	if (ppaf24->read_block * PAF24_SAMPLES_PER_BLOCK > ppaf24->sample_count)
+	{	memset (ppaf24->samples, 0, PAF24_SAMPLES_PER_BLOCK * ppaf24->channels) ;
+		return 1 ;
+		} ;
+
+	/* Read the block. */
+	if ((k = (int) psf_fread (ppaf24->block, 1, ppaf24->blocksize, psf)) != ppaf24->blocksize)
+		psf_log_printf (psf, "*** Warning : short read (%d != %d).\n", k, ppaf24->blocksize) ;
+
+	/* Do endian swapping if necessary. */
+	if ((CPU_IS_BIG_ENDIAN && psf->endian == SF_ENDIAN_LITTLE) || (CPU_IS_LITTLE_ENDIAN && psf->endian == SF_ENDIAN_BIG))
+		endswap_int_array (ppaf24->block, 8 * ppaf24->channels) ;
+
+	/* Unpack block. */
+	for (k = 0 ; k < PAF24_SAMPLES_PER_BLOCK * ppaf24->channels ; k++)
+	{	channel = k % ppaf24->channels ;
+		cptr = ((unsigned char *) ppaf24->block) + PAF24_BLOCK_SIZE * channel + 3 * (k / ppaf24->channels) ;
+		ppaf24->samples [k] = (cptr [0] << 8) | (cptr [1] << 16) | (((unsigned) cptr [2]) << 24) ;
+		} ;
+
+	return 1 ;
+} /* paf24_read_block */
+
+static int
+paf24_read (SF_PRIVATE *psf, PAF24_PRIVATE *ppaf24, int *ptr, int len)
+{	int	count, total = 0 ;
+
+	while (total < len)
+	{	if (ppaf24->read_block * PAF24_SAMPLES_PER_BLOCK >= ppaf24->sample_count)
+		{	memset (&(ptr [total]), 0, (len - total) * sizeof (int)) ;
+			return total ;
+			} ;
+
+		if (ppaf24->read_count >= PAF24_SAMPLES_PER_BLOCK)
+			paf24_read_block (psf, ppaf24) ;
+
+		count = (PAF24_SAMPLES_PER_BLOCK - ppaf24->read_count) * ppaf24->channels ;
+		count = (len - total > count) ? count : len - total ;
+
+		memcpy (&(ptr [total]), &(ppaf24->samples [ppaf24->read_count * ppaf24->channels]), count * sizeof (int)) ;
+		total += count ;
+		ppaf24->read_count += count / ppaf24->channels ;
+		} ;
+
+	return total ;
+} /* paf24_read */
+
+static sf_count_t
+paf24_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len)
+{	BUF_UNION		ubuf ;
+	PAF24_PRIVATE 	*ppaf24 ;
+	int				*iptr ;
+	int				k, bufferlen, readcount, count ;
+	sf_count_t		total = 0 ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	ppaf24 = (PAF24_PRIVATE*) psf->codec_data ;
+
+	iptr = ubuf.ibuf ;
+	bufferlen = ARRAY_LEN (ubuf.ibuf) ;
+	while (len > 0)
+	{	readcount = (len >= bufferlen) ? bufferlen : (int) len ;
+		count = paf24_read (psf, ppaf24, iptr, readcount) ;
+		for (k = 0 ; k < readcount ; k++)
+			ptr [total + k] = iptr [k] >> 16 ;
+		total += count ;
+		len -= readcount ;
+		} ;
+	return total ;
+} /* paf24_read_s */
+
+static sf_count_t
+paf24_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len)
+{	PAF24_PRIVATE *ppaf24 ;
+	int				total ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	ppaf24 = (PAF24_PRIVATE*) psf->codec_data ;
+
+	total = paf24_read (psf, ppaf24, ptr, len) ;
+
+	return total ;
+} /* paf24_read_i */
+
+static sf_count_t
+paf24_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len)
+{	BUF_UNION		ubuf ;
+	PAF24_PRIVATE 	*ppaf24 ;
+	int				*iptr ;
+	int				k, bufferlen, readcount, count ;
+	sf_count_t		total = 0 ;
+	float			normfact ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	ppaf24 = (PAF24_PRIVATE*) psf->codec_data ;
+
+	normfact = (psf->norm_float == SF_TRUE) ? (1.0 / 0x80000000) : (1.0 / 0x100) ;
+
+	iptr = ubuf.ibuf ;
+	bufferlen = ARRAY_LEN (ubuf.ibuf) ;
+	while (len > 0)
+	{	readcount = (len >= bufferlen) ? bufferlen : (int) len ;
+		count = paf24_read (psf, ppaf24, iptr, readcount) ;
+		for (k = 0 ; k < readcount ; k++)
+			ptr [total + k] = normfact * iptr [k] ;
+		total += count ;
+		len -= readcount ;
+		} ;
+	return total ;
+} /* paf24_read_f */
+
+static sf_count_t
+paf24_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len)
+{	BUF_UNION		ubuf ;
+	PAF24_PRIVATE 	*ppaf24 ;
+	int				*iptr ;
+	int				k, bufferlen, readcount, count ;
+	sf_count_t		total = 0 ;
+	double 			normfact ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	ppaf24 = (PAF24_PRIVATE*) psf->codec_data ;
+
+	normfact = (psf->norm_double == SF_TRUE) ? (1.0 / 0x80000000) : (1.0 / 0x100) ;
+
+	iptr = ubuf.ibuf ;
+	bufferlen = ARRAY_LEN (ubuf.ibuf) ;
+	while (len > 0)
+	{	readcount = (len >= bufferlen) ? bufferlen : (int) len ;
+		count = paf24_read (psf, ppaf24, iptr, readcount) ;
+		for (k = 0 ; k < readcount ; k++)
+			ptr [total + k] = normfact * iptr [k] ;
+		total += count ;
+		len -= readcount ;
+		} ;
+	return total ;
+} /* paf24_read_d */
+
+/*---------------------------------------------------------------------------
+*/
+
+static int
+paf24_write_block (SF_PRIVATE *psf, PAF24_PRIVATE *ppaf24)
+{	int				k, nextsample, channel ;
+	unsigned char	*cptr ;
+
+	/* First pack block. */
+
+	if (CPU_IS_LITTLE_ENDIAN)
+	{	for (k = 0 ; k < PAF24_SAMPLES_PER_BLOCK * ppaf24->channels ; k++)
+		{	channel = k % ppaf24->channels ;
+			cptr = ((unsigned char *) ppaf24->block) + PAF24_BLOCK_SIZE * channel + 3 * (k / ppaf24->channels) ;
+			nextsample = ppaf24->samples [k] >> 8 ;
+			cptr [0] = nextsample ;
+			cptr [1] = nextsample >> 8 ;
+			cptr [2] = nextsample >> 16 ;
+			} ;
+
+		/* Do endian swapping if necessary. */
+		if (psf->endian == SF_ENDIAN_BIG)
+			endswap_int_array (ppaf24->block, 8 * ppaf24->channels) ;
+		}
+	else if (CPU_IS_BIG_ENDIAN)
+	{	/* This is correct. */
+		for (k = 0 ; k < PAF24_SAMPLES_PER_BLOCK * ppaf24->channels ; k++)
+		{	channel = k % ppaf24->channels ;
+			cptr = ((unsigned char *) ppaf24->block) + PAF24_BLOCK_SIZE * channel + 3 * (k / ppaf24->channels) ;
+			nextsample = ppaf24->samples [k] >> 8 ;
+			cptr [0] = nextsample ;
+			cptr [1] = nextsample >> 8 ;
+			cptr [2] = nextsample >> 16 ;
+			} ;
+		if (psf->endian == SF_ENDIAN_LITTLE)
+			endswap_int_array (ppaf24->block, 8 * ppaf24->channels) ;
+		} ;
+
+	/* Write block to disk. */
+	if ((k = (int) psf_fwrite (ppaf24->block, 1, ppaf24->blocksize, psf)) != ppaf24->blocksize)
+		psf_log_printf (psf, "*** Warning : short write (%d != %d).\n", k, ppaf24->blocksize) ;
+
+	if (ppaf24->sample_count < ppaf24->write_block * PAF24_SAMPLES_PER_BLOCK + ppaf24->write_count)
+		ppaf24->sample_count = ppaf24->write_block * PAF24_SAMPLES_PER_BLOCK + ppaf24->write_count ;
+
+	if (ppaf24->write_count == PAF24_SAMPLES_PER_BLOCK)
+	{	ppaf24->write_block ++ ;
+		ppaf24->write_count = 0 ;
+		} ;
+
+	return 1 ;
+} /* paf24_write_block */
+
+static int
+paf24_write (SF_PRIVATE *psf, PAF24_PRIVATE *ppaf24, const int *ptr, int len)
+{	int		count, total = 0 ;
+
+	while (total < len)
+	{	count = (PAF24_SAMPLES_PER_BLOCK - ppaf24->write_count) * ppaf24->channels ;
+
+		if (count > len - total)
+			count = len - total ;
+
+		memcpy (&(ppaf24->samples [ppaf24->write_count * ppaf24->channels]), &(ptr [total]), count * sizeof (int)) ;
+		total += count ;
+		ppaf24->write_count += count / ppaf24->channels ;
+
+		if (ppaf24->write_count >= PAF24_SAMPLES_PER_BLOCK)
+			paf24_write_block (psf, ppaf24) ;
+		} ;
+
+	return total ;
+} /* paf24_write */
+
+static sf_count_t
+paf24_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len)
+{	BUF_UNION		ubuf ;
+	PAF24_PRIVATE 	*ppaf24 ;
+	int				*iptr ;
+	int				k, bufferlen, writecount = 0, count ;
+	sf_count_t		total = 0 ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	ppaf24 = (PAF24_PRIVATE*) psf->codec_data ;
+
+	iptr = ubuf.ibuf ;
+	bufferlen = ARRAY_LEN (ubuf.ibuf) ;
+	while (len > 0)
+	{	writecount = (len >= bufferlen) ? bufferlen : (int) len ;
+		for (k = 0 ; k < writecount ; k++)
+			iptr [k] = ptr [total + k] << 16 ;
+		count = paf24_write (psf, ppaf24, iptr, writecount) ;
+		total += count ;
+		len -= writecount ;
+		if (count != writecount)
+			break ;
+		} ;
+	return total ;
+} /* paf24_write_s */
+
+static sf_count_t
+paf24_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len)
+{	PAF24_PRIVATE 	*ppaf24 ;
+	int				writecount, count ;
+	sf_count_t		total = 0 ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	ppaf24 = (PAF24_PRIVATE*) psf->codec_data ;
+
+	while (len > 0)
+	{	writecount = (len > 0x10000000) ? 0x10000000 : (int) len ;
+
+		count = paf24_write (psf, ppaf24, ptr, writecount) ;
+
+		total += count ;
+		len -= count ;
+		if (count != writecount)
+			break ;
+		} ;
+
+	return total ;
+} /* paf24_write_i */
+
+static sf_count_t
+paf24_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len)
+{	BUF_UNION		ubuf ;
+	PAF24_PRIVATE 	*ppaf24 ;
+	int				*iptr ;
+	int				k, bufferlen, writecount = 0, count ;
+	sf_count_t		total = 0 ;
+	float			normfact ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	ppaf24 = (PAF24_PRIVATE*) psf->codec_data ;
+
+	normfact = (psf->norm_float == SF_TRUE) ? (1.0 * 0x7FFFFFFF) : (1.0 / 0x100) ;
+
+	iptr = ubuf.ibuf ;
+	bufferlen = ARRAY_LEN (ubuf.ibuf) ;
+	while (len > 0)
+	{	writecount = (len >= bufferlen) ? bufferlen : (int) len ;
+		for (k = 0 ; k < writecount ; k++)
+			iptr [k] = psf_lrintf (normfact * ptr [total + k]) ;
+		count = paf24_write (psf, ppaf24, iptr, writecount) ;
+		total += count ;
+		len -= writecount ;
+		if (count != writecount)
+			break ;
+		} ;
+
+	return total ;
+} /* paf24_write_f */
+
+static sf_count_t
+paf24_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len)
+{	BUF_UNION		ubuf ;
+	PAF24_PRIVATE 	*ppaf24 ;
+	int				*iptr ;
+	int				k, bufferlen, writecount = 0, count ;
+	sf_count_t		total = 0 ;
+	double			normfact ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	ppaf24 = (PAF24_PRIVATE*) psf->codec_data ;
+
+	normfact = (psf->norm_double == SF_TRUE) ? (1.0 * 0x7FFFFFFF) : (1.0 / 0x100) ;
+
+	iptr = ubuf.ibuf ;
+	bufferlen = ARRAY_LEN (ubuf.ibuf) ;
+	while (len > 0)
+	{	writecount = (len >= bufferlen) ? bufferlen : (int) len ;
+		for (k = 0 ; k < writecount ; k++)
+			iptr [k] = psf_lrint (normfact * ptr [total+k]) ;
+		count = paf24_write (psf, ppaf24, iptr, writecount) ;
+		total += count ;
+		len -= writecount ;
+		if (count != writecount)
+			break ;
+		} ;
+
+	return total ;
+} /* paf24_write_d */
+
diff --git a/src/pcm.c b/src/pcm.c
new file mode 100644
index 0000000..d7dc82c
--- /dev/null
+++ b/src/pcm.c
@@ -0,0 +1,2918 @@
+/*
+** Copyright (C) 1999-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include <math.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+/* Need to be able to handle 3 byte (24 bit) integers. So defined a
+** type and use SIZEOF_TRIBYTE instead of (tribyte).
+*/
+
+typedef	struct tribyte
+{	uint8_t bytes [3] ;
+	} tribyte ;
+
+#define	SIZEOF_TRIBYTE	3
+
+static sf_count_t	pcm_read_sc2s	(SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
+static sf_count_t	pcm_read_uc2s	(SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
+static sf_count_t	pcm_read_bes2s	(SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
+static sf_count_t	pcm_read_les2s	(SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
+static sf_count_t	pcm_read_bet2s	(SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
+static sf_count_t	pcm_read_let2s	(SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
+static sf_count_t	pcm_read_bei2s	(SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
+static sf_count_t	pcm_read_lei2s	(SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
+
+static sf_count_t	pcm_read_sc2i	(SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
+static sf_count_t	pcm_read_uc2i	(SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
+static sf_count_t	pcm_read_bes2i	(SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
+static sf_count_t	pcm_read_les2i	(SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
+static sf_count_t	pcm_read_bet2i	(SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
+static sf_count_t	pcm_read_let2i	(SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
+static sf_count_t	pcm_read_bei2i	(SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
+static sf_count_t	pcm_read_lei2i	(SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
+
+static sf_count_t	pcm_read_sc2f	(SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
+static sf_count_t	pcm_read_uc2f	(SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
+static sf_count_t	pcm_read_bes2f	(SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
+static sf_count_t	pcm_read_les2f	(SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
+static sf_count_t	pcm_read_bet2f	(SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
+static sf_count_t	pcm_read_let2f	(SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
+static sf_count_t	pcm_read_bei2f	(SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
+static sf_count_t	pcm_read_lei2f	(SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
+
+static sf_count_t	pcm_read_sc2d	(SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
+static sf_count_t	pcm_read_uc2d	(SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
+static sf_count_t	pcm_read_bes2d	(SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
+static sf_count_t	pcm_read_les2d	(SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
+static sf_count_t	pcm_read_bet2d	(SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
+static sf_count_t	pcm_read_let2d	(SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
+static sf_count_t	pcm_read_bei2d	(SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
+static sf_count_t	pcm_read_lei2d	(SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
+
+static sf_count_t	pcm_write_s2sc	(SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
+static sf_count_t	pcm_write_s2uc	(SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
+static sf_count_t	pcm_write_s2bes (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
+static sf_count_t	pcm_write_s2les (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
+static sf_count_t	pcm_write_s2bet (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
+static sf_count_t	pcm_write_s2let (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
+static sf_count_t	pcm_write_s2bei (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
+static sf_count_t	pcm_write_s2lei (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
+
+static sf_count_t	pcm_write_i2sc	(SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
+static sf_count_t	pcm_write_i2uc	(SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
+static sf_count_t	pcm_write_i2bes (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
+static sf_count_t	pcm_write_i2les (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
+static sf_count_t	pcm_write_i2bet (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
+static sf_count_t	pcm_write_i2let (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
+static sf_count_t	pcm_write_i2bei (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
+static sf_count_t	pcm_write_i2lei (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
+
+static sf_count_t	pcm_write_f2sc	(SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
+static sf_count_t	pcm_write_f2uc	(SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
+static sf_count_t	pcm_write_f2bes (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
+static sf_count_t	pcm_write_f2les (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
+static sf_count_t	pcm_write_f2bet (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
+static sf_count_t	pcm_write_f2let (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
+static sf_count_t	pcm_write_f2bei (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
+static sf_count_t	pcm_write_f2lei (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
+
+static sf_count_t	pcm_write_d2sc	(SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
+static sf_count_t	pcm_write_d2uc	(SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
+static sf_count_t	pcm_write_d2bes (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
+static sf_count_t	pcm_write_d2les (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
+static sf_count_t	pcm_write_d2bet (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
+static sf_count_t	pcm_write_d2let (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
+static sf_count_t	pcm_write_d2bei (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
+static sf_count_t	pcm_write_d2lei (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
+
+/*-----------------------------------------------------------------------------------------------
+*/
+
+enum
+{	/* Char type for 8 bit files. */
+	SF_CHARS_SIGNED		= 200,
+	SF_CHARS_UNSIGNED	= 201
+} ;
+
+/*-----------------------------------------------------------------------------------------------
+*/
+
+int
+pcm_init (SF_PRIVATE *psf)
+{	int chars = 0 ;
+
+	if (psf->bytewidth == 0 || psf->sf.channels == 0)
+	{	psf_log_printf (psf, "pcm_init : internal error : bytewitdh = %d, channels = %d\n", psf->bytewidth, psf->sf.channels) ;
+		return SFE_INTERNAL ;
+		} ;
+
+	psf->blockwidth = psf->bytewidth * psf->sf.channels ;
+
+	if ((SF_CODEC (psf->sf.format)) == SF_FORMAT_PCM_S8)
+		chars = SF_CHARS_SIGNED ;
+	else if ((SF_CODEC (psf->sf.format)) == SF_FORMAT_PCM_U8)
+		chars = SF_CHARS_UNSIGNED ;
+
+#if CPU_IS_BIG_ENDIAN
+	psf->data_endswap = (psf->endian == SF_ENDIAN_BIG) ? SF_FALSE : SF_TRUE ;
+#else
+	psf->data_endswap = (psf->endian == SF_ENDIAN_LITTLE) ? SF_FALSE : SF_TRUE ;
+#endif
+
+	if (psf->file.mode == SFM_READ || psf->file.mode == SFM_RDWR)
+	{	switch (psf->bytewidth * 0x10000 + psf->endian + chars)
+		{	case (0x10000 + SF_ENDIAN_BIG + SF_CHARS_SIGNED) :
+			case (0x10000 + SF_ENDIAN_LITTLE + SF_CHARS_SIGNED) :
+					psf->read_short		= pcm_read_sc2s ;
+					psf->read_int		= pcm_read_sc2i ;
+					psf->read_float		= pcm_read_sc2f ;
+					psf->read_double	= pcm_read_sc2d ;
+					break ;
+			case (0x10000 + SF_ENDIAN_BIG + SF_CHARS_UNSIGNED) :
+			case (0x10000 + SF_ENDIAN_LITTLE + SF_CHARS_UNSIGNED) :
+					psf->read_short		= pcm_read_uc2s ;
+					psf->read_int		= pcm_read_uc2i ;
+					psf->read_float		= pcm_read_uc2f ;
+					psf->read_double	= pcm_read_uc2d ;
+					break ;
+
+			case (2 * 0x10000 + SF_ENDIAN_BIG) :
+					psf->read_short		= pcm_read_bes2s ;
+					psf->read_int		= pcm_read_bes2i ;
+					psf->read_float		= pcm_read_bes2f ;
+					psf->read_double	= pcm_read_bes2d ;
+					break ;
+			case (3 * 0x10000 + SF_ENDIAN_BIG) :
+					psf->read_short		= pcm_read_bet2s ;
+					psf->read_int		= pcm_read_bet2i ;
+					psf->read_float		= pcm_read_bet2f ;
+					psf->read_double	= pcm_read_bet2d ;
+					break ;
+			case (4 * 0x10000 + SF_ENDIAN_BIG) :
+
+					psf->read_short		= pcm_read_bei2s ;
+					psf->read_int		= pcm_read_bei2i ;
+					psf->read_float		= pcm_read_bei2f ;
+					psf->read_double	= pcm_read_bei2d ;
+					break ;
+
+			case (2 * 0x10000 + SF_ENDIAN_LITTLE) :
+					psf->read_short		= pcm_read_les2s ;
+					psf->read_int		= pcm_read_les2i ;
+					psf->read_float		= pcm_read_les2f ;
+					psf->read_double	= pcm_read_les2d ;
+					break ;
+			case (3 * 0x10000 + SF_ENDIAN_LITTLE) :
+					psf->read_short		= pcm_read_let2s ;
+					psf->read_int		= pcm_read_let2i ;
+					psf->read_float		= pcm_read_let2f ;
+					psf->read_double	= pcm_read_let2d ;
+					break ;
+			case (4 * 0x10000 + SF_ENDIAN_LITTLE) :
+					psf->read_short		= pcm_read_lei2s ;
+					psf->read_int		= pcm_read_lei2i ;
+					psf->read_float		= pcm_read_lei2f ;
+					psf->read_double	= pcm_read_lei2d ;
+					break ;
+			default :
+				psf_log_printf (psf, "pcm.c returning SFE_UNIMPLEMENTED\nbytewidth %d    endian %d\n", psf->bytewidth, psf->endian) ;
+				return SFE_UNIMPLEMENTED ;
+			} ;
+		} ;
+
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	switch (psf->bytewidth * 0x10000 + psf->endian + chars)
+		{	case (0x10000 + SF_ENDIAN_BIG + SF_CHARS_SIGNED) :
+			case (0x10000 + SF_ENDIAN_LITTLE + SF_CHARS_SIGNED) :
+					psf->write_short	= pcm_write_s2sc ;
+					psf->write_int		= pcm_write_i2sc ;
+					psf->write_float	= pcm_write_f2sc ;
+					psf->write_double	= pcm_write_d2sc ;
+					break ;
+			case (0x10000 + SF_ENDIAN_BIG + SF_CHARS_UNSIGNED) :
+			case (0x10000 + SF_ENDIAN_LITTLE + SF_CHARS_UNSIGNED) :
+					psf->write_short	= pcm_write_s2uc ;
+					psf->write_int		= pcm_write_i2uc ;
+					psf->write_float	= pcm_write_f2uc ;
+					psf->write_double	= pcm_write_d2uc ;
+					break ;
+
+			case (2 * 0x10000 + SF_ENDIAN_BIG) :
+					psf->write_short	= pcm_write_s2bes ;
+					psf->write_int		= pcm_write_i2bes ;
+					psf->write_float	= pcm_write_f2bes ;
+					psf->write_double	= pcm_write_d2bes ;
+					break ;
+
+			case (3 * 0x10000 + SF_ENDIAN_BIG) :
+					psf->write_short	= pcm_write_s2bet ;
+					psf->write_int		= pcm_write_i2bet ;
+					psf->write_float	= pcm_write_f2bet ;
+					psf->write_double	= pcm_write_d2bet ;
+					break ;
+
+			case (4 * 0x10000 + SF_ENDIAN_BIG) :
+					psf->write_short	= pcm_write_s2bei ;
+					psf->write_int		= pcm_write_i2bei ;
+					psf->write_float	= pcm_write_f2bei ;
+					psf->write_double	= pcm_write_d2bei ;
+					break ;
+
+			case (2 * 0x10000 + SF_ENDIAN_LITTLE) :
+					psf->write_short	= pcm_write_s2les ;
+					psf->write_int		= pcm_write_i2les ;
+					psf->write_float	= pcm_write_f2les ;
+					psf->write_double	= pcm_write_d2les ;
+					break ;
+
+			case (3 * 0x10000 + SF_ENDIAN_LITTLE) :
+					psf->write_short	= pcm_write_s2let ;
+					psf->write_int		= pcm_write_i2let ;
+					psf->write_float	= pcm_write_f2let ;
+					psf->write_double	= pcm_write_d2let ;
+					break ;
+
+			case (4 * 0x10000 + SF_ENDIAN_LITTLE) :
+					psf->write_short	= pcm_write_s2lei ;
+					psf->write_int		= pcm_write_i2lei ;
+					psf->write_float	= pcm_write_f2lei ;
+					psf->write_double	= pcm_write_d2lei ;
+					break ;
+
+			default :
+				psf_log_printf (psf, "pcm.c returning SFE_UNIMPLEMENTED\nbytewidth %d    endian %d\n", psf->bytewidth, psf->endian) ;
+				return SFE_UNIMPLEMENTED ;
+			} ;
+
+		} ;
+
+	if (psf->filelength > psf->dataoffset)
+	{	psf->datalength = (psf->dataend > 0) ? psf->dataend - psf->dataoffset :
+							psf->filelength - psf->dataoffset ;
+		}
+	else
+		psf->datalength = 0 ;
+
+	psf->sf.frames = psf->blockwidth > 0 ? psf->datalength / psf->blockwidth : 0 ;
+
+	return 0 ;
+} /* pcm_init */
+
+/*==============================================================================
+*/
+
+static inline void
+sc2s_array	(const signed char *src, int count, short *dest)
+{	for (int i = 0 ; i < count ; i++)
+	{	dest [i] = ((uint16_t) src [i]) << 8 ;
+		} ;
+} /* sc2s_array */
+
+static inline void
+uc2s_array	(const unsigned char *src, int count, short *dest)
+{	for (int i = 0 ; i < count ; i++)
+	{	dest [i] = (((uint32_t) src [i]) - 0x80) << 8 ;
+		} ;
+} /* uc2s_array */
+
+static inline void
+let2s_array (const tribyte *src, int count, short *dest)
+{	for (int i = 0 ; i < count ; i++)
+		dest [i] = LET2H_16_PTR (src [i].bytes) ;
+} /* let2s_array */
+
+static inline void
+bet2s_array (const tribyte *src, int count, short *dest)
+{	for (int i = 0 ; i < count ; i++)
+		dest [i] = BET2H_16_PTR (src [i].bytes) ;
+} /* bet2s_array */
+
+static inline void
+lei2s_array (const int *src, int count, short *dest)
+{	int value ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	value = LE2H_32 (src [i]) ;
+		dest [i] = value >> 16 ;
+		} ;
+} /* lei2s_array */
+
+static inline void
+bei2s_array (const int *src, int count, short *dest)
+{	int value ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	value = BE2H_32 (src [i]) ;
+		dest [i] = value >> 16 ;
+		} ;
+} /* bei2s_array */
+
+/*--------------------------------------------------------------------------
+*/
+
+static inline void
+sc2i_array	(const signed char *src, int count, int *dest)
+{	for (int i = 0 ; i < count ; i++)
+	{	dest [i] = arith_shift_left ((int) src [i], 24) ;
+		} ;
+} /* sc2i_array */
+
+static inline void
+uc2i_array	(const unsigned char *src, int count, int *dest)
+{	for (int i = 0 ; i < count ; i++)
+	{	dest [i] = arith_shift_left (((int) src [i]) - 128, 24) ;
+		} ;
+} /* uc2i_array */
+
+static inline void
+bes2i_array (const short *src, int count, int *dest)
+{	short value ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	value = BE2H_16 (src [i]) ;
+		dest [i] = arith_shift_left (value, 16) ;
+		} ;
+} /* bes2i_array */
+
+static inline void
+les2i_array (const short *src, int count, int *dest)
+{	short value ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	value = LE2H_16 (src [i]) ;
+		dest [i] = arith_shift_left (value, 16) ;
+		} ;
+} /* les2i_array */
+
+static inline void
+bet2i_array (const tribyte *src, int count, int *dest)
+{	for (int i = 0 ; i < count ; i++)
+		dest [i] = psf_get_be24 (src [i].bytes, 0) ;
+} /* bet2i_array */
+
+static inline void
+let2i_array (const tribyte *src, int count, int *dest)
+{	for (int i = 0 ; i < count ; i++)
+		dest [i] = psf_get_le24 (src [i].bytes, 0) ;
+} /* let2i_array */
+
+/*--------------------------------------------------------------------------
+*/
+
+static inline void
+sc2f_array	(const signed char *src, int count, float *dest, float normfact)
+{	for (int i = 0 ; i < count ; i++)
+		dest [i] = ((float) src [i]) * normfact ;
+} /* sc2f_array */
+
+static inline void
+uc2f_array	(const unsigned char *src, int count, float *dest, float normfact)
+{	for (int i = 0 ; i < count ; i++)
+		dest [i] = (((int) src [i]) - 128) * normfact ;
+} /* uc2f_array */
+
+static inline void
+les2f_array (const short *src, int count, float *dest, float normfact)
+{	short	value ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	value = src [i] ;
+		value = LE2H_16 (value) ;
+		dest [i] = ((float) value) * normfact ;
+		} ;
+} /* les2f_array */
+
+static inline void
+bes2f_array (const short *src, int count, float *dest, float normfact)
+{	short			value ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	value = src [i] ;
+		value = BE2H_16 (value) ;
+		dest [i] = ((float) value) * normfact ;
+		} ;
+} /* bes2f_array */
+
+static inline void
+let2f_array (const tribyte *src, int count, float *dest, float normfact)
+{	int value ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	value = psf_get_le24 (src [i].bytes, 0) ;
+		dest [i] = ((float) value) * normfact ;
+		} ;
+} /* let2f_array */
+
+static inline void
+bet2f_array (const tribyte *src, int count, float *dest, float normfact)
+{	int value ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	value = psf_get_be24 (src [i].bytes, 0) ;
+		dest [i] = ((float) value) * normfact ;
+		} ;
+} /* bet2f_array */
+
+static inline void
+lei2f_array (const int *src, int count, float *dest, float normfact)
+{	int 			value ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	value = src [i] ;
+		value = LE2H_32 (value) ;
+		dest [i] = ((float) value) * normfact ;
+		} ;
+} /* lei2f_array */
+
+static inline void
+bei2f_array (const int *src, int count, float *dest, float normfact)
+{	int 			value ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	value = src [i] ;
+		value = BE2H_32 (value) ;
+		dest [i] = ((float) value) * normfact ;
+		} ;
+} /* bei2f_array */
+
+/*--------------------------------------------------------------------------
+*/
+
+static inline void
+sc2d_array	(const signed char *src, int count, double *dest, double normfact)
+{	for (int i = 0 ; i < count ; i++)
+		dest [i] = ((double) src [i]) * normfact ;
+} /* sc2d_array */
+
+static inline void
+uc2d_array	(const unsigned char *src, int count, double *dest, double normfact)
+{	for (int i = 0 ; i < count ; i++)
+		dest [i] = (((int) src [i]) - 128) * normfact ;
+} /* uc2d_array */
+
+static inline void
+les2d_array (const short *src, int count, double *dest, double normfact)
+{	short	value ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	value = src [i] ;
+		value = LE2H_16 (value) ;
+		dest [i] = ((double) value) * normfact ;
+		} ;
+} /* les2d_array */
+
+static inline void
+bes2d_array (const short *src, int count, double *dest, double normfact)
+{	short	value ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	value = src [i] ;
+		value = BE2H_16 (value) ;
+		dest [i] = ((double) value) * normfact ;
+		} ;
+} /* bes2d_array */
+
+static inline void
+let2d_array (const tribyte *src, int count, double *dest, double normfact)
+{	int value ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	value = psf_get_le24 (src [i].bytes, 0) ;
+		dest [i] = ((double) value) * normfact ;
+		} ;
+} /* let2d_array */
+
+static inline void
+bet2d_array (const tribyte *src, int count, double *dest, double normfact)
+{	int value ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	value = psf_get_be24 (src [i].bytes, 0) ;
+		dest [i] = ((double) value) * normfact ;
+		} ;
+} /* bet2d_array */
+
+static inline void
+lei2d_array (const int *src, int count, double *dest, double normfact)
+{	int 	value ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	value = src [i] ;
+		value = LE2H_32 (value) ;
+		dest [i] = ((double) value) * normfact ;
+		} ;
+} /* lei2d_array */
+
+static inline void
+bei2d_array (const int *src, int count, double *dest, double normfact)
+{	int 	value ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	value = src [i] ;
+		value = BE2H_32 (value) ;
+		dest [i] = ((double) value) * normfact ;
+		} ;
+} /* bei2d_array */
+
+/*--------------------------------------------------------------------------
+*/
+
+static inline void
+s2sc_array	(const short *src, signed char *dest, int count)
+{	for (int i = 0 ; i < count ; i++)
+		dest [i] = src [i] >> 8 ;
+} /* s2sc_array */
+
+static inline void
+s2uc_array	(const short *src, unsigned char *dest, int count)
+{	for (int i = 0 ; i < count ; i++)
+		dest [i] = (src [i] >> 8) + 0x80 ;
+} /* s2uc_array */
+
+static inline void
+s2let_array (const short *src, tribyte *dest, int count)
+{	for (int i = 0 ; i < count ; i++)
+	{	dest [i].bytes [0] = 0 ;
+		dest [i].bytes [1] = src [i] ;
+		dest [i].bytes [2] = src [i] >> 8 ;
+		} ;
+} /* s2let_array */
+
+static inline void
+s2bet_array (const short *src, tribyte *dest, int count)
+{	for (int i = 0 ; i < count ; i++)
+	{	dest [i].bytes [2] = 0 ;
+		dest [i].bytes [1] = src [i] ;
+		dest [i].bytes [0] = src [i] >> 8 ;
+		} ;
+} /* s2bet_array */
+
+static inline void
+s2lei_array (const short *src, int *dest, int count)
+{	unsigned char	*ucptr ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	ucptr = (unsigned char*) &dest [i] ;
+		ucptr [0] = 0 ;
+		ucptr [1] = 0 ;
+		ucptr [2] = src [i] ;
+		ucptr [3] = src [i] >> 8 ;
+		} ;
+} /* s2lei_array */
+
+static inline void
+s2bei_array (const short *src, int *dest, int count)
+{	unsigned char	*ucptr ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	ucptr = (unsigned char*) &dest [i] ;
+		ucptr [0] = src [i] >> 8 ;
+		ucptr [1] = src [i] ;
+		ucptr [2] = 0 ;
+		ucptr [3] = 0 ;
+		} ;
+} /* s2bei_array */
+
+/*--------------------------------------------------------------------------
+*/
+
+static inline void
+i2sc_array	(const int *src, signed char *dest, int count)
+{	for (int i = 0 ; i < count ; i++)
+		dest [i] = (src [i] >> 24) ;
+} /* i2sc_array */
+
+static inline void
+i2uc_array	(const int *src, unsigned char *dest, int count)
+{	for (int i = 0 ; i < count ; i++)
+		dest [i] = ((src [i] >> 24) + 128) ;
+} /* i2uc_array */
+
+static inline void
+i2bes_array (const int *src, short *dest, int count)
+{	unsigned char	*ucptr ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	ucptr = (unsigned char*) &dest [i] ;
+		ucptr [0] = src [i] >> 24 ;
+		ucptr [1] = src [i] >> 16 ;
+		} ;
+} /* i2bes_array */
+
+static inline void
+i2les_array (const int *src, short *dest, int count)
+{	unsigned char	*ucptr ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	ucptr = (unsigned char*) &dest [i] ;
+		ucptr [0] = src [i] >> 16 ;
+		ucptr [1] = src [i] >> 24 ;
+		} ;
+} /* i2les_array */
+
+static inline void
+i2let_array (const int *src, tribyte *dest, int count)
+{	int value ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	value = src [i] >> 8 ;
+		dest [i].bytes [0] = value ;
+		dest [i].bytes [1] = value >> 8 ;
+		dest [i].bytes [2] = value >> 16 ;
+		} ;
+} /* i2let_array */
+
+static inline void
+i2bet_array (const int *src, tribyte *dest, int count)
+{	int value ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	value = src [i] >> 8 ;
+		dest [i].bytes [2] = value ;
+		dest [i].bytes [1] = value >> 8 ;
+		dest [i].bytes [0] = value >> 16 ;
+		} ;
+} /* i2bet_array */
+
+/*===============================================================================================
+*/
+
+static sf_count_t
+pcm_read_sc2s (SF_PRIVATE *psf, short *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.scbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.scbuf, sizeof (signed char), bufferlen, psf) ;
+		sc2s_array (ubuf.scbuf, readcount, ptr + total) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* pcm_read_sc2s */
+
+static sf_count_t
+pcm_read_uc2s (SF_PRIVATE *psf, short *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ucbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.ucbuf, sizeof (unsigned char), bufferlen, psf) ;
+		uc2s_array (ubuf.ucbuf, readcount, ptr + total) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* pcm_read_uc2s */
+
+static sf_count_t
+pcm_read_bes2s (SF_PRIVATE *psf, short *ptr, sf_count_t len)
+{	int		total ;
+
+	total = (int) psf_fread (ptr, sizeof (short), len, psf) ;
+#if CPU_IS_LITTLE_ENDIAN
+	endswap_short_array (ptr, len) ;
+#endif
+
+	return total ;
+} /* pcm_read_bes2s */
+
+static sf_count_t
+pcm_read_les2s (SF_PRIVATE *psf, short *ptr, sf_count_t len)
+{	int		total ;
+
+	total = psf_fread (ptr, sizeof (short), len, psf) ;
+#if CPU_IS_BIG_ENDIAN
+	endswap_short_array (ptr, len) ;
+#endif
+
+	return total ;
+} /* pcm_read_les2s */
+
+static sf_count_t
+pcm_read_bet2s (SF_PRIVATE *psf, short *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = sizeof (ubuf.ucbuf) / SIZEOF_TRIBYTE ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.ucbuf, SIZEOF_TRIBYTE, bufferlen, psf) ;
+		bet2s_array ((tribyte*) (ubuf.ucbuf), readcount, ptr + total) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* pcm_read_bet2s */
+
+static sf_count_t
+pcm_read_let2s (SF_PRIVATE *psf, short *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = sizeof (ubuf.ucbuf) / SIZEOF_TRIBYTE ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.ucbuf, SIZEOF_TRIBYTE, bufferlen, psf) ;
+		let2s_array ((tribyte*) (ubuf.ucbuf), readcount, ptr + total) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* pcm_read_let2s */
+
+static sf_count_t
+pcm_read_bei2s (SF_PRIVATE *psf, short *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ibuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.ibuf, sizeof (int), bufferlen, psf) ;
+		bei2s_array (ubuf.ibuf, readcount, ptr + total) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* pcm_read_bei2s */
+
+static sf_count_t
+pcm_read_lei2s (SF_PRIVATE *psf, short *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ibuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.ibuf, sizeof (int), bufferlen, psf) ;
+		lei2s_array (ubuf.ibuf, readcount, ptr + total) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* pcm_read_lei2s */
+
+/*-----------------------------------------------------------------------------------------------
+*/
+
+static sf_count_t
+pcm_read_sc2i (SF_PRIVATE *psf, int *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.scbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.scbuf, sizeof (signed char), bufferlen, psf) ;
+		sc2i_array (ubuf.scbuf, readcount, ptr + total) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* pcm_read_sc2i */
+
+static sf_count_t
+pcm_read_uc2i (SF_PRIVATE *psf, int *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ucbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.ucbuf, sizeof (unsigned char), bufferlen, psf) ;
+		uc2i_array (ubuf.ucbuf, readcount, ptr + total) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* pcm_read_uc2i */
+
+static sf_count_t
+pcm_read_bes2i (SF_PRIVATE *psf, int *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.sbuf, sizeof (short), bufferlen, psf) ;
+		bes2i_array (ubuf.sbuf, readcount, ptr + total) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* pcm_read_bes2i */
+
+static sf_count_t
+pcm_read_les2i (SF_PRIVATE *psf, int *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.sbuf, sizeof (short), bufferlen, psf) ;
+		les2i_array (ubuf.sbuf, readcount, ptr + total) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* pcm_read_les2i */
+
+static sf_count_t
+pcm_read_bet2i (SF_PRIVATE *psf, int *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = sizeof (ubuf.ucbuf) / SIZEOF_TRIBYTE ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.ucbuf, SIZEOF_TRIBYTE, bufferlen, psf) ;
+		bet2i_array ((tribyte*) (ubuf.ucbuf), readcount, ptr + total) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* pcm_read_bet2i */
+
+static sf_count_t
+pcm_read_let2i (SF_PRIVATE *psf, int *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = sizeof (ubuf.ucbuf) / SIZEOF_TRIBYTE ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.ucbuf, SIZEOF_TRIBYTE, bufferlen, psf) ;
+		let2i_array ((tribyte*) (ubuf.ucbuf), readcount, ptr + total) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* pcm_read_let2i */
+
+static sf_count_t
+pcm_read_bei2i (SF_PRIVATE *psf, int *ptr, sf_count_t len)
+{	int		total ;
+
+	total = psf_fread (ptr, sizeof (int), len, psf) ;
+#if CPU_IS_LITTLE_ENDIAN
+	endswap_int_array	(ptr, len) ;
+#endif
+
+	return total ;
+} /* pcm_read_bei2i */
+
+static sf_count_t
+pcm_read_lei2i (SF_PRIVATE *psf, int *ptr, sf_count_t len)
+{	int		total ;
+
+	total = psf_fread (ptr, sizeof (int), len, psf) ;
+#if CPU_IS_BIG_ENDIAN
+	endswap_int_array	(ptr, len) ;
+#endif
+
+	return total ;
+} /* pcm_read_lei2i */
+
+/*-----------------------------------------------------------------------------------------------
+*/
+
+static sf_count_t
+pcm_read_sc2f (SF_PRIVATE *psf, float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+	float	normfact ;
+
+	normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x80) : 1.0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.scbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.scbuf, sizeof (signed char), bufferlen, psf) ;
+		sc2f_array (ubuf.scbuf, readcount, ptr + total, normfact) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* pcm_read_sc2f */
+
+static sf_count_t
+pcm_read_uc2f (SF_PRIVATE *psf, float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+	float	normfact ;
+
+	normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x80) : 1.0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ucbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.ucbuf, sizeof (unsigned char), bufferlen, psf) ;
+		uc2f_array (ubuf.ucbuf, readcount, ptr + total, normfact) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* pcm_read_uc2f */
+
+static sf_count_t
+pcm_read_bes2f (SF_PRIVATE *psf, float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+	float	normfact ;
+
+	normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x8000) : 1.0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.sbuf, sizeof (short), bufferlen, psf) ;
+		bes2f_array (ubuf.sbuf, readcount, ptr + total, normfact) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* pcm_read_bes2f */
+
+static sf_count_t
+pcm_read_les2f (SF_PRIVATE *psf, float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+	float	normfact ;
+
+	normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x8000) : 1.0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.sbuf, sizeof (short), bufferlen, psf) ;
+		les2f_array (ubuf.sbuf, readcount, ptr + total, normfact) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* pcm_read_les2f */
+
+static sf_count_t
+pcm_read_bet2f (SF_PRIVATE *psf, float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+	float	normfact ;
+
+	/* Special normfactor because tribyte value is read into an int. */
+	normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x80000000) : 1.0 / 256.0 ;
+
+	bufferlen = sizeof (ubuf.ucbuf) / SIZEOF_TRIBYTE ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.ucbuf, SIZEOF_TRIBYTE, bufferlen, psf) ;
+		bet2f_array ((tribyte*) (ubuf.ucbuf), readcount, ptr + total, normfact) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* pcm_read_bet2f */
+
+static sf_count_t
+pcm_read_let2f (SF_PRIVATE *psf, float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+	float	normfact ;
+
+	/* Special normfactor because tribyte value is read into an int. */
+	normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x80000000) : 1.0 / 256.0 ;
+
+	bufferlen = sizeof (ubuf.ucbuf) / SIZEOF_TRIBYTE ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.ucbuf, SIZEOF_TRIBYTE, bufferlen, psf) ;
+		let2f_array ((tribyte*) (ubuf.ucbuf), readcount, ptr + total, normfact) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* pcm_read_let2f */
+
+static sf_count_t
+pcm_read_bei2f (SF_PRIVATE *psf, float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+	float	normfact ;
+
+	normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x80000000) : 1.0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ibuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.ibuf, sizeof (int), bufferlen, psf) ;
+		bei2f_array (ubuf.ibuf, readcount, ptr + total, normfact) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* pcm_read_bei2f */
+
+static sf_count_t
+pcm_read_lei2f (SF_PRIVATE *psf, float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+	float	normfact ;
+
+	normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x80000000) : 1.0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ibuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.ibuf, sizeof (int), bufferlen, psf) ;
+		lei2f_array (ubuf.ibuf, readcount, ptr + total, normfact) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* pcm_read_lei2f */
+
+/*-----------------------------------------------------------------------------------------------
+*/
+
+static sf_count_t
+pcm_read_sc2d (SF_PRIVATE *psf, double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+	double		normfact ;
+
+	normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((double) 0x80) : 1.0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.scbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.scbuf, sizeof (signed char), bufferlen, psf) ;
+		sc2d_array (ubuf.scbuf, readcount, ptr + total, normfact) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* pcm_read_sc2d */
+
+static sf_count_t
+pcm_read_uc2d (SF_PRIVATE *psf, double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+	double		normfact ;
+
+	normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((double) 0x80) : 1.0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ucbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.ucbuf, sizeof (unsigned char), bufferlen, psf) ;
+		uc2d_array (ubuf.ucbuf, readcount, ptr + total, normfact) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* pcm_read_uc2d */
+
+static sf_count_t
+pcm_read_bes2d (SF_PRIVATE *psf, double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+	double		normfact ;
+
+	normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((double) 0x8000) : 1.0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.sbuf, sizeof (short), bufferlen, psf) ;
+		bes2d_array (ubuf.sbuf, readcount, ptr + total, normfact) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* pcm_read_bes2d */
+
+static sf_count_t
+pcm_read_les2d (SF_PRIVATE *psf, double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+	double		normfact ;
+
+	normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((double) 0x8000) : 1.0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.sbuf, sizeof (short), bufferlen, psf) ;
+		les2d_array (ubuf.sbuf, readcount, ptr + total, normfact) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* pcm_read_les2d */
+
+static sf_count_t
+pcm_read_bet2d (SF_PRIVATE *psf, double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+	double		normfact ;
+
+	normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((double) 0x80000000) : 1.0 / 256.0 ;
+
+	bufferlen = sizeof (ubuf.ucbuf) / SIZEOF_TRIBYTE ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.ucbuf, SIZEOF_TRIBYTE, bufferlen, psf) ;
+		bet2d_array ((tribyte*) (ubuf.ucbuf), readcount, ptr + total, normfact) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* pcm_read_bet2d */
+
+static sf_count_t
+pcm_read_let2d (SF_PRIVATE *psf, double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+	double		normfact ;
+
+	/* Special normfactor because tribyte value is read into an int. */
+	normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((double) 0x80000000) : 1.0 / 256.0 ;
+
+	bufferlen = sizeof (ubuf.ucbuf) / SIZEOF_TRIBYTE ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.ucbuf, SIZEOF_TRIBYTE, bufferlen, psf) ;
+		let2d_array ((tribyte*) (ubuf.ucbuf), readcount, ptr + total, normfact) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* pcm_read_let2d */
+
+static sf_count_t
+pcm_read_bei2d (SF_PRIVATE *psf, double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+	double		normfact ;
+
+	normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((double) 0x80000000) : 1.0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ibuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.ibuf, sizeof (int), bufferlen, psf) ;
+		bei2d_array (ubuf.ibuf, readcount, ptr + total, normfact) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* pcm_read_bei2d */
+
+static sf_count_t
+pcm_read_lei2d (SF_PRIVATE *psf, double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+	double		normfact ;
+
+	normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((double) 0x80000000) : 1.0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ibuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.ibuf, sizeof (int), bufferlen, psf) ;
+		lei2d_array (ubuf.ibuf, readcount, ptr + total, normfact) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* pcm_read_lei2d */
+
+/*===============================================================================================
+**-----------------------------------------------------------------------------------------------
+**===============================================================================================
+*/
+
+static sf_count_t
+pcm_write_s2sc	(SF_PRIVATE *psf, const short *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.scbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		s2sc_array (ptr + total, ubuf.scbuf, bufferlen) ;
+		writecount = (int) psf_fwrite (ubuf.scbuf, sizeof (signed char), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* pcm_write_s2sc */
+
+static sf_count_t
+pcm_write_s2uc	(SF_PRIVATE *psf, const short *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ucbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		s2uc_array (ptr + total, ubuf.ucbuf, bufferlen) ;
+		writecount = (int) psf_fwrite (ubuf.ucbuf, sizeof (unsigned char), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* pcm_write_s2uc */
+
+static sf_count_t
+pcm_write_s2bes	(SF_PRIVATE *psf, const short *ptr, sf_count_t len)
+{
+#if CPU_IS_BIG_ENDIAN
+	return psf_fwrite (ptr, sizeof (short), len, psf) ;
+#else
+	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		endswap_short_copy (ubuf.sbuf, ptr + total, bufferlen) ;
+		writecount = (int) psf_fwrite (ubuf.sbuf, sizeof (short), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+#endif
+} /* pcm_write_s2bes */
+
+static sf_count_t
+pcm_write_s2les	(SF_PRIVATE *psf, const short *ptr, sf_count_t len)
+{
+#if CPU_IS_LITTLE_ENDIAN
+	return psf_fwrite (ptr, sizeof (short), len, psf) ;
+#else
+	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		endswap_short_copy (ubuf.sbuf, ptr + total, bufferlen) ;
+		writecount = (int) psf_fwrite (ubuf.sbuf, sizeof (short), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+#endif
+} /* pcm_write_s2les */
+
+static sf_count_t
+pcm_write_s2bet	(SF_PRIVATE *psf, const short *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = sizeof (ubuf.ucbuf) / SIZEOF_TRIBYTE ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		s2bet_array (ptr + total, (tribyte*) (ubuf.ucbuf), bufferlen) ;
+		writecount = (int) psf_fwrite (ubuf.ucbuf, SIZEOF_TRIBYTE, bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* pcm_write_s2bet */
+
+static sf_count_t
+pcm_write_s2let	(SF_PRIVATE *psf, const short *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = sizeof (ubuf.ucbuf) / SIZEOF_TRIBYTE ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		s2let_array (ptr + total, (tribyte*) (ubuf.ucbuf), bufferlen) ;
+		writecount = (int) psf_fwrite (ubuf.ucbuf, SIZEOF_TRIBYTE, bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* pcm_write_s2let */
+
+static sf_count_t
+pcm_write_s2bei	(SF_PRIVATE *psf, const short *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ibuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		s2bei_array (ptr + total, ubuf.ibuf, bufferlen) ;
+		writecount = (int) psf_fwrite (ubuf.ibuf, sizeof (int), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* pcm_write_s2bei */
+
+static sf_count_t
+pcm_write_s2lei	(SF_PRIVATE *psf, const short *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ibuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		s2lei_array (ptr + total, ubuf.ibuf, bufferlen) ;
+		writecount = (int) psf_fwrite (ubuf.ibuf, sizeof (int), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* pcm_write_s2lei */
+
+/*-----------------------------------------------------------------------------------------------
+*/
+
+static sf_count_t
+pcm_write_i2sc	(SF_PRIVATE *psf, const int *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.scbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		i2sc_array (ptr + total, ubuf.scbuf, bufferlen) ;
+		writecount = (int) psf_fwrite (ubuf.scbuf, sizeof (signed char), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* pcm_write_i2sc */
+
+static sf_count_t
+pcm_write_i2uc	(SF_PRIVATE *psf, const int *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ucbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		i2uc_array (ptr + total, ubuf.ucbuf, bufferlen) ;
+		writecount = (int) psf_fwrite (ubuf.ucbuf, sizeof (signed char), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* pcm_write_i2uc */
+
+static sf_count_t
+pcm_write_i2bes	(SF_PRIVATE *psf, const int *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		i2bes_array (ptr + total, ubuf.sbuf, bufferlen) ;
+		writecount = (int) psf_fwrite (ubuf.sbuf, sizeof (short), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* pcm_write_i2bes */
+
+static sf_count_t
+pcm_write_i2les	(SF_PRIVATE *psf, const int *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		i2les_array (ptr + total, ubuf.sbuf, bufferlen) ;
+		writecount = (int) psf_fwrite (ubuf.sbuf, sizeof (short), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* pcm_write_i2les */
+
+static sf_count_t
+pcm_write_i2bet	(SF_PRIVATE *psf, const int *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = sizeof (ubuf.ucbuf) / SIZEOF_TRIBYTE ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		i2bet_array (ptr + total, (tribyte*) (ubuf.ucbuf), bufferlen) ;
+		writecount = (int) psf_fwrite (ubuf.ucbuf, SIZEOF_TRIBYTE, bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* pcm_write_i2bet */
+
+static sf_count_t
+pcm_write_i2let	(SF_PRIVATE *psf, const int *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = sizeof (ubuf.ucbuf) / SIZEOF_TRIBYTE ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		i2let_array (ptr + total, (tribyte*) (ubuf.ucbuf), bufferlen) ;
+		writecount = (int) psf_fwrite (ubuf.ucbuf, SIZEOF_TRIBYTE, bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* pcm_write_i2les */
+
+static sf_count_t
+pcm_write_i2bei	(SF_PRIVATE *psf, const int *ptr, sf_count_t len)
+{
+#if CPU_IS_BIG_ENDIAN
+	return psf_fwrite (ptr, sizeof (int), len, psf) ;
+#else
+	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ibuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		endswap_int_copy (ubuf.ibuf, ptr + total, bufferlen) ;
+		writecount = (int) psf_fwrite (ubuf.ibuf, sizeof (int), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+#endif
+} /* pcm_write_i2bei */
+
+static sf_count_t
+pcm_write_i2lei	(SF_PRIVATE *psf, const int *ptr, sf_count_t len)
+{
+#if CPU_IS_LITTLE_ENDIAN
+	return psf_fwrite (ptr, sizeof (int), len, psf) ;
+#else
+	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ibuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		endswap_int_copy (ubuf.ibuf, ptr + total, bufferlen) ;
+		writecount = (int) psf_fwrite (ubuf.ibuf, sizeof (int), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+#endif
+} /* pcm_write_i2lei */
+
+/*------------------------------------------------------------------------------
+**==============================================================================
+**------------------------------------------------------------------------------
+*/
+
+static void
+f2sc_array (const float *src, signed char *dest, int count, int normalize)
+{	float normfact ;
+
+	normfact = normalize ? (1.0 * 0x7F) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	dest [i] = psf_lrintf (src [i] * normfact) ;
+		} ;
+} /* f2sc_array */
+
+static void
+f2sc_clip_array (const float *src, signed char *dest, int count, int normalize)
+{	float	normfact, scaled_value ;
+
+	normfact = normalize ? (8.0 * 0x10000000) : (1.0 * 0x1000000) ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	scaled_value = src [i] * normfact ;
+		if (scaled_value >= (1.0 * 0x7FFFFFFF))
+		{	dest [i] = 127 ;
+			continue ;
+			} ;
+		if (scaled_value <= (-8.0 * 0x10000000))
+		{	dest [i] = -128 ;
+			continue ;
+			} ;
+
+		dest [i] = psf_lrintf (scaled_value) >> 24 ;
+		} ;
+} /* f2sc_clip_array */
+
+static sf_count_t
+pcm_write_f2sc	(SF_PRIVATE *psf, const float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	void		(*convert) (const float *, signed char *, int, int) ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	convert = (psf->add_clipping) ? f2sc_clip_array : f2sc_array ;
+	bufferlen = ARRAY_LEN (ubuf.scbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		convert (ptr + total, ubuf.scbuf, bufferlen, psf->norm_float) ;
+		writecount = (int) psf_fwrite (ubuf.scbuf, sizeof (signed char), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* pcm_write_f2sc */
+
+/*==============================================================================
+*/
+
+static	void
+f2uc_array	(const float *src, unsigned char *dest, int count, int normalize)
+{	float normfact ;
+
+	normfact = normalize ? (1.0 * 0x7F) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	dest [i] = psf_lrintf (src [i] * normfact) + 128 ;
+		} ;
+} /* f2uc_array */
+
+static	void
+f2uc_clip_array	(const float *src, unsigned char *dest, int count, int normalize)
+{	float	normfact, scaled_value ;
+
+	normfact = normalize ? (8.0 * 0x10000000) : (1.0 * 0x1000000) ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	scaled_value = src [i] * normfact ;
+		if (scaled_value >= (1.0 * 0x7FFFFFFF))
+		{	dest [i] = 0xFF ;
+			continue ;
+			} ;
+		if (scaled_value <= (-8.0 * 0x10000000))
+		{	dest [i] = 0 ;
+			continue ;
+			} ;
+
+		dest [i] = (psf_lrintf (scaled_value) >> 24) + 128 ;
+		} ;
+} /* f2uc_clip_array */
+
+static sf_count_t
+pcm_write_f2uc	(SF_PRIVATE *psf, const float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	void		(*convert) (const float *, unsigned char *, int, int) ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	convert = (psf->add_clipping) ? f2uc_clip_array : f2uc_array ;
+	bufferlen = ARRAY_LEN (ubuf.ucbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		convert (ptr + total, ubuf.ucbuf, bufferlen, psf->norm_float) ;
+		writecount = (int) psf_fwrite (ubuf.ucbuf, sizeof (unsigned char), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* pcm_write_f2uc */
+
+/*==============================================================================
+*/
+
+static void
+f2bes_array (const float *src, short *dest, int count, int normalize)
+{	unsigned char	*ucptr ;
+	float 			normfact ;
+	short			value ;
+
+	normfact = normalize ? (1.0 * 0x7FFF) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	ucptr = (unsigned char*) &dest [i] ;
+		value = psf_lrintf (src [i] * normfact) ;
+		ucptr [1] = value ;
+		ucptr [0] = value >> 8 ;
+			} ;
+} /* f2bes_array */
+
+static void
+f2bes_clip_array (const float *src, short *dest, int count, int normalize)
+{	unsigned char	*ucptr ;
+	float			normfact, scaled_value ;
+	int				value ;
+
+	normfact = normalize ? (8.0 * 0x10000000) : (1.0 * 0x10000) ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	ucptr = (unsigned char*) &dest [i] ;
+		scaled_value = src [i] * normfact ;
+		if (scaled_value >= (1.0 * 0x7FFFFFFF))
+		{	ucptr [1] = 0xFF ;
+			ucptr [0] = 0x7F ;
+			continue ;
+		} ;
+		if (scaled_value <= (-8.0 * 0x10000000))
+		{	ucptr [1] = 0x00 ;
+			ucptr [0] = 0x80 ;
+			continue ;
+			} ;
+
+		value = psf_lrintf (scaled_value) ;
+		ucptr [1] = value >> 16 ;
+		ucptr [0] = value >> 24 ;
+		} ;
+} /* f2bes_clip_array */
+
+static sf_count_t
+pcm_write_f2bes	(SF_PRIVATE *psf, const float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	void		(*convert) (const float *, short *t, int, int) ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	convert = (psf->add_clipping) ? f2bes_clip_array : f2bes_array ;
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		convert (ptr + total, ubuf.sbuf, bufferlen, psf->norm_float) ;
+		writecount = (int) psf_fwrite (ubuf.sbuf, sizeof (short), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+				break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* pcm_write_f2bes */
+
+/*==============================================================================
+*/
+
+static void
+f2les_array (const float *src, short *dest, int count, int normalize)
+{	unsigned char	*ucptr ;
+	float			normfact ;
+	int				value ;
+
+	normfact = normalize ? (1.0 * 0x7FFF) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	ucptr = (unsigned char*) &dest [i] ;
+		value = psf_lrintf (src [i] * normfact) ;
+		ucptr [0] = value ;
+		ucptr [1] = value >> 8 ;
+		} ;
+} /* f2les_array */
+
+static void
+f2les_clip_array (const float *src, short *dest, int count, int normalize)
+{	unsigned char	*ucptr ;
+	float			normfact, scaled_value ;
+	int				value ;
+
+	normfact = normalize ? (8.0 * 0x10000000) : (1.0 * 0x10000) ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	ucptr = (unsigned char*) &dest [i] ;
+		scaled_value = src [i] * normfact ;
+		if (scaled_value >= (1.0 * 0x7FFFFFFF))
+		{	ucptr [0] = 0xFF ;
+			ucptr [1] = 0x7F ;
+			continue ;
+			} ;
+		if (scaled_value <= (-8.0 * 0x10000000))
+		{	ucptr [0] = 0x00 ;
+			ucptr [1] = 0x80 ;
+			continue ;
+			} ;
+
+		value = psf_lrintf (scaled_value) ;
+		ucptr [0] = value >> 16 ;
+		ucptr [1] = value >> 24 ;
+		} ;
+} /* f2les_clip_array */
+
+static sf_count_t
+pcm_write_f2les	(SF_PRIVATE *psf, const float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	void		(*convert) (const float *, short *t, int, int) ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	convert = (psf->add_clipping) ? f2les_clip_array : f2les_array ;
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		convert (ptr + total, ubuf.sbuf, bufferlen, psf->norm_float) ;
+		writecount = (int) psf_fwrite (ubuf.sbuf, sizeof (short), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* pcm_write_f2les */
+
+/*==============================================================================
+*/
+
+static void
+f2let_array (const float *src, tribyte *dest, int count, int normalize)
+{	float	normfact ;
+	int		value ;
+
+	normfact = normalize ? (1.0 * 0x7FFFFF) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	value = psf_lrintf (src [i] * normfact) ;
+		dest [i].bytes [0] = value ;
+		dest [i].bytes [1] = value >> 8 ;
+		dest [i].bytes [2] = value >> 16 ;
+		} ;
+} /* f2let_array */
+
+static void
+f2let_clip_array (const float *src, tribyte *dest, int count, int normalize)
+{	float	normfact, scaled_value ;
+	int		value ;
+
+	normfact = normalize ? (8.0 * 0x10000000) : (1.0 * 0x100) ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	scaled_value = src [i] * normfact ;
+#if CPU_CLIPS_POSITIVE == 0
+		if (scaled_value >= (1.0 * 0x7FFFFFFF))
+		{	dest [i].bytes [0] = 0xFF ;
+			dest [i].bytes [1] = 0xFF ;
+			dest [i].bytes [2] = 0x7F ;
+			continue ;
+			} ;
+#endif
+#if CPU_CLIPS_NEGATIVE == 0
+		if (scaled_value <= (-8.0 * 0x10000000))
+		{	dest [i].bytes [0] = 0x00 ;
+			dest [i].bytes [1] = 0x00 ;
+			dest [i].bytes [2] = 0x80 ;
+			continue ;
+		} ;
+#endif
+
+		value = psf_lrintf (scaled_value) ;
+		dest [i].bytes [0] = value >> 8 ;
+		dest [i].bytes [1] = value >> 16 ;
+		dest [i].bytes [2] = value >> 24 ;
+		} ;
+} /* f2let_clip_array */
+
+static sf_count_t
+pcm_write_f2let	(SF_PRIVATE *psf, const float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	void		(*convert) (const float *, tribyte *, int, int) ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	convert = (psf->add_clipping) ? f2let_clip_array : f2let_array ;
+	bufferlen = sizeof (ubuf.ucbuf) / SIZEOF_TRIBYTE ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		convert (ptr + total, (tribyte*) (ubuf.ucbuf), bufferlen, psf->norm_float) ;
+		writecount = (int) psf_fwrite (ubuf.ucbuf, SIZEOF_TRIBYTE, bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* pcm_write_f2let */
+
+/*==============================================================================
+*/
+
+static void
+f2bet_array (const float *src, tribyte *dest, int count, int normalize)
+{	float	normfact ;
+	int		value ;
+
+	normfact = normalize ? (1.0 * 0x7FFFFF) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	value = psf_lrintf (src [i] * normfact) ;
+		dest [i].bytes [0] = value >> 16 ;
+		dest [i].bytes [1] = value >> 8 ;
+		dest [i].bytes [2] = value ;
+		} ;
+} /* f2bet_array */
+
+static void
+f2bet_clip_array (const float *src, tribyte *dest, int count, int normalize)
+{	float	normfact, scaled_value ;
+	int		value ;
+
+	normfact = normalize ? (8.0 * 0x10000000) : (1.0 * 0x100) ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	scaled_value = src [i] * normfact ;
+#if CPU_CLIPS_POSITIVE == 0
+		if (scaled_value >= (1.0 * 0x7FFFFFFF))
+		{	dest [i].bytes [0] = 0x7F ;
+			dest [i].bytes [1] = 0xFF ;
+			dest [i].bytes [2] = 0xFF ;
+			continue ;
+			} ;
+#endif
+#if CPU_CLIPS_NEGATIVE == 0
+		if (scaled_value <= (-8.0 * 0x10000000))
+		{	dest [i].bytes [0] = 0x80 ;
+			dest [i].bytes [1] = 0x00 ;
+			dest [i].bytes [2] = 0x00 ;
+			continue ;
+		} ;
+#endif
+
+		value = psf_lrint (scaled_value) ;
+		dest [i].bytes [0] = value >> 24 ;
+		dest [i].bytes [1] = value >> 16 ;
+		dest [i].bytes [2] = value >> 8 ;
+		} ;
+} /* f2bet_clip_array */
+
+static sf_count_t
+pcm_write_f2bet	(SF_PRIVATE *psf, const float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	void		(*convert) (const float *, tribyte *, int, int) ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	convert = (psf->add_clipping) ? f2bet_clip_array : f2bet_array ;
+	bufferlen = sizeof (ubuf.ucbuf) / SIZEOF_TRIBYTE ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		convert (ptr + total, (tribyte*) (ubuf.ucbuf), bufferlen, psf->norm_float) ;
+		writecount = (int) psf_fwrite (ubuf.ucbuf, SIZEOF_TRIBYTE, bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* pcm_write_f2bet */
+
+/*==============================================================================
+*/
+
+static void
+f2bei_array (const float *src, int *dest, int count, int normalize)
+{	unsigned char	*ucptr ;
+	float			normfact ;
+	int				value ;
+
+	normfact = normalize ? (1.0 * 0x7FFFFFFF) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	ucptr = (unsigned char*) &dest [i] ;
+		value = psf_lrintf (src [i] * normfact) ;
+		ucptr [0] = value >> 24 ;
+		ucptr [1] = value >> 16 ;
+		ucptr [2] = value >> 8 ;
+		ucptr [3] = value ;
+		} ;
+} /* f2bei_array */
+
+static void
+f2bei_clip_array (const float *src, int *dest, int count, int normalize)
+{	unsigned char	*ucptr ;
+	float			normfact, scaled_value ;
+	int				value ;
+
+	normfact = normalize ? (8.0 * 0x10000000) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	ucptr = (unsigned char*) &dest [i] ;
+		scaled_value = src [i] * normfact ;
+#if CPU_CLIPS_POSITIVE == 0
+		if (scaled_value >= 1.0 * 0x7FFFFFFF)
+		{	ucptr [0] = 0x7F ;
+			ucptr [1] = 0xFF ;
+			ucptr [2] = 0xFF ;
+			ucptr [3] = 0xFF ;
+			continue ;
+			} ;
+#endif
+#if CPU_CLIPS_NEGATIVE == 0
+		if (scaled_value <= (-8.0 * 0x10000000))
+		{	ucptr [0] = 0x80 ;
+			ucptr [1] = 0x00 ;
+			ucptr [2] = 0x00 ;
+			ucptr [3] = 0x00 ;
+			continue ;
+		} ;
+#endif
+
+		value = psf_lrintf (scaled_value) ;
+		ucptr [0] = value >> 24 ;
+		ucptr [1] = value >> 16 ;
+		ucptr [2] = value >> 8 ;
+		ucptr [3] = value ;
+		} ;
+} /* f2bei_clip_array */
+
+static sf_count_t
+pcm_write_f2bei	(SF_PRIVATE *psf, const float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	void		(*convert) (const float *, int *, int, int) ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	convert = (psf->add_clipping) ? f2bei_clip_array : f2bei_array ;
+	bufferlen = ARRAY_LEN (ubuf.ibuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		convert (ptr + total, ubuf.ibuf, bufferlen, psf->norm_float) ;
+		writecount = (int) psf_fwrite (ubuf.ibuf, sizeof (int), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* pcm_write_f2bei */
+
+/*==============================================================================
+*/
+
+static void
+f2lei_array (const float *src, int *dest, int count, int normalize)
+{	unsigned char	*ucptr ;
+	float			normfact ;
+	int				value ;
+
+	normfact = normalize ? (1.0 * 0x7FFFFFFF) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	ucptr = (unsigned char*) &dest [i] ;
+		value = psf_lrintf (src [i] * normfact) ;
+		ucptr [0] = value ;
+		ucptr [1] = value >> 8 ;
+		ucptr [2] = value >> 16 ;
+		ucptr [3] = value >> 24 ;
+		} ;
+} /* f2lei_array */
+
+static void
+f2lei_clip_array (const float *src, int *dest, int count, int normalize)
+{	unsigned char	*ucptr ;
+	float			normfact, scaled_value ;
+	int				value ;
+
+	normfact = normalize ? (8.0 * 0x10000000) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	ucptr = (unsigned char*) &dest [i] ;
+		scaled_value = src [i] * normfact ;
+#if CPU_CLIPS_POSITIVE == 0
+		if (scaled_value >= (1.0 * 0x7FFFFFFF))
+		{	ucptr [0] = 0xFF ;
+			ucptr [1] = 0xFF ;
+			ucptr [2] = 0xFF ;
+			ucptr [3] = 0x7F ;
+			continue ;
+			} ;
+#endif
+#if CPU_CLIPS_NEGATIVE == 0
+		if (scaled_value <= (-8.0 * 0x10000000))
+		{	ucptr [0] = 0x00 ;
+			ucptr [1] = 0x00 ;
+			ucptr [2] = 0x00 ;
+			ucptr [3] = 0x80 ;
+			continue ;
+			} ;
+#endif
+
+		value = psf_lrintf (scaled_value) ;
+		ucptr [0] = value ;
+		ucptr [1] = value >> 8 ;
+		ucptr [2] = value >> 16 ;
+		ucptr [3] = value >> 24 ;
+		} ;
+} /* f2lei_clip_array */
+
+static sf_count_t
+pcm_write_f2lei	(SF_PRIVATE *psf, const float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	void		(*convert) (const float *, int *, int, int) ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	convert = (psf->add_clipping) ? f2lei_clip_array : f2lei_array ;
+	bufferlen = ARRAY_LEN (ubuf.ibuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		convert (ptr + total, ubuf.ibuf, bufferlen, psf->norm_float) ;
+		writecount = (int) psf_fwrite (ubuf.ibuf, sizeof (int), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* pcm_write_f2lei */
+
+/*==============================================================================
+*/
+
+static void
+d2sc_array	(const double *src, signed char *dest, int count, int normalize)
+{	double	normfact ;
+
+	normfact = normalize ? (1.0 * 0x7F) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	dest [i] = psf_lrint (src [i] * normfact) ;
+		} ;
+} /* d2sc_array */
+
+static void
+d2sc_clip_array	(const double *src, signed char *dest, int count, int normalize)
+{	double	normfact, scaled_value ;
+
+	normfact = normalize ? (8.0 * 0x10000000) : (1.0 * 0x1000000) ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	scaled_value = src [i] * normfact ;
+		if (scaled_value >= (1.0 * 0x7FFFFFFF))
+		{	dest [i] = 127 ;
+			continue ;
+			} ;
+		if (scaled_value <= (-8.0 * 0x10000000))
+		{	dest [i] = -128 ;
+			continue ;
+			} ;
+
+		dest [i] = psf_lrintf (scaled_value) >> 24 ;
+		} ;
+} /* d2sc_clip_array */
+
+static sf_count_t
+pcm_write_d2sc	(SF_PRIVATE *psf, const double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	void		(*convert) (const double *, signed char *, int, int) ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	convert = (psf->add_clipping) ? d2sc_clip_array : d2sc_array ;
+	bufferlen = ARRAY_LEN (ubuf.scbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		convert (ptr + total, ubuf.scbuf, bufferlen, psf->norm_double) ;
+		writecount = (int) psf_fwrite (ubuf.scbuf, sizeof (signed char), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* pcm_write_d2sc */
+
+/*==============================================================================
+*/
+
+static	void
+d2uc_array	(const double *src, unsigned char *dest, int count, int normalize)
+{	double normfact ;
+
+	normfact = normalize ? (1.0 * 0x7F) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	dest [i] = psf_lrint (src [i] * normfact) + 128 ;
+		} ;
+} /* d2uc_array */
+
+static	void
+d2uc_clip_array	(const double *src, unsigned char *dest, int count, int normalize)
+{	double	normfact, scaled_value ;
+
+	normfact = normalize ? (8.0 * 0x10000000) : (1.0 * 0x1000000) ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	scaled_value = src [i] * normfact ;
+		if (scaled_value >= (1.0 * 0x7FFFFFFF))
+		{	dest [i] = 255 ;
+			continue ;
+			} ;
+		if (scaled_value <= (-8.0 * 0x10000000))
+		{	dest [i] = 0 ;
+			continue ;
+			} ;
+
+		dest [i] = (psf_lrint (src [i] * normfact) >> 24) + 128 ;
+		} ;
+} /* d2uc_clip_array */
+
+static sf_count_t
+pcm_write_d2uc	(SF_PRIVATE *psf, const double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	void		(*convert) (const double *, unsigned char *, int, int) ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	convert = (psf->add_clipping) ? d2uc_clip_array : d2uc_array ;
+	bufferlen = ARRAY_LEN (ubuf.ucbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		convert (ptr + total, ubuf.ucbuf, bufferlen, psf->norm_double) ;
+		writecount = (int) psf_fwrite (ubuf.ucbuf, sizeof (unsigned char), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* pcm_write_d2uc */
+
+/*==============================================================================
+*/
+
+static void
+d2bes_array (const double *src, short *dest, int count, int normalize)
+{	unsigned char	*ucptr ;
+	short			value ;
+	double			normfact ;
+
+	normfact = normalize ? (1.0 * 0x7FFF) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	ucptr = (unsigned char*) &dest [i] ;
+		value = psf_lrint (src [i] * normfact) ;
+		ucptr [1] = value ;
+		ucptr [0] = value >> 8 ;
+		} ;
+} /* d2bes_array */
+
+static void
+d2bes_clip_array (const double *src, short *dest, int count, int normalize)
+{	unsigned char	*ucptr ;
+	double			normfact, scaled_value ;
+	int				value ;
+
+	normfact = normalize ? (8.0 * 0x10000000) : (1.0 * 0x10000) ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	ucptr = (unsigned char*) &dest [i] ;
+		scaled_value = src [i] * normfact ;
+		if (scaled_value >= (1.0 * 0x7FFFFFFF))
+		{	ucptr [1] = 0xFF ;
+			ucptr [0] = 0x7F ;
+			continue ;
+			} ;
+		if (scaled_value <= (-8.0 * 0x10000000))
+		{	ucptr [1] = 0x00 ;
+			ucptr [0] = 0x80 ;
+			continue ;
+			} ;
+
+		value = psf_lrint (scaled_value) ;
+		ucptr [1] = value >> 16 ;
+		ucptr [0] = value >> 24 ;
+		} ;
+} /* d2bes_clip_array */
+
+static sf_count_t
+pcm_write_d2bes	(SF_PRIVATE *psf, const double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	void		(*convert) (const double *, short *, int, int) ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	convert = (psf->add_clipping) ? d2bes_clip_array : d2bes_array ;
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		convert (ptr + total, ubuf.sbuf, bufferlen, psf->norm_double) ;
+		writecount = (int) psf_fwrite (ubuf.sbuf, sizeof (short), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* pcm_write_d2bes */
+
+/*==============================================================================
+*/
+
+static void
+d2les_array (const double *src, short *dest, int count, int normalize)
+{	unsigned char	*ucptr ;
+	short			value ;
+	double			normfact ;
+
+	normfact = normalize ? (1.0 * 0x7FFF) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	ucptr = (unsigned char*) &dest [i] ;
+		value = psf_lrint (src [i] * normfact) ;
+		ucptr [0] = value ;
+		ucptr [1] = value >> 8 ;
+		} ;
+} /* d2les_array */
+
+static void
+d2les_clip_array (const double *src, short *dest, int count, int normalize)
+{	unsigned char	*ucptr ;
+	int				value ;
+	double			normfact, scaled_value ;
+
+	normfact = normalize ? (8.0 * 0x10000000) : (1.0 * 0x10000) ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	ucptr = (unsigned char*) &dest [i] ;
+		scaled_value = src [i] * normfact ;
+		if (scaled_value >= (1.0 * 0x7FFFFFFF))
+		{	ucptr [0] = 0xFF ;
+			ucptr [1] = 0x7F ;
+			continue ;
+			} ;
+		if (scaled_value <= (-8.0 * 0x10000000))
+		{	ucptr [0] = 0x00 ;
+			ucptr [1] = 0x80 ;
+			continue ;
+			} ;
+
+		value = psf_lrint (scaled_value) ;
+		ucptr [0] = value >> 16 ;
+		ucptr [1] = value >> 24 ;
+		} ;
+} /* d2les_clip_array */
+
+static sf_count_t
+pcm_write_d2les	(SF_PRIVATE *psf, const double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	void		(*convert) (const double *, short *, int, int) ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	convert = (psf->add_clipping) ? d2les_clip_array : d2les_array ;
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		convert (ptr + total, ubuf.sbuf, bufferlen, psf->norm_double) ;
+		writecount = (int) psf_fwrite (ubuf.sbuf, sizeof (short), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* pcm_write_d2les */
+
+/*==============================================================================
+*/
+
+static void
+d2let_array (const double *src, tribyte *dest, int count, int normalize)
+{	int		value ;
+	double	normfact ;
+
+	normfact = normalize ? (1.0 * 0x7FFFFF) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	value = psf_lrint (src [i] * normfact) ;
+		dest [i].bytes [0] = value ;
+		dest [i].bytes [1] = value >> 8 ;
+		dest [i].bytes [2] = value >> 16 ;
+		} ;
+} /* d2let_array */
+
+static void
+d2let_clip_array (const double *src, tribyte *dest, int count, int normalize)
+{	int		value ;
+	double	normfact, scaled_value ;
+
+	normfact = normalize ? (8.0 * 0x10000000) : (1.0 * 0x100) ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	scaled_value = src [i] * normfact ;
+#if CPU_CLIPS_POSITIVE == 0
+		if (scaled_value >= (1.0 * 0x7FFFFFFF))
+		{	dest [i].bytes [0] = 0xFF ;
+			dest [i].bytes [1] = 0xFF ;
+			dest [i].bytes [2] = 0x7F ;
+			continue ;
+			} ;
+#endif
+#if CPU_CLIPS_NEGATIVE == 0
+		if (scaled_value <= (-8.0 * 0x10000000))
+		{	dest [i].bytes [0] = 0x00 ;
+			dest [i].bytes [1] = 0x00 ;
+			dest [i].bytes [2] = 0x80 ;
+			continue ;
+			} ;
+#endif
+
+		value = psf_lrint (scaled_value) ;
+		dest [i].bytes [0] = value >> 8 ;
+		dest [i].bytes [1] = value >> 16 ;
+		dest [i].bytes [2] = value >> 24 ;
+		} ;
+} /* d2let_clip_array */
+
+static sf_count_t
+pcm_write_d2let	(SF_PRIVATE *psf, const double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	void		(*convert) (const double *, tribyte *, int, int) ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	convert = (psf->add_clipping) ? d2let_clip_array : d2let_array ;
+	bufferlen = sizeof (ubuf.ucbuf) / SIZEOF_TRIBYTE ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		convert (ptr + total, (tribyte*) (ubuf.ucbuf), bufferlen, psf->norm_double) ;
+		writecount = (int) psf_fwrite (ubuf.ucbuf, SIZEOF_TRIBYTE, bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* pcm_write_d2let */
+
+/*==============================================================================
+*/
+
+static void
+d2bet_array (const double *src, tribyte *dest, int count, int normalize)
+{	int		value ;
+	double	normfact ;
+
+	normfact = normalize ? (1.0 * 0x7FFFFF) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	value = psf_lrint (src [i] * normfact) ;
+		dest [i].bytes [2] = value ;
+		dest [i].bytes [1] = value >> 8 ;
+		dest [i].bytes [0] = value >> 16 ;
+		} ;
+} /* d2bet_array */
+
+static void
+d2bet_clip_array (const double *src, tribyte *dest, int count, int normalize)
+{	int		value ;
+	double	normfact, scaled_value ;
+
+	normfact = normalize ? (8.0 * 0x10000000) : (1.0 * 0x100) ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	scaled_value = src [i] * normfact ;
+#if CPU_CLIPS_POSITIVE == 0
+		if (scaled_value >= (1.0 * 0x7FFFFFFF))
+		{	dest [i].bytes [2] = 0xFF ;
+			dest [i].bytes [1] = 0xFF ;
+			dest [i].bytes [0] = 0x7F ;
+			continue ;
+			} ;
+#endif
+#if CPU_CLIPS_NEGATIVE == 0
+		if (scaled_value <= (-8.0 * 0x10000000))
+		{	dest [i].bytes [2] = 0x00 ;
+			dest [i].bytes [1] = 0x00 ;
+			dest [i].bytes [0] = 0x80 ;
+			continue ;
+			} ;
+#endif
+
+		value = psf_lrint (scaled_value) ;
+		dest [i].bytes [2] = value >> 8 ;
+		dest [i].bytes [1] = value >> 16 ;
+		dest [i].bytes [0] = value >> 24 ;
+		} ;
+} /* d2bet_clip_array */
+
+static sf_count_t
+pcm_write_d2bet	(SF_PRIVATE *psf, const double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	void		(*convert) (const double *, tribyte *, int, int) ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	convert = (psf->add_clipping) ? d2bet_clip_array : d2bet_array ;
+	bufferlen = sizeof (ubuf.ucbuf) / SIZEOF_TRIBYTE ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		convert (ptr + total, (tribyte*) (ubuf.ucbuf), bufferlen, psf->norm_double) ;
+		writecount = (int) psf_fwrite (ubuf.ucbuf, SIZEOF_TRIBYTE, bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* pcm_write_d2bet */
+
+/*==============================================================================
+*/
+
+static void
+d2bei_array (const double *src, int *dest, int count, int normalize)
+{	unsigned char	*ucptr ;
+	int				value ;
+	double			normfact ;
+
+	normfact = normalize ? (1.0 * 0x7FFFFFFF) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	ucptr = (unsigned char*) &dest [i] ;
+		value = psf_lrint (src [i] * normfact) ;
+		ucptr [0] = value >> 24 ;
+		ucptr [1] = value >> 16 ;
+		ucptr [2] = value >> 8 ;
+		ucptr [3] = value ;
+		} ;
+} /* d2bei_array */
+
+static void
+d2bei_clip_array (const double *src, int *dest, int count, int normalize)
+{	unsigned char	*ucptr ;
+	int				value ;
+	double			normfact, scaled_value ;
+
+	normfact = normalize ? (8.0 * 0x10000000) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	ucptr = (unsigned char*) &dest [i] ;
+		scaled_value = src [i] * normfact ;
+#if CPU_CLIPS_POSITIVE == 0
+		if (scaled_value >= (1.0 * 0x7FFFFFFF))
+		{	ucptr [3] = 0xFF ;
+			ucptr [2] = 0xFF ;
+			ucptr [1] = 0xFF ;
+			ucptr [0] = 0x7F ;
+			continue ;
+			} ;
+#endif
+#if CPU_CLIPS_NEGATIVE == 0
+		if (scaled_value <= (-8.0 * 0x10000000))
+		{	ucptr [3] = 0x00 ;
+			ucptr [2] = 0x00 ;
+			ucptr [1] = 0x00 ;
+			ucptr [0] = 0x80 ;
+			continue ;
+			} ;
+#endif
+
+		value = psf_lrint (scaled_value) ;
+		ucptr [0] = value >> 24 ;
+		ucptr [1] = value >> 16 ;
+		ucptr [2] = value >> 8 ;
+		ucptr [3] = value ;
+		} ;
+} /* d2bei_clip_array */
+
+static sf_count_t
+pcm_write_d2bei	(SF_PRIVATE *psf, const double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	void		(*convert) (const double *, int *, int, int) ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	convert = (psf->add_clipping) ? d2bei_clip_array : d2bei_array ;
+	bufferlen = ARRAY_LEN (ubuf.ibuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		convert (ptr + total, ubuf.ibuf, bufferlen, psf->norm_double) ;
+		writecount = (int) psf_fwrite (ubuf.ibuf, sizeof (int), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* pcm_write_d2bei */
+
+/*==============================================================================
+*/
+
+static void
+d2lei_array (const double *src, int *dest, int count, int normalize)
+{	unsigned char	*ucptr ;
+	int				value ;
+	double			normfact ;
+
+	normfact = normalize ? (1.0 * 0x7FFFFFFF) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	ucptr = (unsigned char*) &dest [i] ;
+		value = psf_lrint (src [i] * normfact) ;
+		ucptr [0] = value ;
+		ucptr [1] = value >> 8 ;
+		ucptr [2] = value >> 16 ;
+		ucptr [3] = value >> 24 ;
+		} ;
+} /* d2lei_array */
+
+static void
+d2lei_clip_array (const double *src, int *dest, int count, int normalize)
+{	unsigned char	*ucptr ;
+	int				value ;
+	double			normfact, scaled_value ;
+
+	normfact = normalize ? (8.0 * 0x10000000) : 1.0 ;
+
+	for (int i = 0 ; i < count ; i++)
+	{	ucptr = (unsigned char*) &dest [i] ;
+		scaled_value = src [i] * normfact ;
+#if CPU_CLIPS_POSITIVE == 0
+		if (scaled_value >= (1.0 * 0x7FFFFFFF))
+		{	ucptr [0] = 0xFF ;
+			ucptr [1] = 0xFF ;
+			ucptr [2] = 0xFF ;
+			ucptr [3] = 0x7F ;
+			continue ;
+			} ;
+#endif
+#if CPU_CLIPS_NEGATIVE == 0
+		if (scaled_value <= (-8.0 * 0x10000000))
+		{	ucptr [0] = 0x00 ;
+			ucptr [1] = 0x00 ;
+			ucptr [2] = 0x00 ;
+			ucptr [3] = 0x80 ;
+			continue ;
+			} ;
+#endif
+
+		value = psf_lrint (scaled_value) ;
+		ucptr [0] = value ;
+		ucptr [1] = value >> 8 ;
+		ucptr [2] = value >> 16 ;
+		ucptr [3] = value >> 24 ;
+		} ;
+} /* d2lei_clip_array */
+
+static sf_count_t
+pcm_write_d2lei	(SF_PRIVATE *psf, const double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	void		(*convert) (const double *, int *, int, int) ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	convert = (psf->add_clipping) ? d2lei_clip_array : d2lei_array ;
+	bufferlen = ARRAY_LEN (ubuf.ibuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		convert (ptr + total, ubuf.ibuf, bufferlen, psf->norm_double) ;
+		writecount = (int) psf_fwrite (ubuf.ibuf, sizeof (int), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* pcm_write_d2lei */
+
diff --git a/src/pvf.c b/src/pvf.c
new file mode 100644
index 0000000..714f5b3
--- /dev/null
+++ b/src/pvf.c
@@ -0,0 +1,188 @@
+/*
+** Copyright (C) 2002-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<fcntl.h>
+#include	<string.h>
+#include	<ctype.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+/*------------------------------------------------------------------------------
+** Macros to handle big/little endian issues.
+*/
+
+#define PVF1_MARKER		(MAKE_MARKER ('P', 'V', 'F', '1'))
+
+/*------------------------------------------------------------------------------
+** Private static functions.
+*/
+
+static	int		pvf_close		(SF_PRIVATE *psf) ;
+
+static int		pvf_write_header (SF_PRIVATE *psf, int calc_length) ;
+static int		pvf_read_header (SF_PRIVATE *psf) ;
+
+/*------------------------------------------------------------------------------
+** Public function.
+*/
+
+int
+pvf_open	(SF_PRIVATE *psf)
+{	int		subformat ;
+	int		error = 0 ;
+
+	if (psf->file.mode == SFM_READ || (psf->file.mode == SFM_RDWR && psf->filelength > 0))
+	{	if ((error = pvf_read_header (psf)))
+			return error ;
+		} ;
+
+	subformat = SF_CODEC (psf->sf.format) ;
+
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	if ((SF_CONTAINER (psf->sf.format)) != SF_FORMAT_PVF)
+			return	SFE_BAD_OPEN_FORMAT ;
+
+		psf->endian = SF_ENDIAN_BIG ;
+
+		if (pvf_write_header (psf, SF_FALSE))
+			return psf->error ;
+
+		psf->write_header = pvf_write_header ;
+		} ;
+
+	psf->container_close = pvf_close ;
+
+	psf->blockwidth = psf->bytewidth * psf->sf.channels ;
+
+	switch (subformat)
+	{	case SF_FORMAT_PCM_S8 :	/* 8-bit linear PCM. */
+		case SF_FORMAT_PCM_16 :	/* 16-bit linear PCM. */
+		case SF_FORMAT_PCM_32 :	/* 32-bit linear PCM. */
+				error = pcm_init (psf) ;
+				break ;
+
+		default :	break ;
+		} ;
+
+	return error ;
+} /* pvf_open */
+
+/*------------------------------------------------------------------------------
+*/
+
+static int
+pvf_close	(SF_PRIVATE * UNUSED (psf))
+{
+	return 0 ;
+} /* pvf_close */
+
+static int
+pvf_write_header (SF_PRIVATE *psf, int UNUSED (calc_length))
+{	sf_count_t	current ;
+
+	if (psf->pipeoffset > 0)
+		return 0 ;
+
+	current = psf_ftell (psf) ;
+
+	/* Reset the current header length to zero. */
+	psf->header.ptr [0] = 0 ;
+	psf->header.indx = 0 ;
+
+	if (psf->is_pipe == SF_FALSE)
+		psf_fseek (psf, 0, SEEK_SET) ;
+
+	snprintf ((char*) psf->header.ptr, psf->header.len, "PVF1\n%d %d %d\n",
+		psf->sf.channels, psf->sf.samplerate, psf->bytewidth * 8) ;
+
+	psf->header.indx = strlen ((char*) psf->header.ptr) ;
+
+	/* Header construction complete so write it out. */
+	psf_fwrite (psf->header.ptr, psf->header.indx, 1, psf) ;
+
+	if (psf->error)
+		return psf->error ;
+
+	psf->dataoffset = psf->header.indx ;
+
+	if (current > 0)
+		psf_fseek (psf, current, SEEK_SET) ;
+
+	return psf->error ;
+} /* pvf_write_header */
+
+static int
+pvf_read_header (SF_PRIVATE *psf)
+{	char	buffer [32] ;
+	int		marker, channels, samplerate, bitwidth ;
+
+	psf_binheader_readf (psf, "pmj", 0, &marker, 1) ;
+	psf_log_printf (psf, "%M\n", marker) ;
+
+	if (marker != PVF1_MARKER)
+		return SFE_PVF_NO_PVF1 ;
+
+	/* Grab characters up until a newline which is replaced by an EOS. */
+	psf_binheader_readf (psf, "G", buffer, sizeof (buffer)) ;
+
+	if (sscanf (buffer, "%d %d %d", &channels, &samplerate, &bitwidth) != 3)
+		return SFE_PVF_BAD_HEADER ;
+
+	psf_log_printf (psf, " Channels    : %d\n Sample rate : %d\n Bit width   : %d\n",
+				channels, samplerate, bitwidth) ;
+
+	psf->sf.channels = channels ;
+	psf->sf.samplerate = samplerate ;
+
+	switch (bitwidth)
+	{	case 8 :
+				psf->sf.format = SF_FORMAT_PVF | SF_FORMAT_PCM_S8 ;
+				psf->bytewidth = 1 ;
+				break ;
+
+		case 16 :
+				psf->sf.format = SF_FORMAT_PVF | SF_FORMAT_PCM_16 ;
+				psf->bytewidth = 2 ;
+				break ;
+		case 32 :
+				psf->sf.format = SF_FORMAT_PVF | SF_FORMAT_PCM_32 ;
+				psf->bytewidth = 4 ;
+				break ;
+
+		default :
+				return SFE_PVF_BAD_BITWIDTH ;
+		} ;
+
+	psf->dataoffset = psf_ftell (psf) ;
+	psf_log_printf (psf, " Data Offset : %D\n", psf->dataoffset) ;
+
+	psf->endian = SF_ENDIAN_BIG ;
+
+	psf->datalength = psf->filelength - psf->dataoffset ;
+	psf->blockwidth = psf->sf.channels * psf->bytewidth ;
+
+	if (! psf->sf.frames && psf->blockwidth)
+		psf->sf.frames = (psf->filelength - psf->dataoffset) / psf->blockwidth ;
+
+	return 0 ;
+} /* pvf_read_header */
diff --git a/src/raw.c b/src/raw.c
new file mode 100644
index 0000000..c8fcbfd
--- /dev/null
+++ b/src/raw.c
@@ -0,0 +1,111 @@
+/*
+** Copyright (C) 1999-2011 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+
+#include	"sndfile.h"
+#include	"common.h"
+
+/*------------------------------------------------------------------------------
+** Public function.
+*/
+
+int
+raw_open	(SF_PRIVATE *psf)
+{	int	subformat, error = SFE_NO_ERROR ;
+
+	subformat = SF_CODEC (psf->sf.format) ;
+
+	psf->endian = SF_ENDIAN (psf->sf.format) ;
+
+	if (CPU_IS_BIG_ENDIAN && (psf->endian == 0 || psf->endian == SF_ENDIAN_CPU))
+		psf->endian = SF_ENDIAN_BIG ;
+	else if (CPU_IS_LITTLE_ENDIAN && (psf->endian == 0 || psf->endian == SF_ENDIAN_CPU))
+		psf->endian = SF_ENDIAN_LITTLE ;
+
+	psf->blockwidth = psf->bytewidth * psf->sf.channels ;
+	psf->dataoffset = 0 ;
+	psf->datalength = psf->filelength ;
+
+	switch (subformat)
+	{	case SF_FORMAT_PCM_S8 :
+				error = pcm_init (psf) ;
+				break ;
+
+		case SF_FORMAT_PCM_U8 :
+				error = pcm_init (psf) ;
+				break ;
+
+		case SF_FORMAT_PCM_16 :
+		case SF_FORMAT_PCM_24 :
+		case SF_FORMAT_PCM_32 :
+				error = pcm_init (psf) ;
+				break ;
+
+		case SF_FORMAT_ULAW :
+				error = ulaw_init (psf) ;
+				break ;
+
+		case SF_FORMAT_ALAW :
+				error = alaw_init (psf) ;
+				break ;
+
+		case SF_FORMAT_GSM610 :
+				error = gsm610_init (psf) ;
+				break ;
+
+		/* Lite remove start */
+
+		case SF_FORMAT_NMS_ADPCM_16 :
+		case SF_FORMAT_NMS_ADPCM_24 :
+		case SF_FORMAT_NMS_ADPCM_32 :
+				error = nms_adpcm_init (psf) ;
+				break ;
+
+		case SF_FORMAT_FLOAT :
+				error = float32_init (psf) ;
+				break ;
+
+		case SF_FORMAT_DOUBLE :
+				error = double64_init (psf) ;
+				break ;
+
+		case SF_FORMAT_DWVW_12 :
+				error = dwvw_init (psf, 12) ;
+				break ;
+
+		case SF_FORMAT_DWVW_16 :
+				error = dwvw_init (psf, 16) ;
+				break ;
+
+		case SF_FORMAT_DWVW_24 :
+				error = dwvw_init (psf, 24) ;
+				break ;
+
+		case SF_FORMAT_VOX_ADPCM :
+				error = vox_adpcm_init (psf) ;
+				break ;
+		/* Lite remove end */
+
+		default : return SFE_BAD_OPEN_FORMAT ;
+		} ;
+
+	return error ;
+} /* raw_open */
diff --git a/src/rf64.c b/src/rf64.c
new file mode 100644
index 0000000..123db44
--- /dev/null
+++ b/src/rf64.c
@@ -0,0 +1,894 @@
+/*
+** Copyright (C) 2008-2018 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2009      Uli Franke <cls@nebadje.org>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*
+**	This format documented at:
+**	http://www.sr.se/utveckling/tu/bwf/prog/RF_64v1_4.pdf
+**
+**	But this may be a better reference:
+**	http://www.ebu.ch/CMSimages/en/tec_doc_t3306-2007_tcm6-42570.pdf
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<fcntl.h>
+#include	<string.h>
+#include	<ctype.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+#include	"wavlike.h"
+
+/*------------------------------------------------------------------------------
+** Macros to handle big/little endian issues.
+*/
+#define	RF64_MARKER		MAKE_MARKER ('R', 'F', '6', '4')
+#define	RIFF_MARKER		MAKE_MARKER ('R', 'I', 'F', 'F')
+#define	JUNK_MARKER		MAKE_MARKER ('J', 'U', 'N', 'K')
+#define	FFFF_MARKER		MAKE_MARKER (0xff, 0xff, 0xff, 0xff)
+#define	WAVE_MARKER		MAKE_MARKER ('W', 'A', 'V', 'E')
+#define	ds64_MARKER		MAKE_MARKER ('d', 's', '6', '4')
+#define	fmt_MARKER		MAKE_MARKER ('f', 'm', 't', ' ')
+#define	fact_MARKER		MAKE_MARKER ('f', 'a', 'c', 't')
+#define	data_MARKER		MAKE_MARKER ('d', 'a', 't', 'a')
+
+#define bext_MARKER		MAKE_MARKER ('b', 'e', 'x', 't')
+#define cart_MARKER		MAKE_MARKER ('c', 'a', 'r', 't')
+#define OggS_MARKER		MAKE_MARKER ('O', 'g', 'g', 'S')
+#define wvpk_MARKER		MAKE_MARKER ('w', 'v', 'p', 'k')
+#define LIST_MARKER		MAKE_MARKER ('L', 'I', 'S', 'T')
+
+/*
+** The file size limit in bytes below which we can, if requested, write this
+** file as a RIFF/WAVE file.
+*/
+
+#define RIFF_DOWNGRADE_BYTES	((sf_count_t) 0xffffffff)
+
+/*------------------------------------------------------------------------------
+** Private static functions.
+*/
+
+static int	rf64_read_header (SF_PRIVATE *psf, int *blockalign, int *framesperblock) ;
+static int	rf64_write_header (SF_PRIVATE *psf, int calc_length) ;
+static int	rf64_write_tailer (SF_PRIVATE *psf) ;
+static int	rf64_close (SF_PRIVATE *psf) ;
+static int	rf64_command (SF_PRIVATE *psf, int command, void * UNUSED (data), int datasize) ;
+
+static int rf64_set_chunk (SF_PRIVATE *psf, const SF_CHUNK_INFO * chunk_info) ;
+static SF_CHUNK_ITERATOR * rf64_next_chunk_iterator (SF_PRIVATE *psf, SF_CHUNK_ITERATOR * iterator) ;
+static int rf64_get_chunk_size (SF_PRIVATE *psf, const SF_CHUNK_ITERATOR * iterator, SF_CHUNK_INFO * chunk_info) ;
+static int rf64_get_chunk_data (SF_PRIVATE *psf, const SF_CHUNK_ITERATOR * iterator, SF_CHUNK_INFO * chunk_info) ;
+
+/*------------------------------------------------------------------------------
+** Public function.
+*/
+
+int
+rf64_open (SF_PRIVATE *psf)
+{	WAVLIKE_PRIVATE *wpriv ;
+	int	subformat, error = 0 ;
+	int blockalign, framesperblock ;
+
+	if ((wpriv = calloc (1, sizeof (WAVLIKE_PRIVATE))) == NULL)
+		return SFE_MALLOC_FAILED ;
+	psf->container_data = wpriv ;
+	wpriv->wavex_ambisonic = SF_AMBISONIC_NONE ;
+
+	/* All RF64 files are little endian. */
+	psf->endian = SF_ENDIAN_LITTLE ;
+
+	psf->strings.flags = SF_STR_ALLOW_START | SF_STR_ALLOW_END ;
+
+	if (psf->file.mode == SFM_READ || (psf->file.mode == SFM_RDWR && psf->filelength > 0))
+	{	if ((error = rf64_read_header (psf, &blockalign, &framesperblock)) != 0)
+			return error ;
+
+		psf->next_chunk_iterator = rf64_next_chunk_iterator ;
+		psf->get_chunk_size = rf64_get_chunk_size ;
+		psf->get_chunk_data = rf64_get_chunk_data ;
+		} ;
+
+	if ((psf->sf.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_RF64)
+		return	SFE_BAD_OPEN_FORMAT ;
+
+	subformat = psf->sf.format & SF_FORMAT_SUBMASK ;
+
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	if (psf->is_pipe)
+			return SFE_NO_PIPE_WRITE ;
+
+		psf->blockwidth = psf->bytewidth * psf->sf.channels ;
+
+		if ((error = rf64_write_header (psf, SF_FALSE)))
+			return error ;
+
+		psf->write_header = rf64_write_header ;
+		psf->set_chunk = rf64_set_chunk ;
+		} ;
+
+	psf->container_close = rf64_close ;
+	psf->command = rf64_command ;
+
+	switch (subformat)
+	{	case SF_FORMAT_PCM_U8 :
+		case SF_FORMAT_PCM_16 :
+		case SF_FORMAT_PCM_24 :
+		case SF_FORMAT_PCM_32 :
+					error = pcm_init (psf) ;
+					break ;
+
+		case SF_FORMAT_ULAW :
+					error = ulaw_init (psf) ;
+					break ;
+
+		case SF_FORMAT_ALAW :
+					error = alaw_init (psf) ;
+					break ;
+
+		/* Lite remove start */
+		case SF_FORMAT_FLOAT :
+					error = float32_init (psf) ;
+					break ;
+
+		case SF_FORMAT_DOUBLE :
+					error = double64_init (psf) ;
+					break ;
+
+		/* Lite remove end */
+
+		default : 	return SFE_UNIMPLEMENTED ;
+		} ;
+
+	return error ;
+} /* rf64_open */
+
+/*------------------------------------------------------------------------------
+*/
+enum
+{	HAVE_ds64	= 0x01,
+	HAVE_fmt	= 0x02,
+	HAVE_bext	= 0x04,
+	HAVE_data	= 0x08,
+	HAVE_cart	= 0x10,
+	HAVE_PEAK	= 0x20,
+	HAVE_other	= 0x40
+} ;
+
+#define HAVE_CHUNK(CHUNK)	((parsestage & CHUNK) != 0)
+
+static int
+rf64_read_header (SF_PRIVATE *psf, int *blockalign, int *framesperblock)
+{	WAVLIKE_PRIVATE	*wpriv ;
+	WAV_FMT		*wav_fmt ;
+	sf_count_t riff_size = 0, frame_count = 0, ds64_datalength = 0 ;
+	uint32_t marks [2], marker, chunk_size, parsestage = 0 ;
+	int error, done = 0, format = 0 ;
+
+	if ((wpriv = psf->container_data) == NULL)
+		return SFE_INTERNAL ;
+	wav_fmt = &wpriv->wav_fmt ;
+
+	/* Set position to start of file to begin reading header. */
+	psf_binheader_readf (psf, "pmmm", 0, &marker, marks, marks + 1) ;
+	if (marker != RF64_MARKER || marks [1] != WAVE_MARKER)
+		return SFE_RF64_NOT_RF64 ;
+
+	if (marks [0] == FFFF_MARKER)
+		psf_log_printf (psf, "%M\n  %M\n", RF64_MARKER, WAVE_MARKER) ;
+	else
+		psf_log_printf (psf, "%M : 0x%x (should be 0xFFFFFFFF)\n  %M\n", RF64_MARKER, WAVE_MARKER) ;
+
+	while (!done)
+	{
+		marker = chunk_size = 0 ;
+		psf_binheader_readf (psf, "em4", &marker, &chunk_size) ;
+
+		if (marker == 0)
+		{	sf_count_t pos = psf_ftell (psf) ;
+			psf_log_printf (psf, "Have 0 marker at position %D (0x%x).\n", pos, pos) ;
+			break ;
+			} ;
+
+		psf_store_read_chunk_u32 (&psf->rchunks, marker, psf_ftell (psf), chunk_size) ;
+
+		switch (marker)
+		{	case ds64_MARKER :
+				if (parsestage & HAVE_ds64)
+				{	psf_log_printf (psf, "*** Second 'ds64' chunk?\n") ;
+					break ;
+					} ;
+
+				{	unsigned int table_len, bytesread ;
+
+					/* Read ds64 sizes (3 8-byte words). */
+					bytesread = psf_binheader_readf (psf, "888", &riff_size, &ds64_datalength, &frame_count) ;
+
+					/* Read table length. */
+					bytesread += psf_binheader_readf (psf, "4", &table_len) ;
+					/* Skip table for now. (this was "table_len + 4", why?) */
+					bytesread += psf_binheader_readf (psf, "j", table_len) ;
+
+					if (chunk_size == bytesread)
+						psf_log_printf (psf, "%M : %u\n", marker, chunk_size) ;
+					else if (chunk_size >= bytesread + 4)
+					{	unsigned int next ;
+						psf_binheader_readf (psf, "m", &next) ;
+						if (next == fmt_MARKER)
+						{	psf_log_printf (psf, "%M : %u (should be %u)\n", marker, chunk_size, bytesread) ;
+							psf_binheader_readf (psf, "j", -4) ;
+							}
+						else
+						{	psf_log_printf (psf, "%M : %u\n", marker, chunk_size) ;
+							psf_binheader_readf (psf, "j", chunk_size - bytesread - 4) ;
+							} ;
+						} ;
+
+					if (psf->filelength != riff_size + 8)
+						psf_log_printf (psf, "  Riff size : %D (should be %D)\n", riff_size, psf->filelength - 8) ;
+					else
+						psf_log_printf (psf, "  Riff size : %D\n", riff_size) ;
+
+					psf_log_printf (psf, "  Data size : %D\n", ds64_datalength) ;
+
+					psf_log_printf (psf, "  Frames    : %D\n", frame_count) ;
+					psf_log_printf (psf, "  Table length : %u\n", table_len) ;
+
+					} ;
+				parsestage |= HAVE_ds64 ;
+				break ;
+
+			case fmt_MARKER:
+					psf_log_printf (psf, "%M : %u\n", marker, chunk_size) ;
+					if ((error = wavlike_read_fmt_chunk (psf, chunk_size)) != 0)
+						return error ;
+					format = wav_fmt->format ;
+					parsestage |= HAVE_fmt ;
+					break ;
+
+			case bext_MARKER :
+					if ((error = wavlike_read_bext_chunk (psf, chunk_size)) != 0)
+						return error ;
+					parsestage |= HAVE_bext ;
+					break ;
+
+			case cart_MARKER :
+					if ((error = wavlike_read_cart_chunk (psf, chunk_size)) != 0)
+						return error ;
+					parsestage |= HAVE_cart ;
+					break ;
+
+			case INFO_MARKER :
+			case LIST_MARKER :
+					if ((error = wavlike_subchunk_parse (psf, marker, chunk_size)) != 0)
+						return error ;
+					parsestage |= HAVE_other ;
+					break ;
+
+			case PEAK_MARKER :
+					if ((parsestage & (HAVE_ds64 | HAVE_fmt)) != (HAVE_ds64 | HAVE_fmt))
+						return SFE_RF64_PEAK_B4_FMT ;
+
+					parsestage |= HAVE_PEAK ;
+
+					psf_log_printf (psf, "%M : %u\n", marker, chunk_size) ;
+					if ((error = wavlike_read_peak_chunk (psf, chunk_size)) != 0)
+						return error ;
+					psf->peak_info->peak_loc = ((parsestage & HAVE_data) == 0) ? SF_PEAK_START : SF_PEAK_END ;
+					break ;
+
+			case data_MARKER :
+				/* see wav for more sophisticated parsing -> implement state machine with parsestage */
+
+				if (HAVE_CHUNK (HAVE_ds64))
+				{	if (chunk_size == 0xffffffff)
+						psf_log_printf (psf, "%M : 0x%x\n", marker, chunk_size) ;
+					else
+						psf_log_printf (psf, "%M : 0x%x (should be 0xffffffff\n", marker, chunk_size) ;
+					psf->datalength = ds64_datalength ;
+					}
+				else
+				{	if (chunk_size == 0xffffffff)
+					{	psf_log_printf (psf, "%M : 0x%x\n", marker, chunk_size) ;
+						psf_log_printf (psf, "  *** Data length not specified no 'ds64' chunk.\n") ;
+						}
+					else
+					{	psf_log_printf (psf, "%M : 0x%x\n**** Weird, RF64 file without a 'ds64' chunk and no valid 'data' size.\n", marker, chunk_size) ;
+						psf->datalength = chunk_size ;
+						} ;
+					} ;
+
+				psf->dataoffset = psf_ftell (psf) ;
+
+				if (psf->dataoffset > 0)
+				{	if (chunk_size == 0 && riff_size == 8 && psf->filelength > 44)
+					{	psf_log_printf (psf, "  *** Looks like a WAV file which wasn't closed properly. Fixing it.\n") ;
+						psf->datalength = psf->filelength - psf->dataoffset ;
+						} ;
+
+					/* Only set dataend if there really is data at the end. */
+					if (psf->datalength + psf->dataoffset < psf->filelength)
+						psf->dataend = psf->datalength + psf->dataoffset ;
+
+					if (!psf->sf.seekable || psf->dataoffset < 0)
+						break ;
+
+					/* Seek past data and continue reading header. */
+					psf_fseek (psf, psf->datalength, SEEK_CUR) ;
+
+					if (psf_ftell (psf) != psf->datalength + psf->dataoffset)
+						psf_log_printf (psf, "  *** psf_fseek past end error ***\n") ;
+					} ;
+				break ;
+
+			case JUNK_MARKER :
+			case PAD_MARKER :
+				psf_log_printf (psf, "%M : %d\n", marker, chunk_size) ;
+				psf_binheader_readf (psf, "j", chunk_size) ;
+				break ;
+
+			default :
+					if (chunk_size >= 0xffff0000)
+					{	psf_log_printf (psf, "*** Unknown chunk marker (%X) at position %D with length %u. Exiting parser.\n", marker, psf_ftell (psf) - 8, chunk_size) ;
+						done = SF_TRUE ;
+						break ;
+						} ;
+
+					if (isprint ((marker >> 24) & 0xFF) && isprint ((marker >> 16) & 0xFF)
+						&& isprint ((marker >> 8) & 0xFF) && isprint (marker & 0xFF))
+					{	psf_log_printf (psf, "*** %M : %d (unknown marker)\n", marker, chunk_size) ;
+						psf_binheader_readf (psf, "j", chunk_size) ;
+						break ;
+						} ;
+					if (psf_ftell (psf) & 0x03)
+					{	psf_log_printf (psf, "  Unknown chunk marker at position 0x%x. Resynching.\n", chunk_size - 4) ;
+						psf_binheader_readf (psf, "j", -3) ;
+						break ;
+						} ;
+					psf_log_printf (psf, "*** Unknown chunk marker (0x%X) at position 0x%X. Exiting parser.\n", marker, psf_ftell (psf) - 4) ;
+					done = SF_TRUE ;
+					break ;
+			} ;	/* switch (marker) */
+
+		/* The 'data' chunk, a chunk size of 0xffffffff means that the 'data' chunk size
+		** is actually given by the ds64_datalength field.
+		*/
+		if (marker != data_MARKER && chunk_size >= psf->filelength)
+		{	psf_log_printf (psf, "*** Chunk size %u > file length %D. Exiting parser.\n", chunk_size, psf->filelength) ;
+			break ;
+			} ;
+
+		if (psf_ftell (psf) >= psf->filelength - SIGNED_SIZEOF (marker))
+		{	psf_log_printf (psf, "End\n") ;
+			break ;
+			} ;
+		} ;
+
+	if (psf->dataoffset <= 0)
+		return SFE_RF64_NO_DATA ;
+
+	if (psf->sf.channels < 1)
+		return SFE_CHANNEL_COUNT_ZERO ;
+
+	if (psf->sf.channels > SF_MAX_CHANNELS)
+		return SFE_CHANNEL_COUNT ;
+
+	/* WAVs can be little or big endian */
+	psf->endian = psf->rwf_endian ;
+
+	psf_fseek (psf, psf->dataoffset, SEEK_SET) ;
+
+	if (psf->is_pipe == 0)
+	{	/*
+		** Check for 'wvpk' at the start of the DATA section. Not able to
+		** handle this.
+		*/
+		psf_binheader_readf (psf, "4", &marker) ;
+		if (marker == wvpk_MARKER || marker == OggS_MARKER)
+			return SFE_WAV_WVPK_DATA ;
+		} ;
+
+	/* Seek to start of DATA section. */
+	psf_fseek (psf, psf->dataoffset, SEEK_SET) ;
+
+	if (psf->blockwidth)
+	{	if (psf->filelength - psf->dataoffset < psf->datalength)
+			psf->sf.frames = (psf->filelength - psf->dataoffset) / psf->blockwidth ;
+		else
+			psf->sf.frames = psf->datalength / psf->blockwidth ;
+		} ;
+
+	if (frame_count != psf->sf.frames)
+		psf_log_printf (psf, "*** Calculated frame count %d does not match value from 'ds64' chunk of %d.\n", psf->sf.frames, frame_count) ;
+
+	switch (format)
+	{
+		case WAVE_FORMAT_EXTENSIBLE :
+
+			/* with WAVE_FORMAT_EXTENSIBLE the psf->sf.format field is already set. We just have to set the major to rf64 */
+			psf->sf.format = (psf->sf.format & ~SF_FORMAT_TYPEMASK) | SF_FORMAT_RF64 ;
+
+			if (psf->sf.format == (SF_FORMAT_WAVEX | SF_FORMAT_MS_ADPCM))
+			{	*blockalign = wav_fmt->msadpcm.blockalign ;
+				*framesperblock = wav_fmt->msadpcm.samplesperblock ;
+				} ;
+			break ;
+
+		case WAVE_FORMAT_PCM :
+					psf->sf.format = SF_FORMAT_RF64 | u_bitwidth_to_subformat (psf->bytewidth * 8) ;
+					break ;
+
+		case WAVE_FORMAT_MULAW :
+		case IBM_FORMAT_MULAW :
+					psf->sf.format = (SF_FORMAT_RF64 | SF_FORMAT_ULAW) ;
+					break ;
+
+		case WAVE_FORMAT_ALAW :
+		case IBM_FORMAT_ALAW :
+					psf->sf.format = (SF_FORMAT_RF64 | SF_FORMAT_ALAW) ;
+					break ;
+
+		case WAVE_FORMAT_MS_ADPCM :
+					psf->sf.format = (SF_FORMAT_RF64 | SF_FORMAT_MS_ADPCM) ;
+					*blockalign = wav_fmt->msadpcm.blockalign ;
+					*framesperblock = wav_fmt->msadpcm.samplesperblock ;
+					break ;
+
+		case WAVE_FORMAT_IMA_ADPCM :
+					psf->sf.format = (SF_FORMAT_RF64 | SF_FORMAT_IMA_ADPCM) ;
+					*blockalign = wav_fmt->ima.blockalign ;
+					*framesperblock = wav_fmt->ima.samplesperblock ;
+					break ;
+
+		case WAVE_FORMAT_GSM610 :
+					psf->sf.format = (SF_FORMAT_RF64 | SF_FORMAT_GSM610) ;
+					break ;
+
+		case WAVE_FORMAT_IEEE_FLOAT :
+					psf->sf.format = SF_FORMAT_RF64 ;
+					psf->sf.format |= (psf->bytewidth == 8) ? SF_FORMAT_DOUBLE : SF_FORMAT_FLOAT ;
+					break ;
+
+		case WAVE_FORMAT_G721_ADPCM :
+					psf->sf.format = SF_FORMAT_RF64 | SF_FORMAT_G721_32 ;
+					break ;
+
+		default : return SFE_UNIMPLEMENTED ;
+		} ;
+
+	if (wpriv->fmt_is_broken)
+		wavlike_analyze (psf) ;
+
+	/* Only set the format endian-ness if its non-standard big-endian. */
+	if (psf->endian == SF_ENDIAN_BIG)
+		psf->sf.format |= SF_ENDIAN_BIG ;
+
+	return 0 ;
+} /* rf64_read_header */
+
+/*  known WAVEFORMATEXTENSIBLE GUIDS  */
+static const EXT_SUBFORMAT MSGUID_SUBTYPE_PCM =
+{	0x00000001, 0x0000, 0x0010, {	0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }
+} ;
+
+#if 0
+static const EXT_SUBFORMAT MSGUID_SUBTYPE_MS_ADPCM =
+{	0x00000002, 0x0000, 0x0010, {	0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }
+} ;
+#endif
+
+static const EXT_SUBFORMAT MSGUID_SUBTYPE_IEEE_FLOAT =
+{	0x00000003, 0x0000, 0x0010, {	0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }
+} ;
+
+static const EXT_SUBFORMAT MSGUID_SUBTYPE_ALAW =
+{	0x00000006, 0x0000, 0x0010, {	0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }
+} ;
+
+static const EXT_SUBFORMAT MSGUID_SUBTYPE_MULAW =
+{	0x00000007, 0x0000, 0x0010, {	0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }
+} ;
+
+/*
+** the next two are from
+** http://dream.cs.bath.ac.uk/researchdev/wave-ex/bformat.html
+*/
+static const EXT_SUBFORMAT MSGUID_SUBTYPE_AMBISONIC_B_FORMAT_PCM =
+{	0x00000001, 0x0721, 0x11d3, {	0x86, 0x44, 0xC8, 0xC1, 0xCA, 0x00, 0x00, 0x00 }
+} ;
+
+static const EXT_SUBFORMAT MSGUID_SUBTYPE_AMBISONIC_B_FORMAT_IEEE_FLOAT =
+{	0x00000003, 0x0721, 0x11d3, {	0x86, 0x44, 0xC8, 0xC1, 0xCA, 0x00, 0x00, 0x00 }
+} ;
+
+
+static int
+rf64_write_fmt_chunk (SF_PRIVATE *psf)
+{	WAVLIKE_PRIVATE	*wpriv ;
+	int subformat, fmt_size ;
+
+	if ((wpriv = psf->container_data) == NULL)
+		return SFE_INTERNAL ;
+
+	subformat = psf->sf.format & SF_FORMAT_SUBMASK ;
+
+	/* initial section (same for all, it appears) */
+	switch (subformat)
+	{	case SF_FORMAT_PCM_U8 :
+		case SF_FORMAT_PCM_16 :
+		case SF_FORMAT_PCM_24 :
+		case SF_FORMAT_PCM_32 :
+		case SF_FORMAT_FLOAT :
+		case SF_FORMAT_DOUBLE :
+		case SF_FORMAT_ULAW :
+		case SF_FORMAT_ALAW :
+			fmt_size = 2 + 2 + 4 + 4 + 2 + 2 + 2 + 2 + 4 + 4 + 2 + 2 + 8 ;
+
+			/* fmt : format, channels, samplerate */
+			psf_binheader_writef (psf, "4224", BHW4 (fmt_size), BHW2 (WAVE_FORMAT_EXTENSIBLE), BHW2 (psf->sf.channels), BHW4 (psf->sf.samplerate)) ;
+			/*  fmt : bytespersec */
+			psf_binheader_writef (psf, "4", BHW4 (psf->sf.samplerate * psf->bytewidth * psf->sf.channels)) ;
+			/*  fmt : blockalign, bitwidth */
+			psf_binheader_writef (psf, "22", BHW2 (psf->bytewidth * psf->sf.channels), BHW2 (psf->bytewidth * 8)) ;
+
+			/* cbSize 22 is sizeof (WAVEFORMATEXTENSIBLE) - sizeof (WAVEFORMATEX) */
+			psf_binheader_writef (psf, "2", BHW2 (22)) ;
+
+			/* wValidBitsPerSample, for our use same as bitwidth as we use it fully */
+			psf_binheader_writef (psf, "2", BHW2 (psf->bytewidth * 8)) ;
+
+			/* For an Ambisonic file set the channel mask to zero.
+			** Otherwise use a default based on the channel count.
+			*/
+			if (wpriv->wavex_ambisonic != SF_AMBISONIC_NONE)
+				psf_binheader_writef (psf, "4", BHW4 (0)) ;
+			else if (wpriv->wavex_channelmask != 0)
+				psf_binheader_writef (psf, "4", BHW4 (wpriv->wavex_channelmask)) ;
+			else
+			{	/*
+				** Ok some liberty is taken here to use the most commonly used channel masks
+				** instead of "no mapping". If you really want to use "no mapping" for 8 channels and less
+				** please don't use wavex. (otherwise we'll have to create a new SF_COMMAND)
+				*/
+				switch (psf->sf.channels)
+				{	case 1 :	/* center channel mono */
+						psf_binheader_writef (psf, "4", BHW4 (0x4)) ;
+						break ;
+
+					case 2 :	/* front left and right */
+						psf_binheader_writef (psf, "4", BHW4 (0x1 | 0x2)) ;
+						break ;
+
+					case 4 :	/* Quad */
+						psf_binheader_writef (psf, "4", BHW4 (0x1 | 0x2 | 0x10 | 0x20)) ;
+						break ;
+
+					case 6 :	/* 5.1 */
+						psf_binheader_writef (psf, "4", BHW4 (0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20)) ;
+						break ;
+
+					case 8 :	/* 7.1 */
+						psf_binheader_writef (psf, "4", BHW4 (0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20 | 0x40 | 0x80)) ;
+						break ;
+
+					default :	/* 0 when in doubt , use direct out, ie NO mapping*/
+						psf_binheader_writef (psf, "4", BHW4 (0x0)) ;
+						break ;
+					} ;
+				} ;
+			break ;
+
+		case SF_FORMAT_MS_ADPCM : /* Todo, GUID exists might have different header as per wav_write_header */
+		default :
+			return SFE_UNIMPLEMENTED ;
+		} ;
+
+	/* GUID section, different for each */
+
+	switch (subformat)
+	{	case SF_FORMAT_PCM_U8 :
+		case SF_FORMAT_PCM_16 :
+		case SF_FORMAT_PCM_24 :
+		case SF_FORMAT_PCM_32 :
+			wavlike_write_guid (psf, wpriv->wavex_ambisonic == SF_AMBISONIC_NONE ?
+						&MSGUID_SUBTYPE_PCM : &MSGUID_SUBTYPE_AMBISONIC_B_FORMAT_PCM) ;
+			break ;
+
+		case SF_FORMAT_FLOAT :
+		case SF_FORMAT_DOUBLE :
+			wavlike_write_guid (psf, wpriv->wavex_ambisonic == SF_AMBISONIC_NONE ?
+						&MSGUID_SUBTYPE_IEEE_FLOAT : &MSGUID_SUBTYPE_AMBISONIC_B_FORMAT_IEEE_FLOAT) ;
+			break ;
+
+		case SF_FORMAT_ULAW :
+			wavlike_write_guid (psf, &MSGUID_SUBTYPE_MULAW) ;
+			break ;
+
+		case SF_FORMAT_ALAW :
+			wavlike_write_guid (psf, &MSGUID_SUBTYPE_ALAW) ;
+			break ;
+
+		default : return SFE_UNIMPLEMENTED ;
+		} ;
+
+	return 0 ;
+} /* rf64_write_fmt_chunk */
+
+
+static int
+rf64_write_header (SF_PRIVATE *psf, int calc_length)
+{	sf_count_t	current, pad_size ;
+	int 		error = 0, has_data = SF_FALSE, add_fact_chunk = 0 ;
+	WAVLIKE_PRIVATE	*wpriv ;
+
+	if ((wpriv = psf->container_data) == NULL)
+		return SFE_INTERNAL ;
+
+	current = psf_ftell (psf) ;
+
+	if (psf->dataoffset > 0 && current > psf->dataoffset)
+		has_data = SF_TRUE ;
+
+	if (calc_length)
+	{	psf->filelength = psf_get_filelen (psf) ;
+		psf->datalength = psf->filelength - psf->dataoffset ;
+
+		if (psf->dataend)
+			psf->datalength -= psf->filelength - psf->dataend ;
+
+		if (psf->bytewidth > 0)
+			psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ;
+		} ;
+
+	/* Reset the current header length to zero. */
+	psf->header.ptr [0] = 0 ;
+	psf->header.indx = 0 ;
+	psf_fseek (psf, 0, SEEK_SET) ;
+
+	if (wpriv->rf64_downgrade && psf->filelength < RIFF_DOWNGRADE_BYTES)
+	{	psf_binheader_writef (psf, "etm8m", BHWm (RIFF_MARKER), BHW8 ((psf->filelength < 8) ? 8 : psf->filelength - 8), BHWm (WAVE_MARKER)) ;
+		psf_binheader_writef (psf, "m4z", BHWm (JUNK_MARKER), BHW4 (24), BHWz (24)) ;
+		add_fact_chunk = 1 ;
+		}
+	else
+	{	psf_binheader_writef (psf, "em4m", BHWm (RF64_MARKER), BHW4 (0xffffffff), BHWm (WAVE_MARKER)) ;
+		/* Currently no table. */
+		psf_binheader_writef (psf, "m48884", BHWm (ds64_MARKER), BHW4 (28), BHW8 (psf->filelength - 8), BHW8 (psf->datalength), BHW8 (psf->sf.frames), BHW4 (0)) ;
+		} ;
+
+	/* WAVE and 'fmt ' markers. */
+	psf_binheader_writef (psf, "m", BHWm (fmt_MARKER)) ;
+
+	/* Write the 'fmt ' chunk. */
+	switch (psf->sf.format & SF_FORMAT_TYPEMASK)
+	{	case SF_FORMAT_WAV :
+				psf_log_printf (psf, "ooops SF_FORMAT_WAV\n") ;
+				return SFE_UNIMPLEMENTED ;
+				break ;
+
+		case SF_FORMAT_WAVEX :
+		case SF_FORMAT_RF64 :
+				if ((error = rf64_write_fmt_chunk (psf)) != 0)
+					return error ;
+				if (add_fact_chunk)
+					psf_binheader_writef (psf, "tm48", BHWm (fact_MARKER), BHW4 (4), BHW8 (psf->sf.frames)) ;
+				break ;
+
+		default :
+				return SFE_UNIMPLEMENTED ;
+		} ;
+
+	if (psf->broadcast_16k != NULL)
+		wavlike_write_bext_chunk (psf) ;
+
+	if (psf->cart_16k != NULL)
+		wavlike_write_cart_chunk (psf) ;
+
+	/* The LIST/INFO chunk. */
+	if (psf->strings.flags & SF_STR_LOCATE_START)
+		wavlike_write_strings (psf, SF_STR_LOCATE_START) ;
+
+	if (psf->peak_info != NULL && psf->peak_info->peak_loc == SF_PEAK_START)
+		wavlike_write_peak_chunk (psf) ;
+
+	/* Write custom headers. */
+	if (psf->wchunks.used > 0)
+		wavlike_write_custom_chunks (psf) ;
+
+#if 0
+	if (psf->instrument != NULL)
+	{	int		tmp ;
+		double	dtune = (double) (0x40000000) / 25.0 ;
+
+		psf_binheader_writef (psf, "m4", BHWm (smpl_MARKER), BHW4 (9 * 4 + psf->instrument->loop_count * 6 * 4)) ;
+		psf_binheader_writef (psf, "44", BHW4 (0), BHW4 (0)) ; /* Manufacturer zero is everyone */
+		tmp = (int) (1.0e9 / psf->sf.samplerate) ; /* Sample period in nano seconds */
+		psf_binheader_writef (psf, "44", BHW4 (tmp), BHW4 (psf->instrument->basenote)) ;
+		tmp = (unsigned int) (psf->instrument->detune * dtune + 0.5) ;
+		psf_binheader_writef (psf, "4", BHW4 (tmp)) ;
+		psf_binheader_writef (psf, "44", BHW4 (0), BHW4 (0)) ; /* SMTPE format */
+		psf_binheader_writef (psf, "44", BHW4 (psf->instrument->loop_count), BHW4 (0)) ;
+
+		for (tmp = 0 ; tmp < psf->instrument->loop_count ; tmp++)
+		{	int type ;
+
+			type = psf->instrument->loops [tmp].mode ;
+			type = (type == SF_LOOP_FORWARD ? 0 : type == SF_LOOP_BACKWARD ? 2 : type == SF_LOOP_ALTERNATING ? 1 : 32) ;
+
+			psf_binheader_writef (psf, "44", BHW4 (tmp), BHW4 (type)) ;
+			psf_binheader_writef (psf, "44", BHW4 (psf->instrument->loops [tmp].start), BHW4 (psf->instrument->loops [tmp].end)) ;
+			psf_binheader_writef (psf, "44", BHW4 (0), BHW4 (psf->instrument->loops [tmp].count)) ;
+			} ;
+		} ;
+
+#endif
+
+	/* Padding may be needed if string data sizes change. */
+	pad_size = psf->dataoffset - 16 - psf->header.indx ;
+	if (pad_size >= 0)
+		psf_binheader_writef (psf, "m4z", BHWm (PAD_MARKER), BHW4 ((unsigned int) pad_size), BHWz (pad_size)) ;
+
+	if (wpriv->rf64_downgrade && (psf->filelength < RIFF_DOWNGRADE_BYTES))
+		psf_binheader_writef (psf, "tm8", BHWm (data_MARKER), BHW8 (psf->datalength)) ;
+	else
+		psf_binheader_writef (psf, "m4", BHWm (data_MARKER), BHW4 (0xffffffff)) ;
+
+	psf_fwrite (psf->header.ptr, psf->header.indx, 1, psf) ;
+	if (psf->error)
+		return psf->error ;
+
+	if (has_data && psf->dataoffset != psf->header.indx)
+	{	psf_log_printf (psf, "Oooops : has_data && psf->dataoffset != psf->header.indx\n") ;
+		return psf->error = SFE_INTERNAL ;
+		} ;
+
+	psf->dataoffset = psf->header.indx ;
+
+	if (!has_data)
+		psf_fseek (psf, psf->dataoffset, SEEK_SET) ;
+	else if (current > 0)
+		psf_fseek (psf, current, SEEK_SET) ;
+
+	return psf->error ;
+} /* rf64_write_header */
+
+static int
+rf64_write_tailer (SF_PRIVATE *psf)
+{
+	/* Reset the current header buffer length to zero. */
+	psf->header.ptr [0] = 0 ;
+	psf->header.indx = 0 ;
+
+	if (psf->bytewidth > 0 && psf->sf.seekable == SF_TRUE)
+	{	psf->datalength = psf->sf.frames * psf->bytewidth * psf->sf.channels ;
+		psf->dataend = psf->dataoffset + psf->datalength ;
+		} ;
+
+	if (psf->dataend > 0)
+		psf_fseek (psf, psf->dataend, SEEK_SET) ;
+	else
+		psf->dataend = psf_fseek (psf, 0, SEEK_END) ;
+
+	if (psf->dataend & 1)
+		psf_binheader_writef (psf, "z", BHWz (1)) ;
+
+	if (psf->strings.flags & SF_STR_LOCATE_END)
+		wavlike_write_strings (psf, SF_STR_LOCATE_END) ;
+
+	/* Write the tailer. */
+	if (psf->header.indx > 0)
+		psf_fwrite (psf->header.ptr, psf->header.indx, 1, psf) ;
+
+	return 0 ;
+} /* rf64_write_tailer */
+
+static int
+rf64_close (SF_PRIVATE *psf)
+{
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	rf64_write_tailer (psf) ;
+		rf64_write_header (psf, SF_TRUE) ;
+		} ;
+
+	return 0 ;
+} /* rf64_close */
+
+static int
+rf64_command (SF_PRIVATE *psf, int command, void * UNUSED (data), int datasize)
+{	WAVLIKE_PRIVATE	*wpriv ;
+
+	if ((wpriv = psf->container_data) == NULL)
+		return SFE_INTERNAL ;
+
+	switch (command)
+	{	case SFC_WAVEX_SET_AMBISONIC :
+			if ((SF_CONTAINER (psf->sf.format)) == SF_FORMAT_WAVEX)
+			{	if (datasize == SF_AMBISONIC_NONE)
+					wpriv->wavex_ambisonic = SF_AMBISONIC_NONE ;
+				else if (datasize == SF_AMBISONIC_B_FORMAT)
+					wpriv->wavex_ambisonic = SF_AMBISONIC_B_FORMAT ;
+				else
+					return 0 ;
+				} ;
+			return wpriv->wavex_ambisonic ;
+
+		case SFC_WAVEX_GET_AMBISONIC :
+			return wpriv->wavex_ambisonic ;
+
+		case SFC_SET_CHANNEL_MAP_INFO :
+			wpriv->wavex_channelmask = wavlike_gen_channel_mask (psf->channel_map, psf->sf.channels) ;
+			return (wpriv->wavex_channelmask != 0) ;
+
+		case SFC_RF64_AUTO_DOWNGRADE :
+			if (psf->have_written == 0)
+				wpriv->rf64_downgrade = datasize ? SF_TRUE : SF_FALSE ;
+			return wpriv->rf64_downgrade ;
+
+		default :
+			break ;
+	} ;
+
+	return 0 ;
+} /* rf64_command */
+
+static int
+rf64_set_chunk (SF_PRIVATE *psf, const SF_CHUNK_INFO * chunk_info)
+{	return psf_save_write_chunk (&psf->wchunks, chunk_info) ;
+} /* rf64_set_chunk */
+
+static SF_CHUNK_ITERATOR *
+rf64_next_chunk_iterator (SF_PRIVATE *psf, SF_CHUNK_ITERATOR * iterator)
+{	return psf_next_chunk_iterator (&psf->rchunks, iterator) ;
+} /* rf64_next_chunk_iterator */
+
+static int
+rf64_get_chunk_size (SF_PRIVATE *psf, const SF_CHUNK_ITERATOR * iterator, SF_CHUNK_INFO * chunk_info)
+{	int indx ;
+
+	if ((indx = psf_find_read_chunk_iterator (&psf->rchunks, iterator)) < 0)
+		return SFE_UNKNOWN_CHUNK ;
+
+	chunk_info->datalen = psf->rchunks.chunks [indx].len ;
+
+	return SFE_NO_ERROR ;
+} /* rf64_get_chunk_size */
+
+static int
+rf64_get_chunk_data (SF_PRIVATE *psf, const SF_CHUNK_ITERATOR * iterator, SF_CHUNK_INFO * chunk_info)
+{	int indx ;
+	sf_count_t pos ;
+
+	if ((indx = psf_find_read_chunk_iterator (&psf->rchunks, iterator)) < 0)
+		return SFE_UNKNOWN_CHUNK ;
+
+	if (chunk_info->data == NULL)
+		return SFE_BAD_CHUNK_DATA_PTR ;
+
+	chunk_info->id_size = psf->rchunks.chunks [indx].id_size ;
+	memcpy (chunk_info->id, psf->rchunks.chunks [indx].id, sizeof (chunk_info->id) / sizeof (*chunk_info->id)) ;
+
+	pos = psf_ftell (psf) ;
+	psf_fseek (psf, psf->rchunks.chunks [indx].offset, SEEK_SET) ;
+	psf_fread (chunk_info->data, SF_MIN (chunk_info->datalen, psf->rchunks.chunks [indx].len), 1, psf) ;
+	psf_fseek (psf, pos, SEEK_SET) ;
+
+	return SFE_NO_ERROR ;
+} /* rf64_get_chunk_data */
diff --git a/src/rx2.c b/src/rx2.c
new file mode 100644
index 0000000..0a73048
--- /dev/null
+++ b/src/rx2.c
@@ -0,0 +1,318 @@
+/*
+** Copyright (C) 2001-2012 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<string.h>
+#include	<ctype.h>
+#include	<stdarg.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+#if (ENABLE_EXPERIMENTAL_CODE == 0)
+
+int
+rx2_open	(SF_PRIVATE *psf)
+{	if (psf)
+		return SFE_UNIMPLEMENTED ;
+	return 0 ;
+} /* rx2_open */
+
+#else
+
+/*------------------------------------------------------------------------------
+ * Macros to handle big/little endian issues.
+*/
+
+#define	CAT_MARKER	(MAKE_MARKER ('C', 'A', 'T', ' '))
+#define	GLOB_MARKER (MAKE_MARKER ('G', 'L', 'O', 'B'))
+
+#define	RECY_MARKER (MAKE_MARKER ('R', 'E', 'C', 'Y'))
+
+#define	SLCL_MARKER (MAKE_MARKER ('S', 'L', 'C', 'L'))
+#define	SLCE_MARKER (MAKE_MARKER ('S', 'L', 'C', 'E'))
+
+#define	DEVL_MARKER	(MAKE_MARKER ('D', 'E', 'V', 'L'))
+#define	TRSH_MARKER	(MAKE_MARKER ('T', 'R', 'S', 'H'))
+
+#define	EQ_MARKER	(MAKE_MARKER ('E', 'Q', ' ', ' '))
+#define	COMP_MARKER (MAKE_MARKER ('C', 'O', 'M', 'P'))
+
+#define	SINF_MARKER (MAKE_MARKER ('S', 'I', 'N', 'F'))
+#define	SDAT_MARKER (MAKE_MARKER ('S', 'D', 'A', 'T'))
+
+/*------------------------------------------------------------------------------
+ * Typedefs for file chunks.
+*/
+
+
+/*------------------------------------------------------------------------------
+ * Private static functions.
+*/
+static int	rx2_close	(SF_PRIVATE *psf) ;
+
+/*------------------------------------------------------------------------------
+** Public functions.
+*/
+
+int
+rx2_open	(SF_PRIVATE *psf)
+{	static const char *marker_type [4] =
+	{	"Original Enabled", "Enabled Hidden",
+		"Additional/PencilTool", "Disabled"
+		} ;
+
+	BUF_UNION	ubuf ;
+	int error, marker, length, glob_offset, slce_count, frames ;
+	int sdat_length = 0, slce_total = 0 ;
+	int n_channels ;
+
+
+	/* So far only doing read. */
+
+	psf_binheader_readf (psf, "Epm4", 0, &marker, &length) ;
+
+	if (marker != CAT_MARKER)
+	{	psf_log_printf (psf, "length : %d\n", length) ;
+		return -1000 ;
+		} ;
+
+	if (length != psf->filelength - 8)
+		psf_log_printf (psf, "%M : %d (should be %d)\n", marker, length, psf->filelength - 8) ;
+	else
+		psf_log_printf (psf, "%M : %d\n", marker, length) ;
+
+	/* 'REX2' marker */
+	psf_binheader_readf (psf, "m", &marker) ;
+	psf_log_printf (psf, "%M", marker) ;
+
+	/* 'HEAD' marker */
+	psf_binheader_readf (psf, "m", &marker) ;
+	psf_log_printf (psf, "%M\n", marker) ;
+
+	/* Grab 'GLOB' offset. */
+	psf_binheader_readf (psf, "E4", &glob_offset) ;
+	glob_offset += 0x14 ;	/* Add the current file offset. */
+
+	/* Jump to offset 0x30 */
+	psf_binheader_readf (psf, "p", 0x30) ;
+
+	/* Get name length */
+	length = 0 ;
+	psf_binheader_readf (psf, "1", &length) ;
+	if (length >= SIGNED_SIZEOF (ubuf.cbuf))
+	{	psf_log_printf (psf, "  Text : %d *** Error : Too sf_count_t!\n") ;
+		return -1001 ;
+		}
+
+	memset (ubuf.cbuf, 0, sizeof (ubuf.cbuf)) ;
+	psf_binheader_readf (psf, "b", ubuf.cbuf, length) ;
+	psf_log_printf (psf, " Text : \"%s\"\n", ubuf.cbuf) ;
+
+	/* Jump to GLOB offset position. */
+	if (glob_offset & 1)
+		glob_offset ++ ;
+
+	psf_binheader_readf (psf, "p", glob_offset) ;
+
+	slce_count = 0 ;
+	/* GLOB */
+	while (1)
+	{	psf_binheader_readf (psf, "m", &marker) ;
+
+		if (marker != SLCE_MARKER && slce_count > 0)
+		{	psf_log_printf (psf, "   SLCE count : %d\n", slce_count) ;
+			slce_count = 0 ;
+			}
+		switch (marker)
+		{	case GLOB_MARKER:
+					psf_binheader_readf (psf, "E4", &length) ;
+					psf_log_printf (psf, " %M : %d\n", marker, length) ;
+					psf_binheader_readf (psf, "j", length) ;
+					break ;
+
+			case RECY_MARKER:
+					psf_binheader_readf (psf, "E4", &length) ;
+					psf_log_printf (psf, " %M : %d\n", marker, length) ;
+					psf_binheader_readf (psf, "j", (length+1) & 0xFFFFFFFE) ; /* ?????? */
+					break ;
+
+			case CAT_MARKER:
+					psf_binheader_readf (psf, "E4", &length) ;
+					psf_log_printf (psf, " %M : %d\n", marker, length) ;
+					/*-psf_binheader_readf (psf, "j", length) ;-*/
+					break ;
+
+			case DEVL_MARKER:
+					psf_binheader_readf (psf, "mE4", &marker, &length) ;
+					psf_log_printf (psf, "  DEVL%M : %d\n", marker, length) ;
+					if (length & 1)
+						length ++ ;
+					psf_binheader_readf (psf, "j", length) ;
+					break ;
+
+			case EQ_MARKER:
+			case COMP_MARKER:
+					psf_binheader_readf (psf, "E4", &length) ;
+					psf_log_printf (psf, "   %M : %d\n", marker, length) ;
+					/* This is weird!!!! why make this (length - 1) */
+					if (length & 1)
+						length ++ ;
+					psf_binheader_readf (psf, "j", length) ;
+					break ;
+
+			case SLCL_MARKER:
+					psf_log_printf (psf, "  %M\n    (Offset, Next Offset, Type)\n", marker) ;
+					slce_count = 0 ;
+					break ;
+
+			case SLCE_MARKER:
+					{	int len [4], indx ;
+
+						psf_binheader_readf (psf, "E4444", &len [0], &len [1], &len [2], &len [3]) ;
+
+						indx = ((len [3] & 0x0000FFFF) >> 8) & 3 ;
+
+						if (len [2] == 1)
+						{	if (indx != 1)
+								indx = 3 ;	/* 2 cases, where next slice offset = 1 -> disabled & enabled/hidden */
+
+							psf_log_printf (psf, "   %M : (%6d, ?: 0x%X, %s)\n", marker, len [1], (len [3] & 0xFFFF0000) >> 16, marker_type [indx]) ;
+							}
+						else
+						{	slce_total += len [2] ;
+
+							psf_log_printf (psf, "   %M : (%6d, SLCE_next_ofs:%d, ?: 0x%X, %s)\n", marker, len [1], len [2], (len [3] & 0xFFFF0000) >> 16, marker_type [indx]) ;
+							} ;
+
+						slce_count ++ ;
+						} ;
+					break ;
+
+			case SINF_MARKER:
+					psf_binheader_readf (psf, "E4", &length) ;
+					psf_log_printf (psf, " %M : %d\n", marker, length) ;
+
+					psf_binheader_readf (psf, "E2", &n_channels) ;
+					n_channels = (n_channels & 0x0000FF00) >> 8 ;
+					psf_log_printf (psf, "  Channels    : %d\n", n_channels) ;
+
+					psf_binheader_readf (psf, "E44", &psf->sf.samplerate, &frames) ;
+					psf->sf.frames = frames ;
+					psf_log_printf (psf, "  Sample Rate : %d\n", psf->sf.samplerate) ;
+					psf_log_printf (psf, "  Frames      : %D\n", psf->sf.frames) ;
+
+					psf_binheader_readf (psf, "E4", &length) ;
+					psf_log_printf (psf, "  ??????????? : %d\n", length) ;
+
+					psf_binheader_readf (psf, "E4", &length) ;
+					psf_log_printf (psf, "  ??????????? : %d\n", length) ;
+					break ;
+
+			case SDAT_MARKER:
+					psf_binheader_readf (psf, "E4", &length) ;
+
+				sdat_length = length ;
+
+					/* Get the current offset. */
+					psf->dataoffset = psf_binheader_readf (psf, NULL) ;
+
+					if (psf->dataoffset + length != psf->filelength)
+						psf_log_printf (psf, " %M : %d (should be %d)\n", marker, length, psf->dataoffset + psf->filelength) ;
+					else
+						psf_log_printf (psf, " %M : %d\n", marker, length) ;
+					break ;
+
+			default :
+					psf_log_printf (psf, "Unknown marker : 0x%X %M", marker, marker) ;
+					return -1003 ;
+					break ;
+			} ;
+
+		/* SDAT always last marker in file. */
+		if (marker == SDAT_MARKER)
+			break ;
+		} ;
+
+	puts (psf->parselog.buf) ;
+	puts ("-----------------------------------") ;
+
+	printf ("SDAT length  : %d\n", sdat_length) ;
+	printf ("SLCE count   : %d\n", slce_count) ;
+
+	/* Hack for zero slice count. */
+	if (slce_count == 0 && slce_total == 1)
+		slce_total = frames ;
+
+	printf ("SLCE samples : %d\n", slce_total) ;
+
+	/* Two bytes per sample. */
+	printf ("Comp Ratio   : %f:1\n", (2.0 * slce_total * n_channels) / sdat_length) ;
+
+	puts (" ") ;
+
+	psf->parselog.buf [0] = 0 ;
+
+	/* OK, have the header although not too sure what it all means. */
+
+	psf->endian = SF_ENDIAN_BIG ;
+
+	psf->datalength = psf->filelength - psf->dataoffset ;
+
+ 	if (psf_fseek (psf, psf->dataoffset, SEEK_SET))
+		return SFE_BAD_SEEK ;
+
+	psf->sf.format = (SF_FORMAT_REX2 | SF_FORMAT_DWVW_12) ;
+
+	psf->sf.channels	= 1 ;
+	psf->bytewidth		= 2 ;
+	psf->blockwidth		= psf->sf.channels * psf->bytewidth ;
+
+	if ((error = dwvw_init (psf, 16)))
+		return error ;
+
+	psf->container_close = rx2_close ;
+
+	if (! psf->sf.frames && psf->blockwidth)
+		psf->sf.frames = psf->datalength / psf->blockwidth ;
+
+	/* All done. */
+
+	return 0 ;
+} /* rx2_open */
+
+/*------------------------------------------------------------------------------
+*/
+
+static int
+rx2_close	(SF_PRIVATE *psf)
+{
+	if (psf->file.mode == SFM_WRITE)
+	{	/*  Now we know for certain the length of the file we can re-write
+		**	correct values for the FORM, 8SVX and BODY chunks.
+		*/
+
+		} ;
+
+	return 0 ;
+} /* rx2_close */
+
+#endif
diff --git a/src/sd2.c b/src/sd2.c
new file mode 100644
index 0000000..f5ef9de
--- /dev/null
+++ b/src/sd2.c
@@ -0,0 +1,619 @@
+/*
+** Copyright (C) 2001-2020 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2004 Paavo Jumppanen
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*
+** The sd2 support implemented in this file was partially sponsored
+** (financially) by Paavo Jumppanen.
+*/
+
+/*
+** Documentation on the Mac resource fork was obtained here :
+** http://developer.apple.com/documentation/mac/MoreToolbox/MoreToolbox-99.html
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<stdlib.h>
+#include	<string.h>
+#include	<ctype.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+/*------------------------------------------------------------------------------
+ * Markers.
+*/
+
+#define	Sd2f_MARKER			MAKE_MARKER ('S', 'd', '2', 'f')
+#define	Sd2a_MARKER			MAKE_MARKER ('S', 'd', '2', 'a')
+#define	ALCH_MARKER			MAKE_MARKER ('A', 'L', 'C', 'H')
+#define lsf1_MARKER			MAKE_MARKER ('l', 's', 'f', '1')
+
+#define STR_MARKER			MAKE_MARKER ('S', 'T', 'R', ' ')
+#define sdML_MARKER			MAKE_MARKER ('s', 'd', 'M', 'L')
+
+enum
+{	RSRC_STR = 111,
+	RSRC_BIN
+} ;
+
+typedef struct
+{	unsigned char * rsrc_data ;
+	int rsrc_len ;
+	int need_to_free_rsrc_data ;
+
+	int data_offset, data_length ;
+	int map_offset, map_length ;
+
+	int type_count, type_offset ;
+	int item_offset ;
+
+	int str_index, str_count ;
+
+	int string_offset ;
+
+	/* All the above just to get these three. */
+	int sample_size, sample_rate, channels ;
+} SD2_RSRC ;
+
+typedef struct
+{	int type ;
+	int id ;
+	char name [32] ;
+	char value [32] ;
+	int value_len ;
+} STR_RSRC ;
+
+/*------------------------------------------------------------------------------
+ * Private static functions.
+*/
+
+static int sd2_close	(SF_PRIVATE *psf) ;
+
+static int sd2_parse_rsrc_fork (SF_PRIVATE *psf) ;
+static int parse_str_rsrc (SF_PRIVATE *psf, SD2_RSRC * rsrc) ;
+
+static int sd2_write_rsrc_fork (SF_PRIVATE *psf, int calc_length) ;
+
+/*------------------------------------------------------------------------------
+** Public functions.
+*/
+
+int
+sd2_open (SF_PRIVATE *psf)
+{	int subformat, error = 0, valid ;
+
+	/* SD2 is always big endian. */
+	psf->endian = SF_ENDIAN_BIG ;
+
+	if (psf->file.mode == SFM_READ || (psf->file.mode == SFM_RDWR && psf->rsrclength > 0))
+	{	psf_use_rsrc (psf, SF_TRUE) ;
+		valid = psf_file_valid (psf) ;
+		psf_use_rsrc (psf, SF_FALSE) ;
+		if (! valid)
+		{	psf_log_printf (psf, "sd2_open : psf->rsrc.filedes < 0\n") ;
+			return SFE_SD2_BAD_RSRC ;
+			} ;
+
+		error = sd2_parse_rsrc_fork (psf) ;
+
+		if (error)
+			goto error_cleanup ;
+		} ;
+
+	if ((SF_CONTAINER (psf->sf.format)) != SF_FORMAT_SD2)
+	{	error = SFE_BAD_OPEN_FORMAT ;
+		goto error_cleanup ;
+		} ;
+
+	subformat = SF_CODEC (psf->sf.format) ;
+	psf->dataoffset = 0 ;
+
+	/* Only open and write the resource in RDWR mode is its current length is zero. */
+	if (psf->file.mode == SFM_WRITE || (psf->file.mode == SFM_RDWR && psf->rsrclength == 0))
+	{	psf->rsrc.mode = psf->file.mode ;
+		psf_open_rsrc (psf) ;
+
+		error = sd2_write_rsrc_fork (psf, SF_FALSE) ;
+
+		if (error)
+			goto error_cleanup ;
+
+		/* Not needed. */
+		psf->write_header = NULL ;
+		} ;
+
+	psf->container_close = sd2_close ;
+
+	psf->blockwidth = psf->bytewidth * psf->sf.channels ;
+
+	switch (subformat)
+	{	case SF_FORMAT_PCM_S8 :	/* 8-bit linear PCM. */
+		case SF_FORMAT_PCM_16 :	/* 16-bit linear PCM. */
+		case SF_FORMAT_PCM_24 :	/* 24-bit linear PCM */
+		case SF_FORMAT_PCM_32 :	/* 32-bit linear PCM */
+				error = pcm_init (psf) ;
+				break ;
+
+		default :
+				error = SFE_UNIMPLEMENTED ;
+				break ;
+		} ;
+
+	psf_fseek (psf, psf->dataoffset, SEEK_SET) ;
+
+error_cleanup:
+
+	/* Close the resource fork regardless. We won't need it again. */
+	psf_close_rsrc (psf) ;
+
+	return error ;
+} /* sd2_open */
+
+/*------------------------------------------------------------------------------
+*/
+
+static int
+sd2_close	(SF_PRIVATE *psf)
+{
+	if (psf->file.mode == SFM_WRITE)
+	{	/*  Now we know for certain the audio_length of the file we can re-write
+		**	correct values for the FORM, 8SVX and BODY chunks.
+		*/
+
+		} ;
+
+	return 0 ;
+} /* sd2_close */
+
+/*------------------------------------------------------------------------------
+*/
+
+static int
+sd2_write_rsrc_fork (SF_PRIVATE *psf, int UNUSED (calc_length))
+{	SD2_RSRC rsrc ;
+	STR_RSRC str_rsrc [] =
+	{	{ RSRC_STR, 1000, "_sample-size", "", 0 },
+		{ RSRC_STR, 1001, "_sample-rate", "", 0 },
+		{ RSRC_STR, 1002, "_channels", "", 0 },
+		{ RSRC_BIN, 1000, "_Markers", "", 8 }
+		} ;
+
+	int k, str_offset, data_offset, next_str ;
+
+	psf_use_rsrc (psf, SF_TRUE) ;
+
+	memset (&rsrc, 0, sizeof (rsrc)) ;
+
+	rsrc.sample_rate = psf->sf.samplerate ;
+	rsrc.sample_size = psf->bytewidth ;
+	rsrc.channels = psf->sf.channels ;
+
+	rsrc.rsrc_data = psf->header.ptr ;
+	rsrc.rsrc_len = psf->header.len ;
+	memset (rsrc.rsrc_data, 0xea, rsrc.rsrc_len) ;
+
+	snprintf (str_rsrc [0].value, sizeof (str_rsrc [0].value), "_%d", rsrc.sample_size) ;
+	snprintf (str_rsrc [1].value, sizeof (str_rsrc [1].value), "_%d.000000", rsrc.sample_rate) ;
+	snprintf (str_rsrc [2].value, sizeof (str_rsrc [2].value), "_%d", rsrc.channels) ;
+
+	for (k = 0 ; k < ARRAY_LEN (str_rsrc) ; k++)
+	{	if (str_rsrc [k].value_len == 0)
+		{	str_rsrc [k].value_len = strlen (str_rsrc [k].value) ;
+			str_rsrc [k].value [0] = str_rsrc [k].value_len - 1 ;
+			} ;
+
+		/* Turn name string into a pascal string. */
+		str_rsrc [k].name [0] = strlen (str_rsrc [k].name) - 1 ;
+		} ;
+
+	rsrc.data_offset = 0x100 ;
+
+	/*
+	** Calculate data length :
+	**		length of strings, plus the length of the sdML chunk.
+	*/
+	rsrc.data_length = 0 ;
+	for (k = 0 ; k < ARRAY_LEN (str_rsrc) ; k++)
+		rsrc.data_length += str_rsrc [k].value_len + 4 ;
+
+	rsrc.map_offset = rsrc.data_offset + rsrc.data_length ;
+
+	/* Very start of resource fork. */
+	psf_binheader_writef (psf, "E444", BHW4 (rsrc.data_offset), BHW4 (rsrc.map_offset), BHW4 (rsrc.data_length)) ;
+
+	psf_binheader_writef (psf, "Eop", BHWo (0x30), BHWp (psf->file.name)) ;
+	psf_binheader_writef (psf, "Eo2mm", BHWo (0x50), BHW2 (0), BHWm (Sd2f_MARKER), BHWm (lsf1_MARKER)) ;
+
+	/* Very start of resource map. */
+	psf_binheader_writef (psf, "E4444", BHW4 (rsrc.map_offset), BHW4 (rsrc.data_offset), BHW4 (rsrc.map_offset), BHW4 (rsrc.data_length)) ;
+
+	/* These I don't currently understand. */
+	if (1)
+	{	psf_binheader_writef (psf, "Eo1422", BHWo (rsrc.map_offset + 16), BHW1 (1), BHW4 (0x12345678), BHW2 (0xabcd), BHW2 (0)) ;
+		} ;
+
+	/* Resource type offset. */
+	rsrc.type_offset = rsrc.map_offset + 30 ;
+	psf_binheader_writef (psf, "Eo2", BHWo (rsrc.map_offset + 24), BHW2 (rsrc.type_offset - rsrc.map_offset - 2)) ;
+
+	/* Type index max. */
+	rsrc.type_count = 2 ;
+	psf_binheader_writef (psf, "Eo2", BHWo (rsrc.map_offset + 28), BHW2 (rsrc.type_count - 1)) ;
+
+	rsrc.item_offset = rsrc.type_offset + rsrc.type_count * 8 ;
+
+	rsrc.str_count = ARRAY_LEN (str_rsrc) ;
+	rsrc.string_offset = rsrc.item_offset + (rsrc.str_count + 1) * 12 - rsrc.map_offset ;
+	psf_binheader_writef (psf, "Eo2", BHWo (rsrc.map_offset + 26), BHW2 (rsrc.string_offset)) ;
+
+	/* Write 'STR ' resource type. */
+	rsrc.str_count = 3 ;
+	psf_binheader_writef (psf, "Eom22", BHWo (rsrc.type_offset), BHWm (STR_MARKER), BHW2 (rsrc.str_count - 1), BHW2 (0x12)) ;
+
+	/* Write 'sdML' resource type. */
+	psf_binheader_writef (psf, "Em22", BHWm (sdML_MARKER), BHW2 (0), BHW2 (0x36)) ;
+
+	str_offset = rsrc.map_offset + rsrc.string_offset ;
+	next_str = 0 ;
+	data_offset = rsrc.data_offset ;
+	for (k = 0 ; k < ARRAY_LEN (str_rsrc) ; k++)
+	{	psf_binheader_writef (psf, "Eop", BHWo (str_offset), BHWp (str_rsrc [k].name)) ;
+		psf_binheader_writef (psf, "Eo22", BHWo (rsrc.item_offset + k * 12), BHW2 (str_rsrc [k].id), BHW2 (next_str)) ;
+
+		str_offset += strlen (str_rsrc [k].name) ;
+		next_str += strlen (str_rsrc [k].name) ;
+
+		psf_binheader_writef (psf, "Eo4", BHWo (rsrc.item_offset + k * 12 + 4), BHW4 (data_offset - rsrc.data_offset)) ;
+		psf_binheader_writef (psf, "Eo4", BHWo (data_offset), BHW4 (str_rsrc [k].value_len)) ;
+
+		psf_binheader_writef (psf, "Eob", BHWo (data_offset + 4), BHWv (str_rsrc [k].value), BHWz (str_rsrc [k].value_len)) ;
+		data_offset += 4 + str_rsrc [k].value_len ;
+		} ;
+
+	/* Finally, calculate and set map length. */
+	rsrc.map_length = str_offset - rsrc.map_offset ;
+	psf_binheader_writef (psf, "Eo4o4", BHWo (12), BHW4 (rsrc.map_length),
+							BHWo (rsrc.map_offset + 12), BHW4 (rsrc.map_length)) ;
+
+	psf->header.indx = rsrc.map_offset + rsrc.map_length ;
+
+	psf_fwrite (psf->header.ptr, psf->header.indx, 1, psf) ;
+
+	psf_use_rsrc (psf, SF_FALSE) ;
+
+	if (psf->error)
+		return psf->error ;
+
+	return 0 ;
+} /* sd2_write_rsrc_fork */
+
+/*------------------------------------------------------------------------------
+*/
+
+static inline int
+read_rsrc_char (const SD2_RSRC *prsrc, int offset)
+{	const unsigned char * data = prsrc->rsrc_data ;
+	if (offset < 0 || offset >= prsrc->rsrc_len)
+		return 0 ;
+	return data [offset] ;
+} /* read_rsrc_char */
+
+static inline int
+read_rsrc_short (const SD2_RSRC *prsrc, int offset)
+{	const unsigned char * data = prsrc->rsrc_data ;
+	if (offset < 0 || offset + 1 >= prsrc->rsrc_len)
+		return 0 ;
+	return (data [offset] << 8) + data [offset + 1] ;
+} /* read_rsrc_short */
+
+static inline int
+read_rsrc_int (const SD2_RSRC *prsrc, int offset)
+{	const unsigned char * data = prsrc->rsrc_data ;
+	if (offset < 0 || offset + 3 >= prsrc->rsrc_len)
+		return 0 ;
+	return (((uint32_t) data [offset]) << 24) + (data [offset + 1] << 16) + (data [offset + 2] << 8) + data [offset + 3] ;
+} /* read_rsrc_int */
+
+static inline int
+read_rsrc_marker (const SD2_RSRC *prsrc, int offset)
+{	const unsigned char * data = prsrc->rsrc_data ;
+
+	if (offset < 0 || offset + 3 >= prsrc->rsrc_len)
+		return 0 ;
+
+	if (CPU_IS_BIG_ENDIAN)
+		return (((uint32_t) data [offset]) << 24) + (data [offset + 1] << 16) + (data [offset + 2] << 8) + data [offset + 3] ;
+	if (CPU_IS_LITTLE_ENDIAN)
+		return data [offset] + (data [offset + 1] << 8) + (data [offset + 2] << 16) + (((uint32_t) data [offset + 3]) << 24) ;
+
+	return 0 ;
+} /* read_rsrc_marker */
+
+static void
+read_rsrc_str (const SD2_RSRC *prsrc, int offset, char * buffer, int buffer_len)
+{	const unsigned char * data = prsrc->rsrc_data ;
+	int k ;
+
+	memset (buffer, 0, buffer_len) ;
+
+	if (offset < 0 || offset + buffer_len >= prsrc->rsrc_len)
+		return ;
+
+	for (k = 0 ; k < buffer_len - 1 ; k++)
+	{	if (psf_isprint (data [offset + k]) == 0)
+			return ;
+		buffer [k] = data [offset + k] ;
+		} ;
+	return ;
+} /* read_rsrc_str */
+
+static int
+sd2_parse_rsrc_fork (SF_PRIVATE *psf)
+{	SD2_RSRC rsrc ;
+	int k, marker, error = 0 ;
+
+	psf_use_rsrc (psf, SF_TRUE) ;
+
+	memset (&rsrc, 0, sizeof (rsrc)) ;
+
+	rsrc.rsrc_len = psf_get_filelen (psf) ;
+	psf_log_printf (psf, "Resource length : %d (0x%04X)\n", rsrc.rsrc_len, rsrc.rsrc_len) ;
+
+	if (rsrc.rsrc_len > psf->header.len)
+	{	rsrc.rsrc_data = calloc (1, rsrc.rsrc_len) ;
+		rsrc.need_to_free_rsrc_data = SF_TRUE ;
+		}
+	else
+	{
+		rsrc.rsrc_data = psf->header.ptr ;
+		// rsrc.rsrc_len > psf->header.len ;
+		rsrc.need_to_free_rsrc_data = SF_FALSE ;
+		} ;
+
+	/* Read in the whole lot. */
+	psf_fread (rsrc.rsrc_data, rsrc.rsrc_len, 1, psf) ;
+
+	/* Reset the header storage because we have changed to the rsrcdes. */
+	psf->header.indx = psf->header.end = rsrc.rsrc_len ;
+
+	rsrc.data_offset = read_rsrc_int (&rsrc, 0) ;
+	rsrc.map_offset = read_rsrc_int (&rsrc, 4) ;
+	rsrc.data_length = read_rsrc_int (&rsrc, 8) ;
+	rsrc.map_length = read_rsrc_int (&rsrc, 12) ;
+
+	if (rsrc.data_offset == 0x51607 && rsrc.map_offset == 0x20000)
+	{	psf_log_printf (psf, "Trying offset of 0x52 bytes.\n") ;
+		rsrc.data_offset = read_rsrc_int (&rsrc, 0x52 + 0) + 0x52 ;
+		rsrc.map_offset = read_rsrc_int (&rsrc, 0x52 + 4) + 0x52 ;
+		rsrc.data_length = read_rsrc_int (&rsrc, 0x52 + 8) ;
+		rsrc.map_length = read_rsrc_int (&rsrc, 0x52 + 12) ;
+		} ;
+
+	psf_log_printf (psf, "  data offset : 0x%04X\n  map  offset : 0x%04X\n"
+				"  data length : 0x%04X\n  map  length : 0x%04X\n",
+				rsrc.data_offset, rsrc.map_offset, rsrc.data_length, rsrc.map_length) ;
+
+	if (rsrc.data_offset > rsrc.rsrc_len)
+	{	psf_log_printf (psf, "Error : rsrc.data_offset (%d, 0x%x) > len\n", rsrc.data_offset, rsrc.data_offset) ;
+		error = SFE_SD2_BAD_DATA_OFFSET ;
+		goto parse_rsrc_fork_cleanup ;
+		} ;
+
+	if (rsrc.map_offset > rsrc.rsrc_len)
+	{	psf_log_printf (psf, "Error : rsrc.map_offset > len\n") ;
+		error = SFE_SD2_BAD_MAP_OFFSET ;
+		goto parse_rsrc_fork_cleanup ;
+		} ;
+
+	if (rsrc.data_length > rsrc.rsrc_len)
+	{	psf_log_printf (psf, "Error : rsrc.data_length > len\n") ;
+		error = SFE_SD2_BAD_DATA_LENGTH ;
+		goto parse_rsrc_fork_cleanup ;
+		} ;
+
+	if (rsrc.map_length > rsrc.rsrc_len)
+	{	psf_log_printf (psf, "Error : rsrc.map_length > len\n") ;
+		error = SFE_SD2_BAD_MAP_LENGTH ;
+		goto parse_rsrc_fork_cleanup ;
+		} ;
+
+	if (rsrc.data_offset + rsrc.data_length != rsrc.map_offset || rsrc.map_offset + rsrc.map_length != rsrc.rsrc_len)
+	{	psf_log_printf (psf, "Error : This does not look like a MacOSX resource fork.\n") ;
+		error = SFE_SD2_BAD_RSRC ;
+		goto parse_rsrc_fork_cleanup ;
+		} ;
+
+	if (rsrc.map_offset + 28 >= rsrc.rsrc_len)
+	{	psf_log_printf (psf, "Bad map offset (%d + 28 > %d).\n", rsrc.map_offset, rsrc.rsrc_len) ;
+		error = SFE_SD2_BAD_RSRC ;
+		goto parse_rsrc_fork_cleanup ;
+		} ;
+
+	rsrc.string_offset = rsrc.map_offset + read_rsrc_short (&rsrc, rsrc.map_offset + 26) ;
+	if (rsrc.string_offset > rsrc.rsrc_len)
+	{	psf_log_printf (psf, "Bad string offset (%d).\n", rsrc.string_offset) ;
+		error = SFE_SD2_BAD_RSRC ;
+		goto parse_rsrc_fork_cleanup ;
+		} ;
+
+	rsrc.type_offset = rsrc.map_offset + 30 ;
+
+	if (rsrc.map_offset + 28 > rsrc.rsrc_len)
+	{	psf_log_printf (psf, "Bad map offset.\n") ;
+		goto parse_rsrc_fork_cleanup ;
+		} ;
+
+	rsrc.type_count = read_rsrc_short (&rsrc, rsrc.map_offset + 28) + 1 ;
+	if (rsrc.type_count < 1)
+	{	psf_log_printf (psf, "Bad type count.\n") ;
+		error = SFE_SD2_BAD_RSRC ;
+		goto parse_rsrc_fork_cleanup ;
+		} ;
+
+	rsrc.item_offset = rsrc.type_offset + rsrc.type_count * 8 ;
+	if (rsrc.item_offset < 0 || rsrc.item_offset > rsrc.rsrc_len)
+	{	psf_log_printf (psf, "Bad item offset (%d).\n", rsrc.item_offset) ;
+		error = SFE_SD2_BAD_RSRC ;
+		goto parse_rsrc_fork_cleanup ;
+		} ;
+
+	rsrc.str_index = -1 ;
+	for (k = 0 ; k < rsrc.type_count ; k ++)
+	{	if (rsrc.type_offset + k * 8 > rsrc.rsrc_len)
+		{	psf_log_printf (psf, "Bad rsrc marker.\n") ;
+			goto parse_rsrc_fork_cleanup ;
+			} ;
+
+		marker = read_rsrc_marker (&rsrc, rsrc.type_offset + k * 8) ;
+
+		if (marker == STR_MARKER)
+		{	rsrc.str_index = k ;
+			rsrc.str_count = read_rsrc_short (&rsrc, rsrc.type_offset + k * 8 + 4) + 1 ;
+			error = parse_str_rsrc (psf, &rsrc) ;
+			goto parse_rsrc_fork_cleanup ;
+			} ;
+		} ;
+
+	psf_log_printf (psf, "No 'STR ' resource.\n") ;
+	error = SFE_SD2_BAD_RSRC ;
+
+parse_rsrc_fork_cleanup :
+
+	psf_use_rsrc (psf, SF_FALSE) ;
+
+	if (rsrc.need_to_free_rsrc_data)
+		free (rsrc.rsrc_data) ;
+
+	return error ;
+} /* sd2_parse_rsrc_fork */
+
+static int
+parse_str_rsrc (SF_PRIVATE *psf, SD2_RSRC * rsrc)
+{	char name [32], value [32] ;
+	int k, str_offset, rsrc_id, data_offset = 0, data_len = 0 ;
+
+	psf_log_printf (psf, "Finding parameters :\n") ;
+
+	str_offset = rsrc->string_offset ;
+	psf_log_printf (psf, "  Offset    RsrcId    dlen    slen    Value\n") ;
+
+
+	for (k = 0 ; data_offset + data_len < rsrc->rsrc_len ; k++)
+	{	int slen ;
+
+		slen = read_rsrc_char (rsrc, str_offset) ;
+		read_rsrc_str (rsrc, str_offset + 1, name, SF_MIN (SIGNED_SIZEOF (name), slen + 1)) ;
+		str_offset += slen + 1 ;
+
+		// work-around for GitHub issue #340
+		int id_offset = rsrc->item_offset + k * 12 ;
+		if (id_offset < 0 || id_offset + 1 >= rsrc->rsrc_len)
+		{	psf_log_printf (psf, "Exiting parser on id_offset of %d.\n", id_offset) ;
+			break ;
+			}
+		rsrc_id = read_rsrc_short (rsrc, id_offset) ;
+
+		data_offset = rsrc->data_offset + read_rsrc_int (rsrc, rsrc->item_offset + k * 12 + 4) ;
+		if (data_offset < 0 || data_offset > rsrc->rsrc_len)
+		{	psf_log_printf (psf, "Exiting parser on data offset of %d.\n", data_offset) ;
+			break ;
+			} ;
+
+		data_len = read_rsrc_int (rsrc, data_offset) ;
+		if (data_len < 0 || data_len > rsrc->rsrc_len)
+		{	psf_log_printf (psf, "Exiting parser on data length of %d.\n", data_len) ;
+			break ;
+			} ;
+
+		slen = read_rsrc_char (rsrc, data_offset + 4) ;
+		read_rsrc_str (rsrc, data_offset + 5, value, SF_MIN (SIGNED_SIZEOF (value), slen + 1)) ;
+
+		psf_log_printf (psf, "  0x%04x     %4d     %4d     %3d    '%s'\n", data_offset, rsrc_id, data_len, slen, value) ;
+
+		if (strstr (value, "Photoshop"))
+		{	psf_log_printf (psf, "Exiting parser on Photoshop data.\n", data_offset) ;
+			break ;
+			} ;
+
+		if (rsrc_id == 1000 && rsrc->sample_size == 0)
+			rsrc->sample_size = strtol (value, NULL, 10) ;
+		else if (rsrc_id == 1001 && rsrc->sample_rate == 0)
+			rsrc->sample_rate = strtol (value, NULL, 10) ;
+		else if (rsrc_id == 1002 && rsrc->channels == 0)
+			rsrc->channels = strtol (value, NULL, 10) ;
+		} ;
+
+	psf_log_printf (psf, "Found Parameters :\n") ;
+	psf_log_printf (psf, "  sample-size : %d\n", rsrc->sample_size) ;
+	psf_log_printf (psf, "  sample-rate : %d\n", rsrc->sample_rate) ;
+	psf_log_printf (psf, "  channels    : %d\n", rsrc->channels) ;
+
+	if (rsrc->sample_rate <= 4 && rsrc->sample_size > 4)
+	{	int temp ;
+
+		psf_log_printf (psf, "Geez!! Looks like sample rate and sample size got switched.\nCorrecting this screw up.\n") ;
+		temp = rsrc->sample_rate ;
+		rsrc->sample_rate = rsrc->sample_size ;
+		rsrc->sample_size = temp ;
+		} ;
+
+	if (rsrc->sample_rate < 0)
+	{	psf_log_printf (psf, "Bad sample rate (%d)\n", rsrc->sample_rate) ;
+		return SFE_SD2_BAD_RSRC ;
+		} ;
+
+	if (rsrc->channels < 0)
+	{	psf_log_printf (psf, "Bad channel count (%d)\n", rsrc->channels) ;
+		return SFE_SD2_BAD_RSRC ;
+		} ;
+
+	psf->sf.samplerate = rsrc->sample_rate ;
+	psf->sf.channels = rsrc->channels ;
+	psf->bytewidth = rsrc->sample_size ;
+
+	switch (rsrc->sample_size)
+	{	case 1 :
+			psf->sf.format = SF_FORMAT_SD2 | SF_FORMAT_PCM_S8 ;
+			break ;
+
+		case 2 :
+			psf->sf.format = SF_FORMAT_SD2 | SF_FORMAT_PCM_16 ;
+			break ;
+
+		case 3 :
+			psf->sf.format = SF_FORMAT_SD2 | SF_FORMAT_PCM_24 ;
+			break ;
+
+		case 4 :
+			psf->sf.format = SF_FORMAT_SD2 | SF_FORMAT_PCM_32 ;
+			break ;
+
+		default :
+			psf_log_printf (psf, "Bad sample size (%d)\n", rsrc->sample_size) ;
+			return SFE_SD2_BAD_SAMPLE_SIZE ;
+		} ;
+
+	psf_log_printf (psf, "ok\n") ;
+
+	return 0 ;
+} /* parse_str_rsrc */
+
diff --git a/src/sds.c b/src/sds.c
new file mode 100644
index 0000000..6bc7617
--- /dev/null
+++ b/src/sds.c
@@ -0,0 +1,1022 @@
+/*
+** Copyright (C) 2002-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+
+#include "sndfile.h"
+#include "sfendian.h"
+#include "common.h"
+
+/*------------------------------------------------------------------------------
+*/
+
+#define	SDS_DATA_OFFSET				0x15
+#define SDS_BLOCK_SIZE				127
+
+#define SDS_AUDIO_BYTES_PER_BLOCK	120
+
+#define SDS_3BYTE_TO_INT_DECODE(x) (((x) & 0x7F) | (((x) & 0x7F00) >> 1) | (((x) & 0x7F0000) >> 2))
+#define SDS_INT_TO_3BYTE_ENCODE(x) (((x) & 0x7F) | (((x) << 1) & 0x7F00) | (((x) << 2) & 0x7F0000))
+
+/*------------------------------------------------------------------------------
+** Typedefs.
+*/
+
+typedef struct tag_SDS_PRIVATE
+{	int bitwidth, frames ;
+	int	samplesperblock, total_blocks ;
+
+	int (*reader) (SF_PRIVATE *psf, struct tag_SDS_PRIVATE *psds) ;
+	int (*writer) (SF_PRIVATE *psf, struct tag_SDS_PRIVATE *psds) ;
+
+	int read_block, read_count ;
+	unsigned char read_data [SDS_BLOCK_SIZE] ;
+	int	read_samples [SDS_BLOCK_SIZE / 2] ; /* Maximum samples per block */
+
+	int write_block, write_count ;
+	int total_written ;
+	unsigned char write_data [SDS_BLOCK_SIZE] ;
+	int	write_samples [SDS_BLOCK_SIZE / 2] ; /* Maximum samples per block */
+} SDS_PRIVATE ;
+
+/*------------------------------------------------------------------------------
+** Private static functions.
+*/
+
+static int	sds_close	(SF_PRIVATE *psf) ;
+
+static int	sds_write_header (SF_PRIVATE *psf, int calc_length) ;
+static int	sds_read_header (SF_PRIVATE *psf, SDS_PRIVATE *psds) ;
+
+static int	sds_init (SF_PRIVATE *psf, SDS_PRIVATE *psds) ;
+
+static sf_count_t sds_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
+static sf_count_t sds_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
+static sf_count_t sds_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
+static sf_count_t sds_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
+
+static sf_count_t sds_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
+static sf_count_t sds_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
+static sf_count_t sds_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
+static sf_count_t sds_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
+
+static sf_count_t sds_seek (SF_PRIVATE *psf, int mode, sf_count_t offset) ;
+static int sds_byterate (SF_PRIVATE * psf) ;
+
+static int sds_2byte_read (SF_PRIVATE *psf, SDS_PRIVATE *psds) ;
+static int sds_3byte_read (SF_PRIVATE *psf, SDS_PRIVATE *psds) ;
+static int sds_4byte_read (SF_PRIVATE *psf, SDS_PRIVATE *psds) ;
+
+static int sds_read (SF_PRIVATE *psf, SDS_PRIVATE *psds, int *iptr, int readcount) ;
+
+static int sds_2byte_write (SF_PRIVATE *psf, SDS_PRIVATE *psds) ;
+static int sds_3byte_write (SF_PRIVATE *psf, SDS_PRIVATE *psds) ;
+static int sds_4byte_write (SF_PRIVATE *psf, SDS_PRIVATE *psds) ;
+
+static int sds_write (SF_PRIVATE *psf, SDS_PRIVATE *psds, const int *iptr, int writecount) ;
+
+/*------------------------------------------------------------------------------
+** Public function.
+*/
+
+int
+sds_open	(SF_PRIVATE *psf)
+{	SDS_PRIVATE	*psds ;
+	int			error = 0 ;
+
+	/* Hmmmm, need this here to pass update_header_test. */
+	psf->sf.frames = 0 ;
+
+	if (! (psds = calloc (1, sizeof (SDS_PRIVATE))))
+		return SFE_MALLOC_FAILED ;
+	psf->codec_data = psds ;
+
+	if (psf->file.mode == SFM_READ || (psf->file.mode == SFM_RDWR && psf->filelength > 0))
+	{	if ((error = sds_read_header (psf, psds)))
+			return error ;
+		} ;
+
+	if ((SF_CONTAINER (psf->sf.format)) != SF_FORMAT_SDS)
+		return	SFE_BAD_OPEN_FORMAT ;
+
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	if (sds_write_header (psf, SF_FALSE))
+			return psf->error ;
+
+		psf->write_header = sds_write_header ;
+
+		psf_fseek (psf, SDS_DATA_OFFSET, SEEK_SET) ;
+		} ;
+
+	if ((error = sds_init (psf, psds)) != 0)
+		return error ;
+
+	psf->container_close = sds_close ;
+	psf->seek = sds_seek ;
+	psf->byterate = sds_byterate ;
+
+	psf->blockwidth = 0 ;
+
+	return error ;
+} /* sds_open */
+
+/*------------------------------------------------------------------------------
+*/
+
+static int
+sds_close	(SF_PRIVATE *psf)
+{
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	SDS_PRIVATE *psds ;
+
+		if ((psds = (SDS_PRIVATE *) psf->codec_data) == NULL)
+		{	psf_log_printf (psf, "*** Bad psf->codec_data ptr.\n") ;
+			return SFE_INTERNAL ;
+			} ;
+
+		if (psds->write_count > 0)
+		{	memset (&(psds->write_data [psds->write_count]), 0, (psds->samplesperblock - psds->write_count) * sizeof (int)) ;
+			psds->writer (psf, psds) ;
+			} ;
+
+		sds_write_header (psf, SF_TRUE) ;
+		} ;
+
+	return 0 ;
+} /* sds_close */
+
+static int
+sds_init (SF_PRIVATE *psf, SDS_PRIVATE *psds)
+{
+	if (psds->bitwidth < 8 || psds->bitwidth > 28)
+		return (psf->error = SFE_SDS_BAD_BIT_WIDTH) ;
+
+	if (psds->bitwidth < 14)
+	{	psds->reader = sds_2byte_read ;
+		psds->writer = sds_2byte_write ;
+		psds->samplesperblock = SDS_AUDIO_BYTES_PER_BLOCK / 2 ;
+		}
+	else if (psds->bitwidth < 21)
+	{	psds->reader = sds_3byte_read ;
+		psds->writer = sds_3byte_write ;
+		psds->samplesperblock = SDS_AUDIO_BYTES_PER_BLOCK / 3 ;
+		}
+	else
+	{	psds->reader = sds_4byte_read ;
+		psds->writer = sds_4byte_write ;
+		psds->samplesperblock = SDS_AUDIO_BYTES_PER_BLOCK / 4 ;
+		} ;
+
+	if (psf->file.mode == SFM_READ || psf->file.mode == SFM_RDWR)
+	{	psf->read_short		= sds_read_s ;
+		psf->read_int		= sds_read_i ;
+		psf->read_float		= sds_read_f ;
+		psf->read_double	= sds_read_d ;
+
+		/* Read first block. */
+		psds->reader (psf, psds) ;
+		} ;
+
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	psf->write_short	= sds_write_s ;
+		psf->write_int		= sds_write_i ;
+		psf->write_float	= sds_write_f ;
+		psf->write_double	= sds_write_d ;
+		} ;
+
+	return 0 ;
+} /* sds_init */
+
+static int
+sds_read_header (SF_PRIVATE *psf, SDS_PRIVATE *psds)
+{	unsigned char	channel, bitwidth, loop_type, byte ;
+	unsigned short	sample_no, marker ;
+	unsigned int	samp_period, data_length, sustain_loop_start, sustain_loop_end ;
+	int		bytesread, blockcount ;
+
+	/* Set position to start of file to begin reading header. */
+	bytesread = psf_binheader_readf (psf, "pE211", 0, &marker, &channel, &byte) ;
+
+	if (marker != 0xF07E || byte != 0x01)
+		return SFE_SDS_NOT_SDS ;
+
+	bytesread += psf_binheader_readf (psf, "e2", &sample_no) ;
+	sample_no = SDS_3BYTE_TO_INT_DECODE (sample_no) ;
+
+	psf_log_printf (psf, "Midi Sample Dump Standard (.sds)\nF07E\n"
+						" Midi Channel  : %d\n Sample Number : %d\n",
+						channel, sample_no) ;
+
+	bytesread += psf_binheader_readf (psf, "e13", &bitwidth, &samp_period) ;
+
+	samp_period = SDS_3BYTE_TO_INT_DECODE (samp_period) ;
+
+	psds->bitwidth = bitwidth ;
+
+	if (psds->bitwidth > 1)
+		psf_log_printf (psf, " Bit Width     : %d\n", psds->bitwidth) ;
+	else
+	{	psf_log_printf (psf, " Bit Width     : %d (should be > 1)\n", psds->bitwidth) ;
+		return SFE_SDS_BAD_BIT_WIDTH ;
+		} ;
+
+	if (samp_period > 0)
+	{	psf->sf.samplerate = 1000000000 / samp_period ;
+
+		psf_log_printf (psf, " Sample Period : %d\n"
+							" Sample Rate   : %d\n",
+							samp_period, psf->sf.samplerate) ;
+		}
+	else
+	{	psf->sf.samplerate = 16000 ;
+
+		psf_log_printf (psf, " Sample Period : %d (should be > 0)\n"
+							" Sample Rate   : %d (guessed)\n",
+							samp_period, psf->sf.samplerate) ;
+		} ;
+
+	bytesread += psf_binheader_readf (psf, "e3331", &data_length, &sustain_loop_start, &sustain_loop_end, &loop_type) ;
+
+	data_length = SDS_3BYTE_TO_INT_DECODE (data_length) ;
+
+	psf->sf.frames = psds->frames = data_length ;
+
+	sustain_loop_start = SDS_3BYTE_TO_INT_DECODE (sustain_loop_start) ;
+	sustain_loop_end = SDS_3BYTE_TO_INT_DECODE (sustain_loop_end) ;
+
+	psf_log_printf (psf, 	" Sustain Loop\n"
+							"     Start     : %d\n"
+							"     End       : %d\n"
+							"     Loop Type : %d\n",
+			sustain_loop_start, sustain_loop_end, loop_type) ;
+
+	psf->dataoffset = SDS_DATA_OFFSET ;
+	psf->datalength = psf->filelength - psf->dataoffset ;
+
+	bytesread += psf_binheader_readf (psf, "1", &byte) ;
+	if (byte != 0xF7)
+		psf_log_printf (psf, "bad end : %X\n", byte & 0xFF) ;
+
+	for (blockcount = 0 ; bytesread < psf->filelength ; blockcount++)
+	{
+		bytesread += (int) psf_fread (&marker, 1, 2, psf) ;
+
+		if (marker == 0)
+			break ;
+
+		psf_fseek (psf, SDS_BLOCK_SIZE - 2, SEEK_CUR) ;
+		bytesread += SDS_BLOCK_SIZE - 2 ;
+		} ;
+
+	psf_log_printf (psf, "\nBlocks         : %d\n", blockcount) ;
+	psds->total_blocks = blockcount ;
+
+	psds->samplesperblock = SDS_AUDIO_BYTES_PER_BLOCK / ((psds->bitwidth + 6) / 7) ;
+	psf_log_printf (psf, "Samples/Block  : %d\n", psds->samplesperblock) ;
+
+	psf_log_printf (psf, "Frames         : %d\n", blockcount * psds->samplesperblock) ;
+
+	/* Always Mono */
+	psf->sf.channels = 1 ;
+	psf->sf.sections = 1 ;
+
+	/*
+	** Lie to the user about PCM bit width. Always round up to
+	** the next multiple of 8.
+	*/
+	switch ((psds->bitwidth + 7) / 8)
+	{	case 1 :
+			psf->sf.format = SF_FORMAT_SDS | SF_FORMAT_PCM_S8 ;
+			break ;
+
+		case 2 :
+			psf->sf.format = SF_FORMAT_SDS | SF_FORMAT_PCM_16 ;
+			break ;
+
+		case 3 :
+			psf->sf.format = SF_FORMAT_SDS | SF_FORMAT_PCM_24 ;
+			break ;
+
+		case 4 :
+			psf->sf.format = SF_FORMAT_SDS | SF_FORMAT_PCM_32 ;
+			break ;
+
+		default :
+			psf_log_printf (psf, "*** Weird byte width (%d)\n", (psds->bitwidth + 7) / 8) ;
+			return SFE_SDS_BAD_BIT_WIDTH ;
+		} ;
+
+	psf_fseek (psf, SDS_DATA_OFFSET, SEEK_SET) ;
+
+	return 0 ;
+} /* sds_read_header */
+
+static int
+sds_write_header (SF_PRIVATE *psf, int calc_length)
+{	SDS_PRIVATE *psds ;
+	sf_count_t	current ;
+	int samp_period, data_length, sustain_loop_start, sustain_loop_end ;
+	unsigned char loop_type = 0 ;
+
+	if ((psds = (SDS_PRIVATE *) psf->codec_data) == NULL)
+	{	psf_log_printf (psf, "*** Bad psf->codec_data ptr.\n") ;
+		return SFE_INTERNAL ;
+		} ;
+
+	if (psf->pipeoffset > 0)
+		return 0 ;
+
+	current = psf_ftell (psf) ;
+
+	if (calc_length)
+		psf->sf.frames = psds->total_written ;
+
+	if (psds->write_count > 0)
+	{	int current_count = psds->write_count ;
+		int current_block = psds->write_block ;
+
+		psds->writer (psf, psds) ;
+
+		psf_fseek (psf, -1 * SDS_BLOCK_SIZE, SEEK_CUR) ;
+
+		psds->write_count = current_count ;
+		psds->write_block = current_block ;
+		} ;
+
+	/* Reset the current header length to zero. */
+	psf->header.ptr [0] = 0 ;
+	psf->header.indx = 0 ;
+
+	if (psf->is_pipe == SF_FALSE)
+		psf_fseek (psf, 0, SEEK_SET) ;
+
+	psf_binheader_writef (psf, "E211", BHW2 (0xF07E), BHW1 (0), BHW1 (1)) ;
+
+	switch (SF_CODEC (psf->sf.format))
+	{	case SF_FORMAT_PCM_S8 :
+				psds->bitwidth = 8 ;
+				break ;
+		case SF_FORMAT_PCM_16 :
+				psds->bitwidth = 16 ;
+				break ;
+		case SF_FORMAT_PCM_24 :
+				psds->bitwidth = 24 ;
+				break ;
+		default:
+			return SFE_SDS_BAD_BIT_WIDTH ;
+		} ;
+
+	samp_period = SDS_INT_TO_3BYTE_ENCODE (1000000000 / psf->sf.samplerate) ;
+
+	psf_binheader_writef (psf, "e213", BHW2 (0), BHW1 (psds->bitwidth), BHW3 (samp_period)) ;
+
+	data_length			= SDS_INT_TO_3BYTE_ENCODE (psds->total_written) ;
+	sustain_loop_start	= SDS_INT_TO_3BYTE_ENCODE (0) ;
+	sustain_loop_end	= SDS_INT_TO_3BYTE_ENCODE (0) ;
+
+	psf_binheader_writef (psf, "e33311", BHW3 (data_length), BHW3 (sustain_loop_start), BHW3 (sustain_loop_end), BHW1 (loop_type), BHW1 (0xF7)) ;
+
+	/* Header construction complete so write it out. */
+	psf_fwrite (psf->header.ptr, psf->header.indx, 1, psf) ;
+
+	if (psf->error)
+		return psf->error ;
+
+	psf->dataoffset = psf->header.indx ;
+	psf->datalength = psds->write_block * SDS_BLOCK_SIZE ;
+
+	if (current > 0)
+		psf_fseek (psf, current, SEEK_SET) ;
+
+	return psf->error ;
+} /* sds_write_header */
+
+
+/*------------------------------------------------------------------------------
+*/
+
+static int
+sds_2byte_read (SF_PRIVATE *psf, SDS_PRIVATE *psds)
+{	unsigned char *ucptr, checksum ;
+	unsigned int sample ;
+	int 	k ;
+
+	psds->read_block ++ ;
+	psds->read_count = 0 ;
+
+	if (psds->read_block * psds->samplesperblock > psds->frames)
+	{	memset (psds->read_samples, 0, psds->samplesperblock * sizeof (int)) ;
+		return 1 ;
+		} ;
+
+	if ((k = (int) psf_fread (psds->read_data, 1, SDS_BLOCK_SIZE, psf)) != SDS_BLOCK_SIZE)
+		psf_log_printf (psf, "*** Warning : short read (%d != %d).\n", k, SDS_BLOCK_SIZE) ;
+
+	if (psds->read_data [0] != 0xF0)
+	{	printf ("Error A : %02X\n", psds->read_data [0] & 0xFF) ;
+		} ;
+
+	checksum = psds->read_data [1] ;
+	if (checksum != 0x7E)
+	{	printf ("Error 1 : %02X\n", checksum & 0xFF) ;
+		}
+
+	for (k = 2 ; k <= SDS_BLOCK_SIZE - 3 ; k ++)
+		checksum ^= psds->read_data [k] ;
+
+	checksum &= 0x7F ;
+
+	if (checksum != psds->read_data [SDS_BLOCK_SIZE - 2])
+	{	psf_log_printf (psf, "Block %d : checksum is %02X should be %02X\n", psds->read_data [4], checksum, psds->read_data [SDS_BLOCK_SIZE - 2]) ;
+		} ;
+
+	ucptr = psds->read_data + 5 ;
+	for (k = 0 ; k < 120 ; k += 2)
+	{	sample = arith_shift_left (ucptr [k], 25) + arith_shift_left (ucptr [k + 1], 18) ;
+		psds->read_samples [k / 2] = (int) (sample - 0x80000000) ;
+		} ;
+
+	return 1 ;
+} /* sds_2byte_read */
+
+static int
+sds_3byte_read (SF_PRIVATE *psf, SDS_PRIVATE *psds)
+{	unsigned char *ucptr, checksum ;
+	unsigned int sample ;
+	int 	k ;
+
+	psds->read_block ++ ;
+	psds->read_count = 0 ;
+
+	if (psds->read_block * psds->samplesperblock > psds->frames)
+	{	memset (psds->read_samples, 0, psds->samplesperblock * sizeof (int)) ;
+		return 1 ;
+		} ;
+
+	if ((k = (int) psf_fread (psds->read_data, 1, SDS_BLOCK_SIZE, psf)) != SDS_BLOCK_SIZE)
+		psf_log_printf (psf, "*** Warning : short read (%d != %d).\n", k, SDS_BLOCK_SIZE) ;
+
+	if (psds->read_data [0] != 0xF0)
+	{	printf ("Error A : %02X\n", psds->read_data [0] & 0xFF) ;
+		} ;
+
+	checksum = psds->read_data [1] ;
+	if (checksum != 0x7E)
+	{	printf ("Error 1 : %02X\n", checksum & 0xFF) ;
+		}
+
+	for (k = 2 ; k <= SDS_BLOCK_SIZE - 3 ; k ++)
+		checksum ^= psds->read_data [k] ;
+
+	checksum &= 0x7F ;
+
+	if (checksum != psds->read_data [SDS_BLOCK_SIZE - 2])
+	{	psf_log_printf (psf, "Block %d : checksum is %02X should be %02X\n", psds->read_data [4], checksum, psds->read_data [SDS_BLOCK_SIZE - 2]) ;
+		} ;
+
+	ucptr = psds->read_data + 5 ;
+	for (k = 0 ; k < 120 ; k += 3)
+	{	sample = (((uint32_t) ucptr [k]) << 25) + (ucptr [k + 1] << 18) + (ucptr [k + 2] << 11) ;
+		psds->read_samples [k / 3] = (int) (sample - 0x80000000) ;
+		} ;
+
+	return 1 ;
+} /* sds_3byte_read */
+
+static int
+sds_4byte_read (SF_PRIVATE *psf, SDS_PRIVATE *psds)
+{	unsigned char *ucptr, checksum ;
+	uint32_t sample ;
+	int 	k ;
+
+	psds->read_block ++ ;
+	psds->read_count = 0 ;
+
+	if (psds->read_block * psds->samplesperblock > psds->frames)
+	{	memset (psds->read_samples, 0, psds->samplesperblock * sizeof (int)) ;
+		return 1 ;
+		} ;
+
+	if ((k = (int) psf_fread (psds->read_data, 1, SDS_BLOCK_SIZE, psf)) != SDS_BLOCK_SIZE)
+		psf_log_printf (psf, "*** Warning : short read (%d != %d).\n", k, SDS_BLOCK_SIZE) ;
+
+	if (psds->read_data [0] != 0xF0)
+	{	printf ("Error A : %02X\n", psds->read_data [0] & 0xFF) ;
+		} ;
+
+	checksum = psds->read_data [1] ;
+	if (checksum != 0x7E)
+	{	printf ("Error 1 : %02X\n", checksum & 0xFF) ;
+		}
+
+	for (k = 2 ; k <= SDS_BLOCK_SIZE - 3 ; k ++)
+		checksum ^= psds->read_data [k] ;
+
+	checksum &= 0x7F ;
+
+	if (checksum != psds->read_data [SDS_BLOCK_SIZE - 2])
+	{	psf_log_printf (psf, "Block %d : checksum is %02X should be %02X\n", psds->read_data [4], checksum, psds->read_data [SDS_BLOCK_SIZE - 2]) ;
+		} ;
+
+	ucptr = psds->read_data + 5 ;
+	for (k = 0 ; k < 120 ; k += 4)
+	{	sample = (((uint32_t) ucptr [k]) << 25) + (ucptr [k + 1] << 18) + (ucptr [k + 2] << 11) + (ucptr [k + 3] << 4) ;
+		psds->read_samples [k / 4] = (int) (sample - 0x80000000) ;
+		} ;
+
+	return 1 ;
+} /* sds_4byte_read */
+
+
+static sf_count_t
+sds_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	SDS_PRIVATE	*psds ;
+	int			*iptr ;
+	int			k, bufferlen, readcount, count ;
+	sf_count_t	total = 0 ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	psds = (SDS_PRIVATE*) psf->codec_data ;
+
+	iptr = ubuf.ibuf ;
+	bufferlen = ARRAY_LEN (ubuf.ibuf) ;
+	while (len > 0)
+	{	readcount = (len >= bufferlen) ? bufferlen : (int) len ;
+		count = sds_read (psf, psds, iptr, readcount) ;
+		for (k = 0 ; k < readcount ; k++)
+			ptr [total + k] = iptr [k] >> 16 ;
+		total += count ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* sds_read_s */
+
+static sf_count_t
+sds_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len)
+{	SDS_PRIVATE *psds ;
+	int			total ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	psds = (SDS_PRIVATE*) psf->codec_data ;
+
+	total = sds_read (psf, psds, ptr, len) ;
+
+	return total ;
+} /* sds_read_i */
+
+static sf_count_t
+sds_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	SDS_PRIVATE	*psds ;
+	int			*iptr ;
+	int			k, bufferlen, readcount, count ;
+	sf_count_t	total = 0 ;
+	float		normfact ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	psds = (SDS_PRIVATE*) psf->codec_data ;
+
+	if (psf->norm_float == SF_TRUE)
+		normfact = 1.0 / 0x80000000 ;
+	else
+		normfact = 1.0 / (1 << psds->bitwidth) ;
+
+	iptr = ubuf.ibuf ;
+	bufferlen = ARRAY_LEN (ubuf.ibuf) ;
+	while (len > 0)
+	{	readcount = (len >= bufferlen) ? bufferlen : (int) len ;
+		count = sds_read (psf, psds, iptr, readcount) ;
+		for (k = 0 ; k < readcount ; k++)
+			ptr [total + k] = normfact * iptr [k] ;
+		total += count ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* sds_read_f */
+
+static sf_count_t
+sds_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	SDS_PRIVATE	*psds ;
+	int			*iptr ;
+	int			k, bufferlen, readcount, count ;
+	sf_count_t	total = 0 ;
+	double		normfact ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	psds = (SDS_PRIVATE*) psf->codec_data ;
+
+	if (psf->norm_double == SF_TRUE)
+		normfact = 1.0 / 0x80000000 ;
+	else
+		normfact = 1.0 / (1 << psds->bitwidth) ;
+
+	iptr = ubuf.ibuf ;
+	bufferlen = ARRAY_LEN (ubuf.ibuf) ;
+	while (len > 0)
+	{	readcount = (len >= bufferlen) ? bufferlen : (int) len ;
+		count = sds_read (psf, psds, iptr, readcount) ;
+		for (k = 0 ; k < readcount ; k++)
+			ptr [total + k] = normfact * iptr [k] ;
+		total += count ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* sds_read_d */
+
+static int
+sds_read (SF_PRIVATE *psf, SDS_PRIVATE *psds, int *ptr, int len)
+{	int	count, total = 0 ;
+
+	while (total < len)
+	{	if (psds->read_block * psds->samplesperblock >= psds->frames)
+		{	memset (&(ptr [total]), 0, (len - total) * sizeof (int)) ;
+			return total ;
+			} ;
+
+		if (psds->read_count >= psds->samplesperblock)
+			psds->reader (psf, psds) ;
+
+		count = (psds->samplesperblock - psds->read_count) ;
+		count = (len - total > count) ? count : len - total ;
+
+		memcpy (&(ptr [total]), &(psds->read_samples [psds->read_count]), count * sizeof (int)) ;
+		total += count ;
+		psds->read_count += count ;
+		} ;
+
+	return total ;
+} /* sds_read */
+
+/*==============================================================================
+*/
+
+static sf_count_t
+sds_seek (SF_PRIVATE *psf, int mode, sf_count_t seek_from_start)
+{	SDS_PRIVATE	*psds ;
+	sf_count_t	file_offset ;
+	int			newblock, newsample ;
+
+	if ((psds = psf->codec_data) == NULL)
+	{	psf->error = SFE_INTERNAL ;
+		return PSF_SEEK_ERROR ;
+		} ;
+
+	if (psf->datalength < 0 || psf->dataoffset < 0)
+	{	psf->error = SFE_BAD_SEEK ;
+		return PSF_SEEK_ERROR ;
+		} ;
+
+	if (seek_from_start < 0 || seek_from_start > psf->sf.frames)
+	{	psf->error = SFE_BAD_SEEK ;
+		return PSF_SEEK_ERROR ;
+		} ;
+
+	if (mode == SFM_READ && psds->write_count > 0)
+		psds->writer (psf, psds) ;
+
+	newblock = seek_from_start / psds->samplesperblock ;
+	newsample = seek_from_start % psds->samplesperblock ;
+
+	switch (mode)
+	{	case SFM_READ :
+			if (newblock > psds->total_blocks)
+			{	psf->error = SFE_BAD_SEEK ;
+				return PSF_SEEK_ERROR ;
+				} ;
+
+			file_offset = psf->dataoffset + newblock * SDS_BLOCK_SIZE ;
+
+			if (psf_fseek (psf, file_offset, SEEK_SET) != file_offset)
+			{	psf->error = SFE_SEEK_FAILED ;
+				return PSF_SEEK_ERROR ;
+				} ;
+
+			psds->read_block = newblock ;
+			psds->reader (psf, psds) ;
+			psds->read_count = newsample ;
+			break ;
+
+		case SFM_WRITE :
+			if (newblock > psds->total_blocks)
+			{	psf->error = SFE_BAD_SEEK ;
+				return PSF_SEEK_ERROR ;
+				} ;
+
+			file_offset = psf->dataoffset + newblock * SDS_BLOCK_SIZE ;
+
+			if (psf_fseek (psf, file_offset, SEEK_SET) != file_offset)
+			{	psf->error = SFE_SEEK_FAILED ;
+				return PSF_SEEK_ERROR ;
+				} ;
+
+			psds->write_block = newblock ;
+			psds->reader (psf, psds) ;
+			psds->write_count = newsample ;
+			break ;
+
+		default :
+			psf->error = SFE_BAD_SEEK ;
+			return PSF_SEEK_ERROR ;
+			break ;
+		} ;
+
+	return seek_from_start ;
+} /* sds_seek */
+
+static int
+sds_byterate (SF_PRIVATE * psf)
+{
+	if (psf->file.mode == SFM_READ)
+		return (psf->datalength * psf->sf.samplerate) / psf->sf.frames ;
+
+	return -1 ;
+} /* sds_byterate */
+
+/*==============================================================================
+*/
+
+static int
+sds_2byte_write (SF_PRIVATE *psf, SDS_PRIVATE *psds)
+{	unsigned char *ucptr, checksum ;
+	unsigned int sample ;
+	int 	k ;
+
+	psds->write_data [0] = 0xF0 ;
+	psds->write_data [1] = 0x7E ;
+	psds->write_data [2] = 0 ;							/* Channel number */
+	psds->write_data [3] = 2 ;
+	psds->write_data [4] = psds->write_block & 0x7F ;	/* Packet number */
+
+	ucptr = psds->write_data + 5 ;
+	for (k = 0 ; k < 120 ; k += 2)
+	{	sample = psds->write_samples [k / 2] ;
+		sample += 0x80000000 ;
+		ucptr [k] = (sample >> 25) & 0x7F ;
+		ucptr [k + 1] = (sample >> 18) & 0x7F ;
+		} ;
+
+	checksum = psds->write_data [1] ;
+	for (k = 2 ; k <= SDS_BLOCK_SIZE - 3 ; k ++)
+		checksum ^= psds->write_data [k] ;
+	checksum &= 0x7F ;
+
+	psds->write_data [SDS_BLOCK_SIZE - 2] = checksum ;
+	psds->write_data [SDS_BLOCK_SIZE - 1] = 0xF7 ;
+
+	if ((k = (int) psf_fwrite (psds->write_data, 1, SDS_BLOCK_SIZE, psf)) != SDS_BLOCK_SIZE)
+		psf_log_printf (psf, "*** Warning : psf_fwrite (%d != %d).\n", k, SDS_BLOCK_SIZE) ;
+
+	psds->write_block ++ ;
+	psds->write_count = 0 ;
+
+	if (psds->write_block > psds->total_blocks)
+		psds->total_blocks = psds->write_block ;
+	psds->frames = psds->total_blocks * psds->samplesperblock ;
+
+	return 1 ;
+} /* sds_2byte_write */
+
+static int
+sds_3byte_write (SF_PRIVATE *psf, SDS_PRIVATE *psds)
+{	unsigned char *ucptr, checksum ;
+	unsigned int sample ;
+	int 	k ;
+
+	psds->write_data [0] = 0xF0 ;
+	psds->write_data [1] = 0x7E ;
+	psds->write_data [2] = 0 ;							/* Channel number */
+	psds->write_data [3] = 2 ;
+	psds->write_data [4] = psds->write_block & 0x7F ;	/* Packet number */
+
+	ucptr = psds->write_data + 5 ;
+	for (k = 0 ; k < 120 ; k += 3)
+	{	sample = psds->write_samples [k / 3] ;
+		sample += 0x80000000 ;
+		ucptr [k] = (sample >> 25) & 0x7F ;
+		ucptr [k + 1] = (sample >> 18) & 0x7F ;
+		ucptr [k + 2] = (sample >> 11) & 0x7F ;
+		} ;
+
+	checksum = psds->write_data [1] ;
+	for (k = 2 ; k <= SDS_BLOCK_SIZE - 3 ; k ++)
+		checksum ^= psds->write_data [k] ;
+	checksum &= 0x7F ;
+
+	psds->write_data [SDS_BLOCK_SIZE - 2] = checksum ;
+	psds->write_data [SDS_BLOCK_SIZE - 1] = 0xF7 ;
+
+	if ((k = (int) psf_fwrite (psds->write_data, 1, SDS_BLOCK_SIZE, psf)) != SDS_BLOCK_SIZE)
+		psf_log_printf (psf, "*** Warning : psf_fwrite (%d != %d).\n", k, SDS_BLOCK_SIZE) ;
+
+	psds->write_block ++ ;
+	psds->write_count = 0 ;
+
+	if (psds->write_block > psds->total_blocks)
+		psds->total_blocks = psds->write_block ;
+	psds->frames = psds->total_blocks * psds->samplesperblock ;
+
+	return 1 ;
+} /* sds_3byte_write */
+
+static int
+sds_4byte_write (SF_PRIVATE *psf, SDS_PRIVATE *psds)
+{	unsigned char *ucptr, checksum ;
+	unsigned int sample ;
+	int 	k ;
+
+	psds->write_data [0] = 0xF0 ;
+	psds->write_data [1] = 0x7E ;
+	psds->write_data [2] = 0 ;							/* Channel number */
+	psds->write_data [3] = 2 ;
+	psds->write_data [4] = psds->write_block & 0x7F ;	/* Packet number */
+
+	ucptr = psds->write_data + 5 ;
+	for (k = 0 ; k < 120 ; k += 4)
+	{	sample = psds->write_samples [k / 4] ;
+		sample += 0x80000000 ;
+		ucptr [k] = (sample >> 25) & 0x7F ;
+		ucptr [k + 1] = (sample >> 18) & 0x7F ;
+		ucptr [k + 2] = (sample >> 11) & 0x7F ;
+		ucptr [k + 3] = (sample >> 4) & 0x7F ;
+		} ;
+
+	checksum = psds->write_data [1] ;
+	for (k = 2 ; k <= SDS_BLOCK_SIZE - 3 ; k ++)
+		checksum ^= psds->write_data [k] ;
+	checksum &= 0x7F ;
+
+	psds->write_data [SDS_BLOCK_SIZE - 2] = checksum ;
+	psds->write_data [SDS_BLOCK_SIZE - 1] = 0xF7 ;
+
+	if ((k = (int) psf_fwrite (psds->write_data, 1, SDS_BLOCK_SIZE, psf)) != SDS_BLOCK_SIZE)
+		psf_log_printf (psf, "*** Warning : psf_fwrite (%d != %d).\n", k, SDS_BLOCK_SIZE) ;
+
+	psds->write_block ++ ;
+	psds->write_count = 0 ;
+
+	if (psds->write_block > psds->total_blocks)
+		psds->total_blocks = psds->write_block ;
+	psds->frames = psds->total_blocks * psds->samplesperblock ;
+
+	return 1 ;
+} /* sds_4byte_write */
+
+static sf_count_t
+sds_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	SDS_PRIVATE	*psds ;
+	int			*iptr ;
+	int			k, bufferlen, writecount, count ;
+	sf_count_t	total = 0 ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	psds = (SDS_PRIVATE*) psf->codec_data ;
+	psds->total_written += len ;
+
+	iptr = ubuf.ibuf ;
+	bufferlen = ARRAY_LEN (ubuf.ibuf) ;
+	while (len > 0)
+	{	writecount = (len >= bufferlen) ? bufferlen : (int) len ;
+		for (k = 0 ; k < writecount ; k++)
+			iptr [k] = arith_shift_left (ptr [total + k], 16) ;
+		count = sds_write (psf, psds, iptr, writecount) ;
+		total += count ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* sds_write_s */
+
+static sf_count_t
+sds_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len)
+{	SDS_PRIVATE *psds ;
+	int			total ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	psds = (SDS_PRIVATE*) psf->codec_data ;
+	psds->total_written += len ;
+
+	total = sds_write (psf, psds, ptr, len) ;
+
+	return total ;
+} /* sds_write_i */
+
+static sf_count_t
+sds_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	SDS_PRIVATE	*psds ;
+	int			*iptr ;
+	int			k, bufferlen, writecount, count ;
+	sf_count_t	total = 0 ;
+	float		normfact ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	psds = (SDS_PRIVATE*) psf->codec_data ;
+	psds->total_written += len ;
+
+	if (psf->norm_float == SF_TRUE)
+		normfact = 1.0 * 0x80000000 ;
+	else
+		normfact = 1.0 * (1 << psds->bitwidth) ;
+
+	iptr = ubuf.ibuf ;
+	bufferlen = ARRAY_LEN (ubuf.ibuf) ;
+	while (len > 0)
+	{	writecount = (len >= bufferlen) ? bufferlen : (int) len ;
+		for (k = 0 ; k < writecount ; k++)
+			iptr [k] = normfact * ptr [total + k] ;
+		count = sds_write (psf, psds, iptr, writecount) ;
+		total += count ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* sds_write_f */
+
+static sf_count_t
+sds_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	SDS_PRIVATE	*psds ;
+	int			*iptr ;
+	int			k, bufferlen, writecount, count ;
+	sf_count_t	total = 0 ;
+	double		normfact ;
+
+	if (psf->codec_data == NULL)
+		return 0 ;
+	psds = (SDS_PRIVATE*) psf->codec_data ;
+	psds->total_written += len ;
+
+	if (psf->norm_double == SF_TRUE)
+		normfact = 1.0 * 0x80000000 ;
+	else
+		normfact = 1.0 * (1 << psds->bitwidth) ;
+
+	iptr = ubuf.ibuf ;
+	bufferlen = ARRAY_LEN (ubuf.ibuf) ;
+	while (len > 0)
+	{	writecount = (len >= bufferlen) ? bufferlen : (int) len ;
+		for (k = 0 ; k < writecount ; k++)
+			iptr [k] = normfact * ptr [total + k] ;
+		count = sds_write (psf, psds, iptr, writecount) ;
+		total += count ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* sds_write_d */
+
+static int
+sds_write (SF_PRIVATE *psf, SDS_PRIVATE *psds, const int *ptr, int len)
+{	int	count, total = 0 ;
+
+	while (total < len)
+	{	count = psds->samplesperblock - psds->write_count ;
+		if (count > len - total)
+			count = len - total ;
+
+		memcpy (&(psds->write_samples [psds->write_count]), &(ptr [total]), count * sizeof (int)) ;
+		total += count ;
+		psds->write_count += count ;
+
+		if (psds->write_count >= psds->samplesperblock)
+			psds->writer (psf, psds) ;
+		} ;
+
+	return total ;
+} /* sds_write */
+
diff --git a/src/sf_unistd.h b/src/sf_unistd.h
new file mode 100644
index 0000000..698eff2
--- /dev/null
+++ b/src/sf_unistd.h
@@ -0,0 +1,112 @@
+/*
+** Copyright (C) 2002-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/* Microsoft declares some 'unistd.h' functions in 'io.h'. */
+
+#include <sys/stat.h>
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
+
+/* Some defines that microsoft 'forgot' to implement. */
+
+#ifndef R_OK
+#define R_OK	4		/* Test for read permission.  */
+#endif
+
+#ifndef W_OK
+#define W_OK	2		/* Test for write permission.  */
+#endif
+
+#ifndef X_OK
+#ifdef _WIN32
+#define	X_OK	0
+#else
+#define	X_OK	1		/* execute permission - unsupported in windows*/
+#endif
+#endif
+
+#ifndef F_OK
+#define	F_OK	0		/* Test for existence.  */
+#endif
+
+#ifndef S_IRWXU
+#define	S_IRWXU 	0000700	/* rwx, owner */
+#endif
+
+#ifndef		S_IRUSR
+#define		S_IRUSR	0000400	/* read permission, owner */
+#endif
+
+#ifndef		S_IWUSR
+#define		S_IWUSR	0000200	/* write permission, owner */
+#endif
+
+#ifndef		S_IXUSR
+#define		S_IXUSR	0000100	/* execute/search permission, owner */
+#endif
+
+/* Windows (except MinGW) doesn't have group permissions so set all these to zero. */
+#ifndef S_IRWXG
+#define S_IRWXG		0	/* rwx, group */
+#endif
+
+#ifndef S_IRGRP
+#define S_IRGRP		0	/* read permission, group */
+#endif
+
+#ifndef S_IWGRP
+#define S_IWGRP		0	/* write permission, grougroup */
+#endif
+
+#ifndef S_IXGRP
+#define S_IXGRP		0	/* execute/search permission, group */
+#endif
+
+/* Windows (except MinGW) doesn't have others permissions so set all these to zero. */
+#ifndef S_IRWXO
+#define S_IRWXO		0	/* rwx, other */
+#endif
+
+#ifndef S_IROTH
+#define S_IROTH		0	/* read permission, other */
+#endif
+
+#ifndef S_IWOTH
+#define S_IWOTH		0	/* write permission, other */
+#endif
+
+#ifndef S_IXOTH
+#define S_IXOTH		0	/* execute/search permission, other */
+#endif
+
+#ifndef S_ISFIFO
+#define S_ISFIFO(mode)	(((mode) & _S_IFMT) == _S_IFIFO)
+#endif
+
+#ifndef S_ISREG
+#define	S_ISREG(mode)	(((mode) & _S_IFREG) == _S_IFREG)
+#endif
+
+/*
+**	Don't know if these are still needed.
+**
+**	#define	_IFMT		_S_IFMT
+**	#define _IFREG		_S_IFREG
+*/
+
diff --git a/src/sfconfig.h b/src/sfconfig.h
new file mode 100644
index 0000000..de2e56d
--- /dev/null
+++ b/src/sfconfig.h
@@ -0,0 +1,131 @@
+/*
+** Copyright (C) 2005-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*
+**	Autoconf leaves many config parameters undefined.
+**	Here we change then from being undefined to defining them to 0.
+**	This allows things like:
+**
+**		#if HAVE_CONFIG_PARAM
+**
+**	and
+**
+**		if (HAVE_CONFIG_PARAM)
+**			do_something () ;
+*/
+
+#ifndef SFCONFIG_H
+#define SFCONFIG_H
+
+/* Include the Autoconf generated file. */
+#include "config.h"
+
+/* Now fiddle the values. */
+
+#ifndef HAVE_ALSA_ASOUNDLIB_H
+#define HAVE_ALSA_ASOUNDLIB_H 0
+#endif
+
+#ifndef HAVE_BYTESWAP_H
+#define HAVE_BYTESWAP_H 0
+#endif
+
+#ifndef HAVE_DECL_S_IRGRP
+#define	HAVE_DECL_S_IRGRP 0
+#endif
+
+#ifndef HAVE_ENDIAN_H
+#define HAVE_ENDIAN_H 0
+#endif
+
+#ifndef HAVE_FSTAT64
+#define HAVE_FSTAT64 0
+#endif
+
+#ifndef HAVE_FSYNC
+#define HAVE_FSYNC 0
+#endif
+
+#ifndef HAVE_LOCALE_H
+#define HAVE_LOCALE_H 0
+#endif
+
+#ifndef HAVE_LRINT
+#define HAVE_LRINT 0
+#endif
+
+#ifndef HAVE_LRINTF
+#define HAVE_LRINTF 0
+#endif
+
+#ifndef HAVE_MMAP
+#define HAVE_MMAP 0
+#endif
+
+#ifndef HAVE_SETLOCALE
+#define HAVE_SETLOCALE 0
+#endif
+
+#ifndef HAVE_SQLITE3
+#define HAVE_SQLITE3 0
+#endif
+
+#ifndef HAVE_STDINT_H
+#define HAVE_STDINT_H 0
+#endif
+
+#ifndef HAVE_SYS_WAIT_H
+#define HAVE_SYS_WAIT_H 0
+#endif
+
+#ifndef HAVE_SYS_TIME_H
+#define HAVE_SYS_TIME_H 0
+#endif
+
+#ifndef HAVE_UNISTD_H
+#define HAVE_UNISTD_H 0
+#endif
+
+#ifndef HAVE_PIPE
+#define HAVE_PIPE 0
+#endif
+
+#ifndef HAVE_WAITPID
+#define	HAVE_WAITPID 0
+#endif
+
+#ifndef HAVE_X86INTRIN_H
+#define HAVE_X86INTRIN_H 0
+#endif
+
+#if (defined __x86_64__) || (defined _M_X64)
+#define CPU_IS_X86_64	1	/* Define both for x86_64 */
+#define CPU_IS_X86		1
+#elif defined (__i486__) || defined (__i586__) || defined (__i686__) || defined (_M_IX86)
+#define CPU_IS_X86 		1
+#define CPU_IS_X86_64 	0
+#else
+#define CPU_IS_X86		0
+#define CPU_IS_X86_64	0
+#endif
+
+#if (defined (__SSE2__) || defined (_M_AMD64) || (defined (_M_IX86_FP) && (_M_IX86_FP >= 2)) && HAVE_IMMINTRIN_H)
+#define USE_SSE2
+#endif
+
+#endif
diff --git a/src/sfendian.h b/src/sfendian.h
new file mode 100644
index 0000000..ab02742
--- /dev/null
+++ b/src/sfendian.h
@@ -0,0 +1,360 @@
+/*
+** Copyright (C) 1999-2018 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef SFENDIAN_INCLUDED
+#define SFENDIAN_INCLUDED
+
+#include "sfconfig.h"
+
+#include <stdint.h>
+#include <inttypes.h>
+
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+
+#if HAVE_BYTESWAP_H			/* Linux, any CPU */
+#include <byteswap.h>
+
+#define	ENDSWAP_16(x)		(bswap_16 (x))
+#define	ENDSWAP_32(x)		(bswap_32 (x))
+#define	ENDSWAP_64(x)		(bswap_64 (x))
+
+#elif __has_builtin(__builtin_bswap16) && __has_builtin(__builtin_bswap32) && __has_builtin(__builtin_bswap64)
+
+#define ENDSWAP_16(x) ((int16_t) __builtin_bswap16 ((uint16_t) x))
+#define ENDSWAP_32(x) ((int32_t) __builtin_bswap32 ((uint32_t) x))
+#define ENDSWAP_64(x) ((int64_t) __builtin_bswap64 ((uint64_t) x))
+
+#elif COMPILER_IS_GCC
+
+#if CPU_IS_X86
+
+static inline int16_t
+ENDSWAP_16X (int16_t x)
+{	int16_t y ;
+	__asm__ ("rorw $8, %w0" : "=r" (y) : "0" (x) : "cc") ;
+	return y ;
+} /* ENDSWAP_16 */
+
+static inline int32_t
+ENDSWAP_32X (int32_t x)
+{	int32_t y ;
+	__asm__ ("bswap %0" : "=r" (y) : "0" (x)) ;
+	return y ;
+} /* ENDSWAP_32 */
+
+#define ENDSWAP_16 ENDSWAP_16X
+#define ENDSWAP_32 ENDSWAP_32X
+
+#endif
+
+#if CPU_IS_X86_64
+
+static inline int64_t
+ENDSWAP_64X (int64_t x)
+{	int64_t y ;
+	__asm__ ("bswap %q0" : "=r" (y) : "0" (x)) ;
+	return y ;
+} /* ENDSWAP_64X */
+
+#define ENDSWAP_64 ENDSWAP_64X
+
+#endif
+
+#elif defined _MSC_VER
+#include <stdlib.h>
+
+#define	ENDSWAP_16(x)		(_byteswap_ushort (x))
+#define	ENDSWAP_32(x)		(_byteswap_ulong (x))
+#define	ENDSWAP_64(x)		(_byteswap_uint64 (x))
+
+#endif
+
+#ifndef ENDSWAP_16
+#define	ENDSWAP_16(x)		((((x) >> 8) & 0xFF) + (((x) & 0xFF) << 8))
+#endif
+
+#ifndef ENDSWAP_32
+#define	ENDSWAP_32(x)		((((x) >> 24) & 0xFF) + (((x) >> 8) & 0xFF00) + (((x) & 0xFF00) << 8) + (((x) & 0xFF) << 24))
+#endif
+
+#ifndef ENDSWAP_64
+static inline uint64_t
+ENDSWAP_64 (uint64_t x)
+{	union
+	{	uint32_t parts [2] ;
+		uint64_t whole ;
+	} u ;
+	uint32_t temp ;
+
+	u.whole = x ;
+	temp = u.parts [0] ;
+	u.parts [0] = ENDSWAP_32 (u.parts [1]) ;
+	u.parts [1] = ENDSWAP_32 (temp) ;
+	return u.whole ;
+}
+#endif
+
+/*
+** Many file types (ie WAV, AIFF) use sets of four consecutive bytes as a
+** marker indicating different sections of the file.
+** The following MAKE_MARKER macro allows th creation of integer constants
+** for these markers.
+*/
+
+#if (CPU_IS_LITTLE_ENDIAN == 1)
+	#define	MAKE_MARKER(a, b, c, d)		((uint32_t) ((a) | ((b) << 8) | ((c) << 16) | (((uint32_t) (d)) << 24)))
+#elif (CPU_IS_BIG_ENDIAN == 1)
+	#define	MAKE_MARKER(a, b, c, d)		((uint32_t) ((((uint32_t) (a)) << 24) | ((b) << 16) | ((c) << 8) | (d)))
+#else
+	#error "Target CPU endian-ness unknown. May need to hand edit src/sfconfig.h"
+#endif
+
+/*
+** Macros to handle reading of data of a specific endian-ness into host endian
+** shorts and ints. The single input is an unsigned char* pointer to the start
+** of the object. There are two versions of each macro as we need to deal with
+** both big and little endian CPUs.
+*/
+
+#if (CPU_IS_LITTLE_ENDIAN == 1)
+	#define LE2H_16(x)			(x)
+	#define LE2H_32(x)			(x)
+
+	#define BE2H_16(x)			ENDSWAP_16 (x)
+	#define BE2H_32(x)			ENDSWAP_32 (x)
+	#define BE2H_64(x)			ENDSWAP_64 (x)
+
+	#define H2BE_16(x)			ENDSWAP_16 (x)
+	#define H2BE_32(x)			ENDSWAP_32 (x)
+
+	#define H2LE_16(x)			(x)
+	#define H2LE_32(x)			(x)
+
+#elif (CPU_IS_BIG_ENDIAN == 1)
+	#define LE2H_16(x)			ENDSWAP_16 (x)
+	#define LE2H_32(x)			ENDSWAP_32 (x)
+
+	#define BE2H_16(x)			(x)
+	#define BE2H_32(x)			(x)
+	#define	BE2H_64(x)			(x)
+
+	#define H2BE_16(x)			(x)
+	#define H2BE_32(x)			(x)
+
+	#define H2LE_16(x)			ENDSWAP_16 (x)
+	#define H2LE_32(x)			ENDSWAP_32 (x)
+
+#else
+	#error "Target CPU endian-ness unknown. May need to hand edit src/sfconfig.h"
+#endif
+
+#define LE2H_32_PTR(x)			(((x) [0]) + ((x) [1] << 8) + ((x) [2] << 16) + ((x) [3] << 24))
+
+#define LET2H_16_PTR(x)			((x) [1] + ((x) [2] << 8))
+#define LET2H_32_PTR(x)			(((x) [0] << 8) + ((x) [1] << 16) + ((x) [2] << 24))
+
+#define BET2H_16_PTR(x)			(((x) [0] << 8) + (x) [1])
+#define BET2H_32_PTR(x)			(((x) [0] << 24) + ((x) [1] << 16) + ((x) [2] << 8))
+
+static inline void
+psf_put_be64 (uint8_t *ptr, int offset, int64_t value)
+{
+	ptr [offset] = (uint8_t) (value >> 56) ;
+	ptr [offset + 1] = (uint8_t) (value >> 48) ;
+	ptr [offset + 2] = (uint8_t) (value >> 40) ;
+	ptr [offset + 3] = (uint8_t) (value >> 32) ;
+	ptr [offset + 4] = (uint8_t) (value >> 24) ;
+	ptr [offset + 5] = (uint8_t) (value >> 16) ;
+	ptr [offset + 6] = (uint8_t) (value >> 8) ;
+	ptr [offset + 7] = (uint8_t) value ;
+} /* psf_put_be64 */
+
+static inline void
+psf_put_be32 (uint8_t *ptr, int offset, int32_t value)
+{
+	ptr [offset] = (uint8_t) (value >> 24) ;
+	ptr [offset + 1] = (uint8_t) (value >> 16) ;
+	ptr [offset + 2] = (uint8_t) (value >> 8) ;
+	ptr [offset + 3] = (uint8_t) value ;
+} /* psf_put_be32 */
+
+static inline void
+psf_put_be16 (uint8_t *ptr, int offset, int16_t value)
+{
+	ptr [offset] = (uint8_t) (value >> 8) ;
+	ptr [offset + 1] = (uint8_t) value ;
+} /* psf_put_be16 */
+
+static inline int64_t
+psf_get_be64 (const uint8_t *ptr, int offset)
+{	int64_t value ;
+
+	value = (int64_t) ((uint64_t) ptr [offset] << 24) ;
+	value += (int64_t) ((uint64_t) ptr [offset + 1] << 16) ;
+	value += (int64_t) ((uint64_t) ptr [offset + 2] << 8) ;
+	value += ptr [offset + 3] ;
+
+	value = (int64_t) (((uint64_t) value) << 32) ;
+
+	value += (int64_t) ((uint64_t) ptr [offset + 4] << 24) ;
+	value += (int64_t) ((uint64_t) ptr [offset + 5] << 16) ;
+	value += (int64_t) ((uint64_t) ptr [offset + 6] << 8) ;
+	value += ptr [offset + 7] ;
+	return value ;
+} /* psf_get_be64 */
+
+static inline int64_t
+psf_get_le64 (const uint8_t *ptr, int offset)
+{	int64_t value = (int64_t) ((uint64_t) ptr [offset + 7] << 24) ;
+	value += (int64_t) ((uint64_t) ptr [offset + 6] << 16) ;
+	value += (int64_t) ((uint64_t) ptr [offset + 5] << 8) ;
+	value += ptr [offset + 4] ;
+
+	value = (int64_t) (((uint64_t) value) << 32) ;
+
+	value += (int64_t) ((uint64_t) ptr [offset + 3] << 24) ;
+	value += (int64_t) ((uint64_t) ptr [offset + 2] << 16) ;
+	value += (int64_t) ((uint64_t) ptr [offset + 1] << 8) ;
+	value += ptr [offset] ;
+	return value ;
+} /* psf_get_le64 */
+
+static inline int32_t
+psf_get_be32 (const uint8_t *ptr, int offset)
+{	int32_t value = ((uint32_t) ptr [offset]) << 24 ;
+	value += ptr [offset + 1] << 16 ;
+	value += ptr [offset + 2] << 8 ;
+	value += ptr [offset + 3] ;
+	return value ;
+} /* psf_get_be32 */
+
+static inline int32_t
+psf_get_le32 (const uint8_t *ptr, int offset)
+{	int32_t value = ((uint32_t) ptr [offset + 3]) << 24 ;
+	value += ptr [offset + 2] << 16 ;
+	value += ptr [offset + 1] << 8 ;
+	value += ptr [offset] ;
+	return value ;
+} /* psf_get_le32 */
+
+static inline int32_t
+psf_get_be24 (const uint8_t *ptr, int offset)
+{	int32_t value = ((uint32_t) ptr [offset]) << 24 ;
+	value += ptr [offset + 1] << 16 ;
+	value += ptr [offset + 2] << 8 ;
+	return value ;
+} /* psf_get_be24 */
+
+static inline int32_t
+psf_get_le24 (const uint8_t *ptr, int offset)
+{	int32_t value = ((uint32_t) ptr [offset + 2]) << 24 ;
+	value += ptr [offset + 1] << 16 ;
+	value += ptr [offset] << 8 ;
+	return value ;
+} /* psf_get_le24 */
+
+static inline int16_t
+psf_get_be16 (const uint8_t *ptr, int offset)
+{	return (int16_t) (ptr [offset] << 8) + ptr [offset + 1] ;
+} /* psf_get_be16 */
+
+/*-----------------------------------------------------------------------------------------------
+** Generic functions for performing endian swapping on integer arrays.
+*/
+
+static inline void
+endswap_short_array (short *ptr, int len)
+{
+	for (int i = 0 ; i < len ; i++)
+	{	short temp = ptr [i] ;
+		ptr [i] = ENDSWAP_16 (temp) ;
+		} ;
+} /* endswap_short_array */
+
+static inline void
+endswap_short_copy (short *dest, const short *src, int len)
+{
+	for (int i = 0 ; i < len ; i++)
+	{	dest [i] = ENDSWAP_16 (src [i]) ;
+		} ;
+} /* endswap_short_copy */
+
+static inline void
+endswap_int_array (int *ptr, int len)
+{
+	for (int i = 0 ; i < len ; i++)
+	{	int temp = ptr [i] ;
+		ptr [i] = ENDSWAP_32 (temp) ;
+		} ;
+} /* endswap_int_array */
+
+static inline void
+endswap_int_copy (int *dest, const int *src, int len)
+{
+	for (int i = 0 ; i < len ; i++)
+	{	dest [i] = ENDSWAP_32 (src [i]) ;
+		} ;
+} /* endswap_int_copy */
+
+/*========================================================================================
+*/
+
+static inline void
+endswap_int64_t_array (int64_t *ptr, int len)
+{
+	for (int i = 0 ; i < len ; i++)
+	{	int64_t value = ptr [i] ;
+		ptr [i] = ENDSWAP_64 (value) ;
+		} ;
+} /* endswap_int64_t_array */
+
+static inline void
+endswap_int64_t_copy (int64_t *dest, const int64_t *src, int len)
+{
+	for (int i = 0 ; i < len ; i++)
+	{	int64_t value = src [i] ;
+		dest [i] = ENDSWAP_64 (value) ;
+		} ;
+} /* endswap_int64_t_copy */
+
+/* A couple of wrapper functions. */
+
+static inline void
+endswap_float_array (float *ptr, int len)
+{	endswap_int_array ((int *) ptr, len) ;
+} /* endswap_float_array */
+
+static inline void
+endswap_double_array (double *ptr, int len)
+{	endswap_int64_t_array ((int64_t *) ptr, len) ;
+} /* endswap_double_array */
+
+static inline void
+endswap_float_copy (float *dest, const float *src, int len)
+{	endswap_int_copy ((int *) dest, (const int *) src, len) ;
+} /* endswap_float_copy */
+
+static inline void
+endswap_double_copy (double *dest, const double *src, int len)
+{	endswap_int64_t_copy ((int64_t *) dest, (const int64_t *) src, len) ;
+} /* endswap_double_copy */
+
+#endif /* SFENDIAN_INCLUDED */
+
diff --git a/src/sndfile.c b/src/sndfile.c
new file mode 100644
index 0000000..9a6c622
--- /dev/null
+++ b/src/sndfile.c
@@ -0,0 +1,3450 @@
+/*
+** Copyright (C) 1999-2018 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdlib.h>
+#include	<string.h>
+#include	<ctype.h>
+#include	<assert.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#elif defined _WIN32
+#include <io.h>
+#endif
+
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#endif
+
+#define		SNDFILE_MAGICK	0x1234C0DE
+
+#ifdef __APPLE__
+	/*
+	**	Detect if a compile for a universal binary is being attempted and barf if it is.
+	**	See the URL below for the rationale.
+	*/
+	#ifdef __BIG_ENDIAN__
+		#if (CPU_IS_LITTLE_ENDIAN == 1)
+			#error "Universal binary compile detected. See http://libsndfile.github.io/libsndfile/FAQ.html#Q018"
+		#endif
+	#endif
+
+	#ifdef __LITTLE_ENDIAN__
+		#if (CPU_IS_BIG_ENDIAN == 1)
+			#error "Universal binary compile detected. See http://libsndfile.github.io/libsndfile/FAQ.html#Q018"
+		#endif
+	#endif
+#endif
+
+
+typedef struct
+{	int 		error ;
+	const char	*str ;
+} ErrorStruct ;
+
+static
+ErrorStruct SndfileErrors [] =
+{
+	/* Public error values and their associated strings. */
+	{	SF_ERR_NO_ERROR				, "No Error." },
+	{	SF_ERR_UNRECOGNISED_FORMAT	, "Format not recognised." },
+	{	SF_ERR_SYSTEM				, "System error." /* Often replaced. */ 	},
+	{	SF_ERR_MALFORMED_FILE		, "Supported file format but file is malformed." },
+	{	SF_ERR_UNSUPPORTED_ENCODING	, "Supported file format but unsupported encoding." },
+
+	/* Private error values and their associated strings. */
+	{	SFE_ZERO_MAJOR_FORMAT	, "Error : major format is 0." },
+	{	SFE_ZERO_MINOR_FORMAT	, "Error : minor format is 0." },
+	{	SFE_BAD_FILE			, "File does not exist or is not a regular file (possibly a pipe?)." },
+	{	SFE_BAD_FILE_READ		, "File exists but no data could be read." },
+	{	SFE_OPEN_FAILED			, "Could not open file." },
+	{	SFE_BAD_SNDFILE_PTR		, "Not a valid SNDFILE* pointer." },
+	{	SFE_BAD_SF_INFO_PTR		, "NULL SF_INFO pointer passed to libsndfile." },
+	{	SFE_BAD_SF_INCOMPLETE	, "SF_PRIVATE struct incomplete and end of header parsing." },
+	{	SFE_BAD_FILE_PTR		, "Bad FILE pointer." },
+	{	SFE_BAD_INT_PTR			, "Internal error, Bad pointer." },
+	{	SFE_BAD_STAT_SIZE		, "Error : software was misconfigured at compile time (sizeof statbuf.st_size)." },
+	{	SFE_NO_TEMP_DIR			, "Error : Could not file temp dir." },
+
+	{	SFE_MALLOC_FAILED		, "Internal malloc () failed." },
+	{	SFE_UNIMPLEMENTED		, "File contains data in an unimplemented format." },
+	{	SFE_BAD_READ_ALIGN		, "Attempt to read a non-integer number of channels." },
+	{	SFE_BAD_WRITE_ALIGN 	, "Attempt to write a non-integer number of channels." },
+	{	SFE_NOT_READMODE		, "Read attempted on file currently open for write." },
+	{	SFE_NOT_WRITEMODE		, "Write attempted on file currently open for read." },
+	{	SFE_BAD_MODE_RW			, "Error : This file format does not support read/write mode." },
+	{	SFE_BAD_SF_INFO			, "Internal error : SF_INFO struct incomplete." },
+	{	SFE_BAD_OFFSET			, "Error : supplied offset beyond end of file." },
+	{	SFE_NO_EMBED_SUPPORT	, "Error : embedding not supported for this file format." },
+	{	SFE_NO_EMBEDDED_RDWR	, "Error : cannot open embedded file read/write." },
+	{	SFE_NO_PIPE_WRITE		, "Error : this file format does not support pipe write." },
+	{	SFE_BAD_VIRTUAL_IO		, "Error : bad pointer on SF_VIRTUAL_IO struct." },
+	{	SFE_BAD_BROADCAST_INFO_SIZE
+								, "Error : bad coding_history_size in SF_BROADCAST_INFO struct." },
+	{	SFE_BAD_BROADCAST_INFO_TOO_BIG
+								, "Error : SF_BROADCAST_INFO struct too large." },
+	{	SFE_BAD_CART_INFO_SIZE				, "Error: SF_CART_INFO struct too large." },
+	{	SFE_BAD_CART_INFO_TOO_BIG			, "Error: bad tag_text_size in SF_CART_INFO struct." },
+	{	SFE_INTERLEAVE_MODE		, "Attempt to write to file with non-interleaved data." },
+	{	SFE_INTERLEAVE_SEEK		, "Bad karma in seek during interleave read operation." },
+	{	SFE_INTERLEAVE_READ		, "Bad karma in read during interleave read operation." },
+
+	{	SFE_INTERNAL			, "Unspecified internal error." },
+	{	SFE_BAD_COMMAND_PARAM	, "Bad parameter passed to function sf_command." },
+	{	SFE_BAD_ENDIAN			, "Bad endian-ness. Try default endian-ness" },
+	{	SFE_CHANNEL_COUNT_ZERO	, "Channel count is zero." },
+	{	SFE_CHANNEL_COUNT		, "Too many channels specified." },
+	{	SFE_CHANNEL_COUNT_BAD	, "Bad channel count." },
+
+	{	SFE_BAD_SEEK			, "Internal psf_fseek() failed." },
+	{	SFE_NOT_SEEKABLE		, "Seek attempted on unseekable file type." },
+	{	SFE_AMBIGUOUS_SEEK		, "Error : combination of file open mode and seek command is ambiguous." },
+	{	SFE_WRONG_SEEK			, "Error : invalid seek parameters." },
+	{	SFE_SEEK_FAILED			, "Error : parameters OK, but psf_seek() failed." },
+
+	{	SFE_BAD_OPEN_MODE		, "Error : bad mode parameter for file open." },
+	{	SFE_OPEN_PIPE_RDWR		, "Error : attempt to open a pipe in read/write mode." },
+	{	SFE_RDWR_POSITION		, "Error on RDWR position (cryptic)." },
+	{	SFE_RDWR_BAD_HEADER		, "Error : Cannot open file in read/write mode due to string data in header." },
+	{	SFE_CMD_HAS_DATA		, "Error : Command fails because file already has audio data." },
+
+	{	SFE_STR_NO_SUPPORT		, "Error : File type does not support string data." },
+	{	SFE_STR_NOT_WRITE		, "Error : Trying to set a string when file is not in write mode." },
+	{	SFE_STR_MAX_DATA		, "Error : Maximum string data storage reached." },
+	{	SFE_STR_MAX_COUNT		, "Error : Maximum string data count reached." },
+	{	SFE_STR_BAD_TYPE		, "Error : Bad string data type." },
+	{	SFE_STR_NO_ADD_END		, "Error : file type does not support strings added at end of file." },
+	{	SFE_STR_BAD_STRING		, "Error : bad string." },
+	{	SFE_STR_WEIRD			, "Error : Weird string error." },
+
+	{	SFE_WAV_NO_RIFF			, "Error in WAV file. No 'RIFF' chunk marker." },
+	{	SFE_WAV_NO_WAVE			, "Error in WAV file. No 'WAVE' chunk marker." },
+	{	SFE_WAV_NO_FMT			, "Error in WAV/W64/RF64 file. No 'fmt ' chunk marker." },
+	{	SFE_WAV_BAD_FMT			, "Error in WAV/W64/RF64 file. Malformed 'fmt ' chunk." },
+	{	SFE_WAV_FMT_SHORT		, "Error in WAV/W64/RF64 file. Short 'fmt ' chunk." },
+
+	{	SFE_WAV_BAD_FACT		, "Error in WAV file. 'fact' chunk out of place." },
+	{	SFE_WAV_BAD_PEAK		, "Error in WAV file. Bad 'PEAK' chunk." },
+	{	SFE_WAV_PEAK_B4_FMT		, "Error in WAV file. 'PEAK' chunk found before 'fmt ' chunk." },
+
+	{	SFE_WAV_BAD_FORMAT		, "Error in WAV file. Errors in 'fmt ' chunk." },
+	{	SFE_WAV_BAD_BLOCKALIGN	, "Error in WAV file. Block alignment in 'fmt ' chunk is incorrect." },
+	{	SFE_WAV_NO_DATA			, "Error in WAV file. No 'data' chunk marker." },
+	{	SFE_WAV_BAD_LIST		, "Error in WAV file. Malformed LIST chunk." },
+	{	SFE_WAV_UNKNOWN_CHUNK	, "Error in WAV file. File contains an unknown chunk marker." },
+	{	SFE_WAV_WVPK_DATA		, "Error in WAV file. Data is in WAVPACK format." },
+
+	{	SFE_WAV_ADPCM_NOT4BIT	, "Error in ADPCM WAV file. Invalid bit width." },
+	{	SFE_WAV_ADPCM_CHANNELS	, "Error in ADPCM WAV file. Invalid number of channels." },
+	{	SFE_WAV_ADPCM_SAMPLES	, "Error in ADPCM WAV file. Invalid number of samples per block." },
+	{	SFE_WAV_GSM610_FORMAT	, "Error in GSM610 WAV file. Invalid format chunk." },
+	{	SFE_WAV_NMS_FORMAT		, "Error in NMS ADPCM WAV file. Invalid format chunk." },
+
+	{	SFE_AIFF_NO_FORM		, "Error in AIFF file, bad 'FORM' marker." },
+	{	SFE_AIFF_AIFF_NO_FORM	, "Error in AIFF file, 'AIFF' marker without 'FORM'." },
+	{	SFE_AIFF_COMM_NO_FORM	, "Error in AIFF file, 'COMM' marker without 'FORM'." },
+	{	SFE_AIFF_SSND_NO_COMM	, "Error in AIFF file, 'SSND' marker without 'COMM'." },
+	{	SFE_AIFF_UNKNOWN_CHUNK	, "Error in AIFF file, unknown chunk." },
+	{	SFE_AIFF_COMM_CHUNK_SIZE, "Error in AIFF file, bad 'COMM' chunk size." },
+	{	SFE_AIFF_BAD_COMM_CHUNK , "Error in AIFF file, bad 'COMM' chunk." },
+	{	SFE_AIFF_PEAK_B4_COMM	, "Error in AIFF file. 'PEAK' chunk found before 'COMM' chunk." },
+	{	SFE_AIFF_BAD_PEAK		, "Error in AIFF file. Bad 'PEAK' chunk." },
+	{	SFE_AIFF_NO_SSND		, "Error in AIFF file, bad 'SSND' chunk." },
+	{	SFE_AIFF_NO_DATA		, "Error in AIFF file, no sound data." },
+	{	SFE_AIFF_RW_SSND_NOT_LAST, "Error in AIFF file, RDWR only possible if SSND chunk at end of file." },
+
+	{	SFE_AU_UNKNOWN_FORMAT	, "Error in AU file, unknown format." },
+	{	SFE_AU_NO_DOTSND		, "Error in AU file, missing '.snd' or 'dns.' marker." },
+	{	SFE_AU_EMBED_BAD_LEN	, "Embedded AU file with unknown length." },
+
+	{	SFE_RAW_READ_BAD_SPEC	, "Error while opening RAW file for read. Must specify format and channels.\n"
+									"Possibly trying to open unsupported format." },
+	{	SFE_RAW_BAD_BITWIDTH	, "Error. RAW file bitwidth must be a multiple of 8." },
+	{	SFE_RAW_BAD_FORMAT		, "Error. Bad format field in SF_INFO struct when opening a RAW file for read." },
+
+	{	SFE_PAF_NO_MARKER		, "Error in PAF file, no marker." },
+	{	SFE_PAF_VERSION			, "Error in PAF file, bad version." },
+	{	SFE_PAF_UNKNOWN_FORMAT	, "Error in PAF file, unknown format." },
+	{	SFE_PAF_SHORT_HEADER	, "Error in PAF file. File shorter than minimal header." },
+	{	SFE_PAF_BAD_CHANNELS	, "Error in PAF file. Bad channel count." },
+
+	{	SFE_SVX_NO_FORM			, "Error in 8SVX / 16SV file, no 'FORM' marker." },
+	{	SFE_SVX_NO_BODY			, "Error in 8SVX / 16SV file, no 'BODY' marker." },
+	{	SFE_SVX_NO_DATA			, "Error in 8SVX / 16SV file, no sound data." },
+	{	SFE_SVX_BAD_COMP		, "Error in 8SVX / 16SV file, unsupported compression format." },
+	{	SFE_SVX_BAD_NAME_LENGTH	, "Error in 8SVX / 16SV file, NAME chunk too long." },
+
+	{	SFE_NIST_BAD_HEADER		, "Error in NIST file, bad header." },
+	{	SFE_NIST_CRLF_CONVERISON, "Error : NIST file damaged by Windows CR -> CRLF conversion process."	},
+	{	SFE_NIST_BAD_ENCODING	, "Error in NIST file, unsupported compression format." },
+
+	{	SFE_VOC_NO_CREATIVE		, "Error in VOC file, no 'Creative Voice File' marker." },
+	{	SFE_VOC_BAD_FORMAT		, "Error in VOC file, bad format." },
+	{	SFE_VOC_BAD_VERSION		, "Error in VOC file, bad version number." },
+	{	SFE_VOC_BAD_MARKER		, "Error in VOC file, bad marker in file." },
+	{	SFE_VOC_BAD_SECTIONS	, "Error in VOC file, incompatible VOC sections." },
+	{	SFE_VOC_MULTI_SAMPLERATE, "Error in VOC file, more than one sample rate defined." },
+	{	SFE_VOC_MULTI_SECTION	, "Unimplemented VOC file feature, file contains multiple sound sections." },
+	{	SFE_VOC_MULTI_PARAM		, "Error in VOC file, file contains multiple bit or channel widths." },
+	{	SFE_VOC_SECTION_COUNT	, "Error in VOC file, too many sections." },
+	{	SFE_VOC_NO_PIPE			, "Error : not able to operate on VOC files over a pipe." },
+
+	{	SFE_IRCAM_NO_MARKER		, "Error in IRCAM file, bad IRCAM marker." },
+	{	SFE_IRCAM_BAD_CHANNELS	, "Error in IRCAM file, bad channel count." },
+	{	SFE_IRCAM_UNKNOWN_FORMAT, "Error in IRCAM file, unknown encoding format." },
+
+	{	SFE_W64_64_BIT			, "Error in W64 file, file contains 64 bit offset." },
+	{	SFE_W64_NO_RIFF			, "Error in W64 file. No 'riff' chunk marker." },
+	{	SFE_W64_NO_WAVE			, "Error in W64 file. No 'wave' chunk marker." },
+	{	SFE_W64_NO_DATA			, "Error in W64 file. No 'data' chunk marker." },
+	{	SFE_W64_ADPCM_NOT4BIT	, "Error in ADPCM W64 file. Invalid bit width." },
+	{	SFE_W64_ADPCM_CHANNELS	, "Error in ADPCM W64 file. Invalid number of channels." },
+	{	SFE_W64_GSM610_FORMAT	, "Error in GSM610 W64 file. Invalid format chunk." },
+
+	{	SFE_MAT4_BAD_NAME		, "Error in MAT4 file. No variable name." },
+	{	SFE_MAT4_NO_SAMPLERATE	, "Error in MAT4 file. No sample rate." },
+
+	{	SFE_MAT5_BAD_ENDIAN		, "Error in MAT5 file. Not able to determine endian-ness." },
+	{	SFE_MAT5_NO_BLOCK		, "Error in MAT5 file. Bad block structure." },
+	{	SFE_MAT5_SAMPLE_RATE	, "Error in MAT5 file. Not able to determine sample rate." },
+
+	{	SFE_PVF_NO_PVF1			, "Error in PVF file. No PVF1 marker." },
+	{	SFE_PVF_BAD_HEADER		, "Error in PVF file. Bad header." },
+	{	SFE_PVF_BAD_BITWIDTH	, "Error in PVF file. Bad bit width." },
+
+	{	SFE_XI_BAD_HEADER		, "Error in XI file. Bad header." },
+	{	SFE_XI_EXCESS_SAMPLES	, "Error in XI file. Excess samples in file." },
+	{	SFE_XI_NO_PIPE			, "Error : not able to operate on XI files over a pipe." },
+
+	{	SFE_HTK_NO_PIPE			, "Error : not able to operate on HTK files over a pipe." },
+
+	{	SFE_SDS_NOT_SDS			, "Error : not an SDS file." },
+	{	SFE_SDS_BAD_BIT_WIDTH	, "Error : bad bit width for SDS file." },
+
+	{	SFE_SD2_FD_DISALLOWED	, "Error : cannot open SD2 file without a file name." },
+	{	SFE_SD2_BAD_DATA_OFFSET	, "Error : bad data offset." },
+	{	SFE_SD2_BAD_MAP_OFFSET	, "Error : bad map offset." },
+	{	SFE_SD2_BAD_DATA_LENGTH	, "Error : bad data length." },
+	{	SFE_SD2_BAD_MAP_LENGTH	, "Error : bad map length." },
+	{	SFE_SD2_BAD_RSRC		, "Error : bad resource fork." },
+	{	SFE_SD2_BAD_SAMPLE_SIZE	, "Error : bad sample size." },
+
+	{	SFE_FLAC_BAD_HEADER		, "Error : bad flac header." },
+	{	SFE_FLAC_NEW_DECODER	, "Error : problem while creating flac decoder." },
+	{	SFE_FLAC_INIT_DECODER	, "Error : problem with initialization of the flac decoder." },
+	{	SFE_FLAC_LOST_SYNC		, "Error : flac decoder lost sync." },
+	{	SFE_FLAC_BAD_SAMPLE_RATE, "Error : flac does not support this sample rate." },
+	{	SFE_FLAC_CHANNEL_COUNT_CHANGED, "Error : flac channel changed mid stream." },
+	{	SFE_FLAC_UNKOWN_ERROR	, "Error : unknown error in flac decoder." },
+
+	{	SFE_WVE_NOT_WVE			, "Error : not a WVE file." },
+	{	SFE_WVE_NO_PIPE			, "Error : not able to operate on WVE files over a pipe." },
+
+	{	SFE_DWVW_BAD_BITWIDTH	, "Error : Bad bit width for DWVW encoding. Must be 12, 16 or 24." },
+	{	SFE_G72X_NOT_MONO		, "Error : G72x encoding does not support more than 1 channel." },
+	{	SFE_NMS_ADPCM_NOT_MONO	, "Error : NMS ADPCM encoding does not support more than 1 channel." },
+
+	{	SFE_VORBIS_ENCODER_BUG	, "Error : Sample rate chosen is known to trigger a Vorbis encoder bug on this CPU." },
+
+	{	SFE_RF64_NOT_RF64		, "Error : Not an RF64 file." },
+	{	SFE_RF64_PEAK_B4_FMT	, "Error in RF64 file. 'PEAK' chunk found before 'fmt ' chunk." },
+	{	SFE_RF64_NO_DATA		, "Error in RF64 file. No 'data' chunk marker." },
+
+	{	SFE_ALAC_FAIL_TMPFILE	, "Error : Failed to open tmp file for ALAC encoding." },
+
+	{	SFE_BAD_CHUNK_PTR		, "Error : Bad SF_CHUNK_INFO pointer." },
+	{	SFE_UNKNOWN_CHUNK		, "Error : Unknown chunk marker." },
+	{	SFE_BAD_CHUNK_FORMAT	, "Error : Reading/writing chunks from this file format is not supported." },
+	{	SFE_BAD_CHUNK_MARKER	, "Error : Bad chunk marker." },
+	{	SFE_BAD_CHUNK_DATA_PTR	, "Error : Bad data pointer in SF_CHUNK_INFO struct." },
+	{	SFE_FILENAME_TOO_LONG	, "Error : Supplied filename too long." },
+	{	SFE_NEGATIVE_RW_LEN		, "Error : Length parameter passed to read/write is negative." },
+
+	{	SFE_OPUS_BAD_SAMPLERATE	, "Error : Opus only supports sample rates of 8000, 12000, 16000, 24000, and 48000." },
+
+	{	SFE_MPEG_BAD_SAMPLERATE	, "Error : MPEG-1/2/2.5 only supports sample rates of 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, and 48000." },
+
+	{	SFE_CAF_NOT_CAF			, "Error : Not a CAF file." },
+	{	SFE_CAF_NO_DESC			, "Error : No 'desc' marker in CAF file." },
+	{	SFE_CAF_BAD_PEAK		, "Error : Bad 'PEAK' chunk in CAF file." },
+
+	{	SFE_AVR_NOT_AVR			, "Error : Not an AVR file." },
+	{	SFE_AVR_BAD_REZ_SIGN	, "Error : Bad rez/sign combination." },
+
+	{	SFE_MPC_NO_MARKER		, "Error : No marker in MPC2K file." },
+
+	{	SFE_MAX_ERROR			, "Maximum error number." },
+	{	SFE_MAX_ERROR + 1		, NULL }
+} ;
+
+/*------------------------------------------------------------------------------
+*/
+
+static int 	format_from_extension (SF_PRIVATE *psf) ;
+static int	guess_file_type (SF_PRIVATE *psf) ;
+static int	validate_sfinfo (SF_INFO *sfinfo) ;
+static int	validate_psf (SF_PRIVATE *psf) ;
+static void	save_header_info (SF_PRIVATE *psf) ;
+static int	psf_close (SF_PRIVATE *psf) ;
+
+static int	try_resource_fork (SF_PRIVATE * psf) ;
+
+/*------------------------------------------------------------------------------
+** Private (static) variables.
+*/
+
+int	sf_errno = 0 ;
+static char	sf_parselog [SF_BUFFER_LEN] = { 0 } ;
+static char	sf_syserr [SF_SYSERR_LEN] = { 0 } ;
+
+/*------------------------------------------------------------------------------
+*/
+
+#define	VALIDATE_SNDFILE_AND_ASSIGN_PSF(a, b, c)	\
+		{	if ((a) == NULL)						\
+			{	sf_errno = SFE_BAD_SNDFILE_PTR ;	\
+				return 0 ;							\
+				} ;									\
+			(b) = (SF_PRIVATE*) (a) ;				\
+			if ((b)->virtual_io == SF_FALSE &&		\
+				psf_file_valid (b) == 0)			\
+			{	(b)->error = SFE_BAD_FILE_PTR ;		\
+				return 0 ;							\
+				} ;									\
+			if ((b)->Magick != SNDFILE_MAGICK)		\
+			{	(b)->error = SFE_BAD_SNDFILE_PTR ;	\
+				return 0 ;							\
+				} ;									\
+			if (c) (b)->error = 0 ;					\
+			}
+
+/*------------------------------------------------------------------------------
+**	Public functions.
+*/
+
+SNDFILE*
+sf_open	(const char *path, int mode, SF_INFO *sfinfo)
+{	SF_PRIVATE 	*psf ;
+	const char *utf8path_ptr ;
+#ifdef _WIN32
+	LPWSTR wpath ;
+	int nResult ;
+	int wpath_len ;
+	char utf8path [SF_FILENAME_LEN] ;
+	DWORD dwError ;
+#endif
+
+	/* Ultimate sanity check. */
+	assert (sizeof (sf_count_t) == 8) ;
+
+	if ((psf = psf_allocate ()) == NULL)
+	{	sf_errno = SFE_MALLOC_FAILED ;
+		return	NULL ;
+		} ;
+
+	psf_init_files (psf) ;
+
+	psf_log_printf (psf, "File : %s\n", path) ;
+
+#ifdef _WIN32
+	nResult = MultiByteToWideChar (CP_ACP, 0, path, -1, NULL, 0) ;
+	if (nResult == 0)
+	{	sf_errno = SF_ERR_UNSUPPORTED_ENCODING ;
+		psf_close (psf) ;
+		return NULL ;
+		} ;
+
+	wpath_len = nResult ;
+	wpath = malloc (wpath_len * sizeof (WCHAR)) ;
+	if (!wpath)
+	{	sf_errno = SFE_MALLOC_FAILED ;
+		psf_close (psf) ;
+		return NULL ;
+		} ;
+
+	nResult = MultiByteToWideChar (CP_ACP, 0, path, -1, wpath, wpath_len) ;
+	if (nResult == 0)
+	{	sf_errno = SF_ERR_UNSUPPORTED_ENCODING ;
+		free (wpath) ;
+		psf_close (psf) ;
+		return NULL ;
+		} ;
+
+	nResult = WideCharToMultiByte (CP_UTF8, 0, wpath, wpath_len, NULL, 0, NULL,
+		NULL) ;
+	if (nResult == 0)
+	{	sf_errno = SF_ERR_UNSUPPORTED_ENCODING ;
+		free (wpath) ;
+		psf_close (psf) ;
+		return NULL ;
+		} ;
+
+	nResult = WideCharToMultiByte (CP_UTF8, 0, wpath, wpath_len, utf8path,
+		SF_FILENAME_LEN, NULL, NULL) ;
+
+	free (wpath) ;
+
+	if (nResult == 0)
+	{	dwError = GetLastError () ;
+		if (dwError == ERROR_INSUFFICIENT_BUFFER)
+			sf_errno = SFE_FILENAME_TOO_LONG ;
+		else
+			sf_errno = SF_ERR_UNSUPPORTED_ENCODING ;
+		psf_close (psf) ;
+		return NULL ;
+		} ;
+
+	utf8path_ptr = utf8path ;
+#else
+	utf8path_ptr = path ;
+#endif
+
+	if (psf_copy_filename (psf, utf8path_ptr) != 0)
+	{	sf_errno = psf->error ;
+		psf_close (psf) ;
+		return	NULL ;
+		} ;
+
+	psf->file.mode = mode ;
+	if (strcmp (path, "-") == 0)
+		psf->error = psf_set_stdio (psf) ;
+	else
+		psf->error = psf_fopen (psf) ;
+
+	return psf_open_file (psf, sfinfo) ;
+} /* sf_open */
+
+SNDFILE*
+sf_open_fd	(int fd, int mode, SF_INFO *sfinfo, int close_desc)
+{	SF_PRIVATE 	*psf ;
+
+	if ((SF_CONTAINER (sfinfo->format)) == SF_FORMAT_SD2)
+	{	sf_errno = SFE_SD2_FD_DISALLOWED ;
+		if (close_desc)
+			close (fd) ;
+
+		return	NULL ;
+		} ;
+
+	if ((psf = psf_allocate ()) == NULL)
+	{	sf_errno = SFE_MALLOC_FAILED ;
+		if (close_desc)
+			close (fd) ;
+
+		return	NULL ;
+		} ;
+
+	psf_init_files (psf) ;
+	psf_copy_filename (psf, "") ;
+
+	psf->file.mode = mode ;
+	psf->file.do_not_close_descriptor = !close_desc;
+	psf_set_file (psf, fd) ;
+	psf->is_pipe = psf_is_pipe (psf) ;
+	psf->fileoffset = psf_ftell (psf) ;
+
+	return psf_open_file (psf, sfinfo) ;
+} /* sf_open_fd */
+
+SNDFILE*
+sf_open_virtual	(SF_VIRTUAL_IO *sfvirtual, int mode, SF_INFO *sfinfo, void *user_data)
+{	SF_PRIVATE 	*psf ;
+
+	/* Make sure we have a valid set ot virtual pointers. */
+	if (sfvirtual->get_filelen == NULL)
+	{	sf_errno = SFE_BAD_VIRTUAL_IO ;
+		snprintf (sf_parselog, sizeof (sf_parselog), "Bad vio_get_filelen in SF_VIRTUAL_IO struct.\n") ;
+		return NULL ;
+		} ;
+
+	if ((sfvirtual->seek == NULL || sfvirtual->tell == NULL) && sfinfo->seekable)
+	{	sf_errno = SFE_BAD_VIRTUAL_IO ;
+		snprintf (sf_parselog, sizeof (sf_parselog), "Bad vio_seek / vio_tell in SF_VIRTUAL_IO struct.\n") ;
+		return NULL ;
+		} ;
+
+	if ((mode == SFM_READ || mode == SFM_RDWR) && sfvirtual->read == NULL)
+	{	sf_errno = SFE_BAD_VIRTUAL_IO ;
+		snprintf (sf_parselog, sizeof (sf_parselog), "Bad vio_read in SF_VIRTUAL_IO struct.\n") ;
+		return NULL ;
+		} ;
+
+	if ((mode == SFM_WRITE || mode == SFM_RDWR) && sfvirtual->write == NULL)
+	{	sf_errno = SFE_BAD_VIRTUAL_IO ;
+		snprintf (sf_parselog, sizeof (sf_parselog), "Bad vio_write in SF_VIRTUAL_IO struct.\n") ;
+		return NULL ;
+		} ;
+
+	if ((psf = psf_allocate ()) == NULL)
+	{	sf_errno = SFE_MALLOC_FAILED ;
+		return	NULL ;
+		} ;
+
+	psf_init_files (psf) ;
+
+	psf->virtual_io = SF_TRUE ;
+	psf->vio = *sfvirtual ;
+	psf->vio_user_data = user_data ;
+
+	psf->file.mode = mode ;
+
+	return psf_open_file (psf, sfinfo) ;
+} /* sf_open_virtual */
+
+int
+sf_close	(SNDFILE *sndfile)
+{	SF_PRIVATE	*psf ;
+
+	VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ;
+
+	return psf_close (psf) ;
+} /* sf_close */
+
+void
+sf_write_sync	(SNDFILE *sndfile)
+{	SF_PRIVATE	*psf ;
+
+	if ((psf = (SF_PRIVATE *) sndfile) == NULL)
+		return ;
+
+	psf_fsync (psf) ;
+
+	return ;
+} /* sf_write_sync */
+
+/*==============================================================================
+*/
+
+const char*
+sf_error_number	(int errnum)
+{	static const char *bad_errnum =
+		"No error defined for this error number. This is a bug in libsndfile." ;
+	int	k ;
+
+	if (errnum == SFE_MAX_ERROR)
+		return SndfileErrors [0].str ;
+
+	if (errnum < 0 || errnum > SFE_MAX_ERROR)
+	{	/* This really shouldn't happen in release versions. */
+		printf ("Not a valid error number (%d).\n", errnum) ;
+		return bad_errnum ;
+		} ;
+
+	for (k = 0 ; SndfileErrors [k].str ; k++)
+		if (errnum == SndfileErrors [k].error)
+			return SndfileErrors [k].str ;
+
+	return bad_errnum ;
+} /* sf_error_number */
+
+const char*
+sf_strerror (SNDFILE *sndfile)
+{	SF_PRIVATE 	*psf = NULL ;
+	int errnum ;
+
+	if (sndfile == NULL)
+	{	errnum = sf_errno ;
+		if (errnum == SFE_SYSTEM && sf_syserr [0])
+			return sf_syserr ;
+		}
+	else
+	{	psf = (SF_PRIVATE *) sndfile ;
+
+		if (psf->Magick != SNDFILE_MAGICK)
+			return	"sf_strerror : Bad magic number." ;
+
+		errnum = psf->error ;
+
+		if (errnum == SFE_SYSTEM && psf->syserr [0])
+			return psf->syserr ;
+		} ;
+
+	return sf_error_number (errnum) ;
+} /* sf_strerror */
+
+/*------------------------------------------------------------------------------
+*/
+
+int
+sf_error (SNDFILE *sndfile)
+{	SF_PRIVATE	*psf ;
+
+	if (sndfile == NULL)
+		return sf_errno ;
+
+	VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 0) ;
+
+	if (psf->error)
+		return psf->error ;
+
+	return 0 ;
+} /* sf_error */
+
+/*------------------------------------------------------------------------------
+*/
+
+int
+sf_perror (SNDFILE *sndfile)
+{	SF_PRIVATE 	*psf ;
+	int 		errnum ;
+
+	if (sndfile == NULL)
+	{	errnum = sf_errno ;
+		}
+	else
+	{	VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 0) ;
+		errnum = psf->error ;
+		} ;
+
+	fprintf (stderr, "%s\n", sf_error_number (errnum)) ;
+	return SFE_NO_ERROR ;
+} /* sf_perror */
+
+
+/*------------------------------------------------------------------------------
+*/
+
+int
+sf_error_str (SNDFILE *sndfile, char *str, size_t maxlen)
+{	SF_PRIVATE 	*psf ;
+	int 		errnum ;
+
+	if (str == NULL)
+		return SFE_INTERNAL ;
+
+	if (sndfile == NULL)
+		errnum = sf_errno ;
+	else
+	{	VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 0) ;
+		errnum = psf->error ;
+		} ;
+
+	snprintf (str, maxlen, "%s", sf_error_number (errnum)) ;
+
+	return SFE_NO_ERROR ;
+} /* sf_error_str */
+
+/*==============================================================================
+*/
+
+int
+sf_format_check	(const SF_INFO *info)
+{	int	subformat, endian ;
+
+	subformat = SF_CODEC (info->format) ;
+	endian = SF_ENDIAN (info->format) ;
+
+	/* This is the place where each file format can check if the supplied
+	** SF_INFO struct is valid.
+	** Return 0 on failure, 1 ons success.
+	*/
+
+	if (info->channels < 1 || info->channels > SF_MAX_CHANNELS)
+		return 0 ;
+
+	if (info->samplerate < 0)
+		return 0 ;
+
+	switch (SF_CONTAINER (info->format))
+	{	case SF_FORMAT_WAV :
+				/* WAV now allows both endian, RIFF or RIFX (little or big respectively) */
+				if (subformat == SF_FORMAT_PCM_U8 || subformat == SF_FORMAT_PCM_16)
+					return 1 ;
+				if (subformat == SF_FORMAT_PCM_24 || subformat == SF_FORMAT_PCM_32)
+					return 1 ;
+				if ((subformat == SF_FORMAT_IMA_ADPCM || subformat == SF_FORMAT_MS_ADPCM) && info->channels <= 2)
+					return 1 ;
+				if (subformat == SF_FORMAT_GSM610 && info->channels == 1)
+					return 1 ;
+				if (subformat == SF_FORMAT_G721_32 && info->channels == 1)
+					return 1 ;
+				if (subformat == SF_FORMAT_ULAW || subformat == SF_FORMAT_ALAW)
+					return 1 ;
+				if (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE)
+					return 1 ;
+				if ((subformat == SF_FORMAT_NMS_ADPCM_16 || subformat == SF_FORMAT_NMS_ADPCM_24 ||
+							subformat == SF_FORMAT_NMS_ADPCM_32) && info->channels == 1)
+					return 1 ;
+				if (subformat == SF_FORMAT_MPEG_LAYER_III && info->channels <= 2)
+					return 1 ;
+				break ;
+
+		case SF_FORMAT_WAVEX :
+				if (endian == SF_ENDIAN_BIG || endian == SF_ENDIAN_CPU)
+					return 0 ;
+				if (subformat == SF_FORMAT_PCM_U8 || subformat == SF_FORMAT_PCM_16)
+					return 1 ;
+				if (subformat == SF_FORMAT_PCM_24 || subformat == SF_FORMAT_PCM_32)
+					return 1 ;
+				if (subformat == SF_FORMAT_ULAW || subformat == SF_FORMAT_ALAW)
+					return 1 ;
+				if (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE)
+					return 1 ;
+				break ;
+
+		case SF_FORMAT_AIFF :
+				/* AIFF does allow both endian-nesses for PCM data.*/
+				if (subformat == SF_FORMAT_PCM_16 || subformat == SF_FORMAT_PCM_24 || subformat == SF_FORMAT_PCM_32)
+					return 1 ;
+				/* For other encodings reject any endian-ness setting. */
+				if (endian != 0)
+					return 0 ;
+				if (subformat == SF_FORMAT_PCM_U8 || subformat == SF_FORMAT_PCM_S8)
+					return 1 ;
+				if (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE)
+					return 1 ;
+				if (subformat == SF_FORMAT_ULAW || subformat == SF_FORMAT_ALAW)
+					return 1 ;
+				if ((subformat == SF_FORMAT_DWVW_12 || subformat == SF_FORMAT_DWVW_16 ||
+							subformat == SF_FORMAT_DWVW_24) && info-> channels == 1)
+					return 1 ;
+				if (subformat == SF_FORMAT_GSM610 && info->channels == 1)
+					return 1 ;
+				if (subformat == SF_FORMAT_IMA_ADPCM && (info->channels == 1 || info->channels == 2))
+					return 1 ;
+				break ;
+
+		case SF_FORMAT_AU :
+				if (subformat == SF_FORMAT_PCM_S8 || subformat == SF_FORMAT_PCM_16)
+					return 1 ;
+				if (subformat == SF_FORMAT_PCM_24 || subformat == SF_FORMAT_PCM_32)
+					return 1 ;
+				if (subformat == SF_FORMAT_ULAW || subformat == SF_FORMAT_ALAW)
+					return 1 ;
+				if (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE)
+					return 1 ;
+				if (subformat == SF_FORMAT_G721_32 && info->channels == 1)
+					return 1 ;
+				if (subformat == SF_FORMAT_G723_24 && info->channels == 1)
+					return 1 ;
+				if (subformat == SF_FORMAT_G723_40 && info->channels == 1)
+					return 1 ;
+				break ;
+
+		case SF_FORMAT_CAF :
+				if (subformat == SF_FORMAT_PCM_S8 || subformat == SF_FORMAT_PCM_16)
+					return 1 ;
+				if (subformat == SF_FORMAT_PCM_24 || subformat == SF_FORMAT_PCM_32)
+					return 1 ;
+				if (subformat == SF_FORMAT_ULAW || subformat == SF_FORMAT_ALAW)
+					return 1 ;
+				if (subformat == SF_FORMAT_ALAC_16 || subformat == SF_FORMAT_ALAC_20)
+					return 1 ;
+				if (subformat == SF_FORMAT_ALAC_24 || subformat == SF_FORMAT_ALAC_32)
+					return 1 ;
+				if (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE)
+					return 1 ;
+				break ;
+
+		case SF_FORMAT_RAW :
+				if (subformat == SF_FORMAT_PCM_U8 || subformat == SF_FORMAT_PCM_S8 || subformat == SF_FORMAT_PCM_16)
+					return 1 ;
+				if (subformat == SF_FORMAT_PCM_24 || subformat == SF_FORMAT_PCM_32)
+					return 1 ;
+				if (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE)
+					return 1 ;
+				if (subformat == SF_FORMAT_ALAW || subformat == SF_FORMAT_ULAW)
+					return 1 ;
+				if ((subformat == SF_FORMAT_DWVW_12 || subformat == SF_FORMAT_DWVW_16 ||
+							subformat == SF_FORMAT_DWVW_24) && info-> channels == 1)
+					return 1 ;
+				if (subformat == SF_FORMAT_GSM610 && info->channels == 1)
+					return 1 ;
+				if (subformat == SF_FORMAT_VOX_ADPCM && info->channels == 1)
+					return 1 ;
+				if ((subformat == SF_FORMAT_NMS_ADPCM_16 || subformat == SF_FORMAT_NMS_ADPCM_24 ||
+							subformat == SF_FORMAT_NMS_ADPCM_32) && info->channels == 1)
+					return 1 ;
+				break ;
+
+		case SF_FORMAT_PAF :
+				if (subformat == SF_FORMAT_PCM_S8 || subformat == SF_FORMAT_PCM_16 || subformat == SF_FORMAT_PCM_24)
+					return 1 ;
+				break ;
+
+		case SF_FORMAT_SVX :
+				/* SVX only supports writing mono SVX files. */
+				if (info->channels > 1)
+					return 0 ;
+				/* Always big endian. */
+				if (endian == SF_ENDIAN_LITTLE || endian == SF_ENDIAN_CPU)
+					return 0 ;
+
+				if (subformat == SF_FORMAT_PCM_S8 || subformat == SF_FORMAT_PCM_16)
+					return 1 ;
+				break ;
+
+		case SF_FORMAT_NIST :
+				if (subformat == SF_FORMAT_PCM_S8 || subformat == SF_FORMAT_PCM_16)
+					return 1 ;
+				if (subformat == SF_FORMAT_PCM_24 || subformat == SF_FORMAT_PCM_32)
+					return 1 ;
+				if (subformat == SF_FORMAT_ULAW || subformat == SF_FORMAT_ALAW)
+					return 1 ;
+				break ;
+
+		case SF_FORMAT_IRCAM :
+				if (info->channels > 256)
+					return 0 ;
+				if (subformat == SF_FORMAT_PCM_16 || subformat == SF_FORMAT_PCM_32)
+					return 1 ;
+				if (subformat == SF_FORMAT_ULAW || subformat == SF_FORMAT_ALAW || subformat == SF_FORMAT_FLOAT)
+					return 1 ;
+				break ;
+
+		case SF_FORMAT_VOC :
+				if (info->channels > 2)
+					return 0 ;
+				/* VOC is strictly little endian. */
+				if (endian == SF_ENDIAN_BIG || endian == SF_ENDIAN_CPU)
+					return 0 ;
+				if (subformat == SF_FORMAT_PCM_U8 || subformat == SF_FORMAT_PCM_16)
+					return 1 ;
+				if (subformat == SF_FORMAT_ULAW || subformat == SF_FORMAT_ALAW)
+					return 1 ;
+				break ;
+
+		case SF_FORMAT_W64 :
+				/* W64 is strictly little endian. */
+				if (endian == SF_ENDIAN_BIG || endian == SF_ENDIAN_CPU)
+					return 0 ;
+				if (subformat == SF_FORMAT_PCM_U8 || subformat == SF_FORMAT_PCM_16)
+					return 1 ;
+				if (subformat == SF_FORMAT_PCM_24 || subformat == SF_FORMAT_PCM_32)
+					return 1 ;
+				if ((subformat == SF_FORMAT_IMA_ADPCM || subformat == SF_FORMAT_MS_ADPCM) && info->channels <= 2)
+					return 1 ;
+				if (subformat == SF_FORMAT_GSM610 && info->channels == 1)
+					return 1 ;
+				if (subformat == SF_FORMAT_ULAW || subformat == SF_FORMAT_ALAW)
+					return 1 ;
+				if (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE)
+					return 1 ;
+				break ;
+
+		case SF_FORMAT_MAT4 :
+				if (subformat == SF_FORMAT_PCM_16 || subformat == SF_FORMAT_PCM_32)
+					return 1 ;
+				if (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE)
+					return 1 ;
+				break ;
+
+		case SF_FORMAT_MAT5 :
+				if (subformat == SF_FORMAT_PCM_U8 || subformat == SF_FORMAT_PCM_16 || subformat == SF_FORMAT_PCM_32)
+					return 1 ;
+				if (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE)
+					return 1 ;
+				break ;
+
+		case SF_FORMAT_PVF :
+				if (subformat == SF_FORMAT_PCM_S8 || subformat == SF_FORMAT_PCM_16 || subformat == SF_FORMAT_PCM_32)
+					return 1 ;
+				break ;
+
+		case SF_FORMAT_XI :
+				if (info->channels != 1)
+					return 0 ;
+				if (subformat == SF_FORMAT_DPCM_8 || subformat == SF_FORMAT_DPCM_16)
+					return 1 ;
+				break ;
+
+		case SF_FORMAT_HTK :
+				if (info->channels != 1)
+					return 0 ;
+				/* HTK is strictly big endian. */
+				if (endian == SF_ENDIAN_LITTLE || endian == SF_ENDIAN_CPU)
+					return 0 ;
+				if (subformat == SF_FORMAT_PCM_16)
+					return 1 ;
+				break ;
+
+		case SF_FORMAT_SDS :
+				if (info->channels != 1)
+					return 0 ;
+				/* SDS is strictly big endian. */
+				if (endian == SF_ENDIAN_LITTLE || endian == SF_ENDIAN_CPU)
+					return 0 ;
+				if (subformat == SF_FORMAT_PCM_S8 || subformat == SF_FORMAT_PCM_16 || subformat == SF_FORMAT_PCM_24)
+					return 1 ;
+				break ;
+
+		case SF_FORMAT_AVR :
+				if (info->channels > 2)
+					return 0 ;
+				/* SDS is strictly big endian. */
+				if (endian == SF_ENDIAN_LITTLE || endian == SF_ENDIAN_CPU)
+					return 0 ;
+				if (subformat == SF_FORMAT_PCM_U8 || subformat == SF_FORMAT_PCM_S8 || subformat == SF_FORMAT_PCM_16)
+					return 1 ;
+				break ;
+
+		case SF_FORMAT_FLAC :
+				/* FLAC can't do more than 8 channels. */
+				if (info->channels > 8)
+					return 0 ;
+				if (endian != SF_ENDIAN_FILE)
+					return 0 ;
+				if (subformat == SF_FORMAT_PCM_S8 || subformat == SF_FORMAT_PCM_16 || subformat == SF_FORMAT_PCM_24)
+					return 1 ;
+				break ;
+
+		case SF_FORMAT_SD2 :
+				/* SD2 is strictly big endian. */
+				if (endian == SF_ENDIAN_LITTLE || endian == SF_ENDIAN_CPU)
+					return 0 ;
+				if (subformat == SF_FORMAT_PCM_S8 || subformat == SF_FORMAT_PCM_16 || subformat == SF_FORMAT_PCM_24 || subformat == SF_FORMAT_PCM_32)
+					return 1 ;
+				break ;
+
+		case SF_FORMAT_WVE :
+				if (info->channels > 1)
+					return 0 ;
+				/* WVE is strictly big endian. */
+				if (endian == SF_ENDIAN_BIG || endian == SF_ENDIAN_CPU)
+					return 0 ;
+				if (subformat == SF_FORMAT_ALAW)
+					return 1 ;
+				break ;
+
+		case SF_FORMAT_OGG :
+				if (endian != SF_ENDIAN_FILE)
+					return 0 ;
+				if (subformat == SF_FORMAT_VORBIS)
+					return 1 ;
+				if (subformat == SF_FORMAT_OPUS)
+					return 1 ;
+				break ;
+
+		case SF_FORMAT_MPC2K :
+				if (info->channels > 2)
+					return 0 ;
+				/* MPC2000 is strictly little endian. */
+				if (endian == SF_ENDIAN_BIG || endian == SF_ENDIAN_CPU)
+					return 0 ;
+				if (subformat == SF_FORMAT_PCM_16)
+					return 1 ;
+				break ;
+
+		case SF_FORMAT_RF64 :
+				if (endian == SF_ENDIAN_BIG || endian == SF_ENDIAN_CPU)
+					return 0 ;
+				if (subformat == SF_FORMAT_PCM_U8 || subformat == SF_FORMAT_PCM_16)
+					return 1 ;
+				if (subformat == SF_FORMAT_PCM_24 || subformat == SF_FORMAT_PCM_32)
+					return 1 ;
+				if (subformat == SF_FORMAT_ULAW || subformat == SF_FORMAT_ALAW)
+					return 1 ;
+				if (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE)
+					return 1 ;
+				break ;
+
+		case SF_FORMAT_MPEG :
+				if (info->channels > 2)
+					return 0 ;
+				if (endian != SF_ENDIAN_FILE)
+					return 0 ;
+				if (subformat == SF_FORMAT_MPEG_LAYER_I || subformat == SF_FORMAT_MPEG_LAYER_II || subformat == SF_FORMAT_MPEG_LAYER_III)
+					return 1 ;
+				break ;
+		default : break ;
+		} ;
+
+	return 0 ;
+} /* sf_format_check */
+
+/*------------------------------------------------------------------------------
+*/
+
+const char *
+sf_version_string (void)
+{
+#if	ENABLE_EXPERIMENTAL_CODE
+	return PACKAGE_NAME "-" PACKAGE_VERSION "-exp" ;
+#else
+	return PACKAGE_NAME "-" PACKAGE_VERSION ;
+#endif
+}
+
+
+/*------------------------------------------------------------------------------
+*/
+
+int
+sf_command	(SNDFILE *sndfile, int command, void *data, int datasize)
+{	SF_PRIVATE *psf = (SF_PRIVATE *) sndfile ;
+	double quality ;
+	double latency ;
+	int old_value ;
+
+	/* This set of commands do not need the sndfile parameter. */
+	switch (command)
+	{	case SFC_GET_LIB_VERSION :
+			if (data == NULL)
+			{	if (psf)
+					psf->error = SFE_BAD_COMMAND_PARAM ;
+				return 0 ;
+				} ;
+			snprintf (data, datasize, "%s", sf_version_string ()) ;
+			return strlen (data) ;
+
+		case SFC_GET_SIMPLE_FORMAT_COUNT :
+			if (data == NULL || datasize != SIGNED_SIZEOF (int))
+				return (sf_errno = SFE_BAD_COMMAND_PARAM) ;
+			*((int*) data) = psf_get_format_simple_count () ;
+			return 0 ;
+
+		case SFC_GET_SIMPLE_FORMAT :
+			if (data == NULL || datasize != SIGNED_SIZEOF (SF_FORMAT_INFO))
+				return (sf_errno = SFE_BAD_COMMAND_PARAM) ;
+			return psf_get_format_simple (data) ;
+
+		case SFC_GET_FORMAT_MAJOR_COUNT :
+			if (data == NULL || datasize != SIGNED_SIZEOF (int))
+				return (sf_errno = SFE_BAD_COMMAND_PARAM) ;
+			*((int*) data) = psf_get_format_major_count () ;
+			return 0 ;
+
+		case SFC_GET_FORMAT_MAJOR :
+			if (data == NULL || datasize != SIGNED_SIZEOF (SF_FORMAT_INFO))
+				return (sf_errno = SFE_BAD_COMMAND_PARAM) ;
+			return psf_get_format_major (data) ;
+
+		case SFC_GET_FORMAT_SUBTYPE_COUNT :
+			if (data == NULL || datasize != SIGNED_SIZEOF (int))
+				return (sf_errno = SFE_BAD_COMMAND_PARAM) ;
+			*((int*) data) = psf_get_format_subtype_count () ;
+			return 0 ;
+
+		case SFC_GET_FORMAT_SUBTYPE :
+			if (data == NULL || datasize != SIGNED_SIZEOF (SF_FORMAT_INFO))
+				return (sf_errno = SFE_BAD_COMMAND_PARAM) ;
+			return psf_get_format_subtype (data) ;
+
+		case SFC_GET_FORMAT_INFO :
+			if (data == NULL || datasize != SIGNED_SIZEOF (SF_FORMAT_INFO))
+				return (sf_errno = SFE_BAD_COMMAND_PARAM) ;
+			return psf_get_format_info (data) ;
+		} ;
+
+	if (sndfile == NULL && command == SFC_GET_LOG_INFO)
+	{	if (data == NULL)
+			return (sf_errno = SFE_BAD_COMMAND_PARAM) ;
+		snprintf (data, datasize, "%s", sf_parselog) ;
+		return strlen (data) ;
+		} ;
+
+	VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ;
+
+	switch (command)
+	{	case SFC_SET_NORM_FLOAT :
+			old_value = psf->norm_float ;
+			psf->norm_float = (datasize) ? SF_TRUE : SF_FALSE ;
+			return old_value ;
+
+		case SFC_GET_CURRENT_SF_INFO :
+			if (data == NULL || datasize != SIGNED_SIZEOF (SF_INFO))
+				return (sf_errno = SFE_BAD_COMMAND_PARAM) ;
+			memcpy (data, &psf->sf, sizeof (SF_INFO)) ;
+			break ;
+
+		case SFC_SET_NORM_DOUBLE :
+			old_value = psf->norm_double ;
+			psf->norm_double = (datasize) ? SF_TRUE : SF_FALSE ;
+			return old_value ;
+
+		case SFC_GET_NORM_FLOAT :
+			return psf->norm_float ;
+
+		case SFC_GET_NORM_DOUBLE :
+			return psf->norm_double ;
+
+		case SFC_SET_SCALE_FLOAT_INT_READ :
+			old_value = psf->float_int_mult ;
+
+			psf->float_int_mult = (datasize != 0) ? SF_TRUE : SF_FALSE ;
+			if (psf->float_int_mult && psf->float_max < 0.0)
+				/* Scale to prevent wrap-around distortion. */
+				psf->float_max = (32768.0 / 32767.0) * psf_calc_signal_max (psf, SF_FALSE) ;
+			return old_value ;
+
+		case SFC_SET_SCALE_INT_FLOAT_WRITE :
+			old_value = psf->scale_int_float ;
+			psf->scale_int_float = (datasize != 0) ? SF_TRUE : SF_FALSE ;
+			return old_value ;
+
+		case SFC_SET_ADD_PEAK_CHUNK :
+			{	int format = SF_CONTAINER (psf->sf.format) ;
+
+				/* Only WAV and AIFF support the PEAK chunk. */
+				switch (format)
+				{	case SF_FORMAT_AIFF :
+					case SF_FORMAT_CAF :
+					case SF_FORMAT_WAV :
+					case SF_FORMAT_WAVEX :
+					case SF_FORMAT_RF64 :
+						break ;
+
+					default :
+						return SF_FALSE ;
+					} ;
+
+				format = SF_CODEC (psf->sf.format) ;
+
+				/* Only files containg the following data types support the PEAK chunk. */
+				if (format != SF_FORMAT_FLOAT && format != SF_FORMAT_DOUBLE)
+					return SF_FALSE ;
+
+				} ;
+			/* Can only do this is in SFM_WRITE mode. */
+			if (psf->file.mode != SFM_WRITE && psf->file.mode != SFM_RDWR)
+				return SF_FALSE ;
+			/* If data has already been written this must fail. */
+			if (psf->have_written)
+			{	psf->error = SFE_CMD_HAS_DATA ;
+				return SF_FALSE ;
+				} ;
+			/* Everything seems OK, so set psf->has_peak and re-write header. */
+			if (datasize == SF_FALSE && psf->peak_info != NULL)
+			{	free (psf->peak_info) ;
+				psf->peak_info = NULL ;
+				}
+			else if (psf->peak_info == NULL)
+			{	psf->peak_info = peak_info_calloc (psf->sf.channels) ;
+				if (psf->peak_info != NULL)
+					psf->peak_info->peak_loc = SF_PEAK_START ;
+				} ;
+
+			if (psf->write_header)
+				psf->write_header (psf, SF_TRUE) ;
+			return datasize ;
+
+		case SFC_SET_ADD_HEADER_PAD_CHUNK :
+			return SF_FALSE ;
+
+		case SFC_GET_LOG_INFO :
+			if (data == NULL)
+				return SFE_BAD_COMMAND_PARAM ;
+			snprintf (data, datasize, "%s", psf->parselog.buf) ;
+			return strlen (data) ;
+
+		case SFC_CALC_SIGNAL_MAX :
+			if (data == NULL || datasize != sizeof (double))
+				return (psf->error = SFE_BAD_COMMAND_PARAM) ;
+			*((double*) data) = psf_calc_signal_max (psf, SF_FALSE) ;
+			break ;
+
+		case SFC_CALC_NORM_SIGNAL_MAX :
+			if (data == NULL || datasize != sizeof (double))
+				return (psf->error = SFE_BAD_COMMAND_PARAM) ;
+			*((double*) data) = psf_calc_signal_max (psf, SF_TRUE) ;
+			break ;
+
+		case SFC_CALC_MAX_ALL_CHANNELS :
+			if (data == NULL || datasize != SIGNED_SIZEOF (double) * psf->sf.channels)
+				return (psf->error = SFE_BAD_COMMAND_PARAM) ;
+			return psf_calc_max_all_channels (psf, (double*) data, SF_FALSE) ;
+
+		case SFC_CALC_NORM_MAX_ALL_CHANNELS :
+			if (data == NULL || datasize != SIGNED_SIZEOF (double) * psf->sf.channels)
+				return (psf->error = SFE_BAD_COMMAND_PARAM) ;
+			return psf_calc_max_all_channels (psf, (double*) data, SF_TRUE) ;
+
+		case SFC_GET_SIGNAL_MAX :
+			if (data == NULL || datasize != sizeof (double))
+			{	psf->error = SFE_BAD_COMMAND_PARAM ;
+				return SF_FALSE ;
+				} ;
+			return psf_get_signal_max (psf, (double *) data) ;
+
+		case SFC_GET_MAX_ALL_CHANNELS :
+			if (data == NULL || datasize != SIGNED_SIZEOF (double) * psf->sf.channels)
+			{	psf->error = SFE_BAD_COMMAND_PARAM ;
+				return SF_FALSE ;
+				} ;
+			return psf_get_max_all_channels (psf, (double*) data) ;
+
+		case SFC_UPDATE_HEADER_NOW :
+			if (psf->write_header)
+				psf->write_header (psf, SF_TRUE) ;
+			break ;
+
+		case SFC_SET_UPDATE_HEADER_AUTO :
+			psf->auto_header = datasize ? SF_TRUE : SF_FALSE ;
+			return psf->auto_header ;
+			break ;
+
+		case SFC_SET_ADD_DITHER_ON_WRITE :
+		case SFC_SET_ADD_DITHER_ON_READ :
+			/*
+			** FIXME !
+			** These are obsolete. Just return.
+			** Remove some time after version 1.0.8.
+			*/
+			break ;
+
+		case SFC_SET_DITHER_ON_WRITE :
+			if (data == NULL || datasize != SIGNED_SIZEOF (SF_DITHER_INFO))
+				return (psf->error = SFE_BAD_COMMAND_PARAM) ;
+			memcpy (&psf->write_dither, data, sizeof (psf->write_dither)) ;
+			if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+				dither_init (psf, SFM_WRITE) ;
+			break ;
+
+		case SFC_SET_DITHER_ON_READ :
+			if (data == NULL || datasize != SIGNED_SIZEOF (SF_DITHER_INFO))
+				return (psf->error = SFE_BAD_COMMAND_PARAM) ;
+			memcpy (&psf->read_dither, data, sizeof (psf->read_dither)) ;
+			if (psf->file.mode == SFM_READ || psf->file.mode == SFM_RDWR)
+				dither_init (psf, SFM_READ) ;
+			break ;
+
+		case SFC_FILE_TRUNCATE :
+			if (psf->file.mode != SFM_WRITE && psf->file.mode != SFM_RDWR)
+				return SF_TRUE ;
+			if (datasize != sizeof (sf_count_t))
+				return SF_TRUE ;
+			if (data == NULL || datasize != sizeof (sf_count_t))
+			{	psf->error = SFE_BAD_COMMAND_PARAM ;
+				return SF_FALSE ;
+				}
+			else
+			{	sf_count_t position ;
+
+				position = *((sf_count_t*) data) ;
+
+				if (sf_seek (sndfile, position, SEEK_SET) != position)
+					return SF_TRUE ;
+
+				psf->sf.frames = position ;
+
+				position = psf_fseek (psf, 0, SEEK_CUR) ;
+
+				return psf_ftruncate (psf, position) ;
+				} ;
+			break ;
+
+		case SFC_SET_RAW_START_OFFSET :
+			if (data == NULL || datasize != sizeof (sf_count_t))
+				return (psf->error = SFE_BAD_COMMAND_PARAM) ;
+
+			if ((SF_CONTAINER (psf->sf.format)) != SF_FORMAT_RAW)
+				return (psf->error = SFE_BAD_COMMAND_PARAM) ;
+
+			psf->dataoffset = *((sf_count_t*) data) ;
+			sf_seek (sndfile, 0, SEEK_CUR) ;
+			break ;
+
+		case SFC_GET_EMBED_FILE_INFO :
+			if (data == NULL || datasize != sizeof (SF_EMBED_FILE_INFO))
+				return (psf->error = SFE_BAD_COMMAND_PARAM) ;
+
+			((SF_EMBED_FILE_INFO*) data)->offset = psf->fileoffset ;
+			((SF_EMBED_FILE_INFO*) data)->length = psf->filelength ;
+			break ;
+
+		/* Lite remove start */
+		case SFC_TEST_IEEE_FLOAT_REPLACE :
+			psf->ieee_replace = (datasize) ? SF_TRUE : SF_FALSE ;
+			if ((SF_CODEC (psf->sf.format)) == SF_FORMAT_FLOAT)
+				float32_init (psf) ;
+			else if ((SF_CODEC (psf->sf.format)) == SF_FORMAT_DOUBLE)
+				double64_init (psf) ;
+			else
+				return (psf->error = SFE_BAD_COMMAND_PARAM) ;
+			break ;
+		/* Lite remove end */
+
+		case SFC_SET_CLIPPING :
+			psf->add_clipping = (datasize) ? SF_TRUE : SF_FALSE ;
+			return psf->add_clipping ;
+
+		case SFC_GET_CLIPPING :
+			return psf->add_clipping ;
+
+		case SFC_GET_LOOP_INFO :
+			if (datasize != sizeof (SF_LOOP_INFO) || data == NULL)
+			{	psf->error = SFE_BAD_COMMAND_PARAM ;
+				return SF_FALSE ;
+				} ;
+			if (psf->loop_info == NULL)
+				return SF_FALSE ;
+			memcpy (data, psf->loop_info, sizeof (SF_LOOP_INFO)) ;
+			return SF_TRUE ;
+
+		case SFC_SET_BROADCAST_INFO :
+			{	int format = SF_CONTAINER (psf->sf.format) ;
+
+				/* Only WAV and RF64 supports the BEXT (Broadcast) chunk. */
+				if (format != SF_FORMAT_WAV && format != SF_FORMAT_WAVEX && format != SF_FORMAT_RF64)
+					return SF_FALSE ;
+				} ;
+
+			/* Only makes sense in SFM_WRITE or SFM_RDWR mode. */
+			if ((psf->file.mode != SFM_WRITE) && (psf->file.mode != SFM_RDWR))
+				return SF_FALSE ;
+			/* If data has already been written this must fail. */
+			if (psf->broadcast_16k == NULL && psf->have_written)
+			{	psf->error = SFE_CMD_HAS_DATA ;
+				return SF_FALSE ;
+				} ;
+
+			if (!broadcast_var_set (psf, data, datasize))
+				return SF_FALSE ;
+
+			if (psf->write_header)
+				psf->write_header (psf, SF_TRUE) ;
+			return SF_TRUE ;
+
+		case SFC_GET_BROADCAST_INFO :
+			if (data == NULL)
+			{	psf->error = SFE_BAD_COMMAND_PARAM ;
+				return SF_FALSE ;
+				} ;
+			return broadcast_var_get (psf, data, datasize) ;
+
+		case SFC_SET_CART_INFO :
+			{	int format = SF_CONTAINER (psf->sf.format) ;
+				/* Only WAV and RF64 support cart chunk format */
+				if (format != SF_FORMAT_WAV && format != SF_FORMAT_RF64)
+					return SF_FALSE ;
+				} ;
+
+			/* Only makes sense in SFM_WRITE or SFM_RDWR mode */
+			if ((psf->file.mode != SFM_WRITE) && (psf->file.mode != SFM_RDWR))
+				return SF_FALSE ;
+			/* If data has already been written this must fail. */
+			if (psf->cart_16k == NULL && psf->have_written)
+			{	psf->error = SFE_CMD_HAS_DATA ;
+				return SF_FALSE ;
+				} ;
+			if (!cart_var_set (psf, data, datasize))
+				return SF_FALSE ;
+			if (psf->write_header)
+				psf->write_header (psf, SF_TRUE) ;
+			return SF_TRUE ;
+
+		case SFC_GET_CART_INFO :
+			if (data == NULL)
+			{	psf->error = SFE_BAD_COMMAND_PARAM ;
+				return SF_FALSE ;
+				} ;
+			return cart_var_get (psf, data, datasize) ;
+
+		case SFC_GET_CUE_COUNT :
+			if (datasize != sizeof (uint32_t) || data == NULL)
+			{	psf->error = SFE_BAD_COMMAND_PARAM ;
+				return SF_FALSE ;
+				} ;
+			if (psf->cues != NULL)
+			{	*((uint32_t *) data) = psf->cues->cue_count ;
+				return SF_TRUE ;
+				} ;
+			return SF_FALSE ;
+
+		case SFC_GET_CUE :
+			if (datasize < (int) sizeof (uint32_t) || data == NULL)
+			{	psf->error = SFE_BAD_COMMAND_PARAM ;
+				return SF_FALSE ;
+				} ;
+			if (psf->cues == NULL)
+				return SF_FALSE ;
+			psf_get_cues (psf, data, datasize) ;
+			return SF_TRUE ;
+
+		case SFC_SET_CUE :
+			if (psf->have_written)
+			{	psf->error = SFE_CMD_HAS_DATA ;
+				return SF_FALSE ;
+				} ;
+			if (datasize < (int) sizeof (uint32_t) || data == NULL)
+			{	psf->error = SFE_BAD_COMMAND_PARAM ;
+				return SF_FALSE ;
+				} ;
+			if (psf->cues == NULL && (psf->cues = psf_cues_dup (data, datasize)) == NULL)
+			{	psf->error = SFE_MALLOC_FAILED ;
+				return SF_FALSE ;
+				} ;
+			return SF_TRUE ;
+
+		case SFC_GET_INSTRUMENT :
+			if (datasize != sizeof (SF_INSTRUMENT) || data == NULL)
+			{	psf->error = SFE_BAD_COMMAND_PARAM ;
+				return SF_FALSE ;
+				} ;
+			if (psf->instrument == NULL)
+				return SF_FALSE ;
+			memcpy (data, psf->instrument, sizeof (SF_INSTRUMENT)) ;
+			return SF_TRUE ;
+
+		case SFC_SET_INSTRUMENT :
+			/* If data has already been written this must fail. */
+			if (psf->have_written)
+			{	psf->error = SFE_CMD_HAS_DATA ;
+				return SF_FALSE ;
+				} ;
+			if (datasize != sizeof (SF_INSTRUMENT) || data == NULL)
+			{	psf->error = SFE_BAD_COMMAND_PARAM ;
+				return SF_FALSE ;
+				} ;
+
+			if (psf->instrument == NULL && (psf->instrument = psf_instrument_alloc ()) == NULL)
+			{	psf->error = SFE_MALLOC_FAILED ;
+				return SF_FALSE ;
+				} ;
+			memcpy (psf->instrument, data, sizeof (SF_INSTRUMENT)) ;
+			return SF_TRUE ;
+
+		case SFC_RAW_DATA_NEEDS_ENDSWAP :
+			return psf->data_endswap ;
+
+		case SFC_GET_CHANNEL_MAP_INFO :
+			if (psf->channel_map == NULL)
+				return SF_FALSE ;
+
+			if (data == NULL || datasize != SIGNED_SIZEOF (psf->channel_map [0]) * psf->sf.channels)
+			{	psf->error = SFE_BAD_COMMAND_PARAM ;
+				return SF_FALSE ;
+				} ;
+
+			memcpy (data, psf->channel_map, datasize) ;
+			return SF_TRUE ;
+
+		case SFC_SET_CHANNEL_MAP_INFO :
+			if (psf->have_written)
+			{	psf->error = SFE_CMD_HAS_DATA ;
+				return SF_FALSE ;
+				} ;
+			if (data == NULL || datasize != SIGNED_SIZEOF (psf->channel_map [0]) * psf->sf.channels)
+			{	psf->error = SFE_BAD_COMMAND_PARAM ;
+				return SF_FALSE ;
+				} ;
+
+			{	int *iptr ;
+
+				for (iptr = data ; iptr < (int*) data + psf->sf.channels ; iptr++)
+				{	if (*iptr <= SF_CHANNEL_MAP_INVALID || *iptr >= SF_CHANNEL_MAP_MAX)
+					{	psf->error = SFE_BAD_COMMAND_PARAM ;
+						return SF_FALSE ;
+						} ;
+					} ;
+				} ;
+
+			free (psf->channel_map) ;
+			if ((psf->channel_map = malloc (datasize)) == NULL)
+			{	psf->error = SFE_MALLOC_FAILED ;
+				return SF_FALSE ;
+				} ;
+
+			memcpy (psf->channel_map, data, datasize) ;
+
+			/*
+			**	Pass the command down to the container's command handler.
+			**	Don't pass user data, use validated psf->channel_map data instead.
+			*/
+			if (psf->command)
+				return psf->command (psf, command, NULL, 0) ;
+			return SF_FALSE ;
+
+		case SFC_SET_VBR_ENCODING_QUALITY :
+			if (data == NULL || datasize != sizeof (double))
+				return SF_FALSE ;
+
+			quality = *((double *) data) ;
+			quality = 1.0 - SF_MAX (0.0, SF_MIN (1.0, quality)) ;
+			return sf_command (sndfile, SFC_SET_COMPRESSION_LEVEL, &quality, sizeof (quality)) ;
+
+		case SFC_SET_OGG_PAGE_LATENCY_MS :
+			if (data == NULL || datasize != sizeof (double))
+				return SF_FALSE ;
+
+			latency = *((double *) data) ;
+			return sf_command (sndfile, SFC_SET_OGG_PAGE_LATENCY, &latency, sizeof (latency)) ;
+
+		default :
+			/* Must be a file specific command. Pass it on. */
+			if (psf->command)
+				return psf->command (psf, command, data, datasize) ;
+
+			psf_log_printf (psf, "*** sf_command : cmd = 0x%X\n", command) ;
+			return (psf->error = SFE_BAD_COMMAND_PARAM) ;
+		} ;
+
+	return 0 ;
+} /* sf_command */
+
+/*------------------------------------------------------------------------------
+*/
+
+sf_count_t
+sf_seek	(SNDFILE *sndfile, sf_count_t offset, int whence)
+{	SF_PRIVATE 	*psf ;
+	sf_count_t	seek_from_start = 0, retval ;
+
+	VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ;
+
+	if (! psf->sf.seekable)
+	{	psf->error = SFE_NOT_SEEKABLE ;
+		return	PSF_SEEK_ERROR ;
+		} ;
+
+	/* If the whence parameter has a mode ORed in, check to see that
+	** it makes sense.
+	*/
+	if (((whence & SFM_MASK) == SFM_WRITE && psf->file.mode == SFM_READ) ||
+			((whence & SFM_MASK) == SFM_READ && psf->file.mode == SFM_WRITE))
+	{	psf->error = SFE_WRONG_SEEK ;
+		return PSF_SEEK_ERROR ;
+		} ;
+
+	/* Convert all SEEK_CUR and SEEK_END into seek_from_start to be
+	** used with SEEK_SET.
+	*/
+	switch (whence)
+	{	/* The SEEK_SET behaviour is independant of mode. */
+		case SEEK_SET :
+		case SEEK_SET | SFM_READ :
+		case SEEK_SET | SFM_WRITE :
+		case SEEK_SET | SFM_RDWR :
+				seek_from_start = offset ;
+				break ;
+
+		/* The SEEK_CUR is a little more tricky. */
+		case SEEK_CUR :
+				if (offset == 0)
+				{	if (psf->file.mode == SFM_READ)
+						return psf->read_current ;
+					if (psf->file.mode == SFM_WRITE)
+						return psf->write_current ;
+					} ;
+				if (psf->file.mode == SFM_READ)
+					seek_from_start = psf->read_current + offset ;
+				else if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+					seek_from_start = psf->write_current + offset ;
+				else
+					psf->error = SFE_AMBIGUOUS_SEEK ;
+				break ;
+
+		case SEEK_CUR | SFM_READ :
+				if (offset == 0)
+					return psf->read_current ;
+				seek_from_start = psf->read_current + offset ;
+				break ;
+
+		case SEEK_CUR | SFM_WRITE :
+				if (offset == 0)
+					return psf->write_current ;
+				seek_from_start = psf->write_current + offset ;
+				break ;
+
+		/* The SEEK_END */
+		case SEEK_END :
+		case SEEK_END | SFM_READ :
+		case SEEK_END | SFM_WRITE :
+				seek_from_start = psf->sf.frames + offset ;
+				break ;
+
+		default :
+				psf->error = SFE_BAD_SEEK ;
+				break ;
+		} ;
+
+	if (psf->error)
+		return PSF_SEEK_ERROR ;
+
+	if (psf->file.mode == SFM_RDWR || psf->file.mode == SFM_WRITE)
+	{	if (seek_from_start < 0)
+		{	psf->error = SFE_BAD_SEEK ;
+			return PSF_SEEK_ERROR ;
+			} ;
+		}
+	else if (seek_from_start < 0 || seek_from_start > psf->sf.frames)
+	{	psf->error = SFE_BAD_SEEK ;
+		return PSF_SEEK_ERROR ;
+		} ;
+
+	if (psf->seek)
+	{	int new_mode = (whence & SFM_MASK) ? (whence & SFM_MASK) : psf->file.mode ;
+
+		retval = psf->seek (psf, new_mode, seek_from_start) ;
+
+		switch (new_mode)
+		{	case SFM_READ :
+					psf->read_current = retval ;
+					break ;
+			case SFM_WRITE :
+					psf->write_current = retval ;
+					break ;
+			case SFM_RDWR :
+					psf->read_current = retval ;
+					psf->write_current = retval ;
+					new_mode = SFM_READ ;
+					break ;
+			} ;
+
+		psf->last_op = new_mode ;
+
+		return retval ;
+		} ;
+
+	psf->error = SFE_AMBIGUOUS_SEEK ;
+	return PSF_SEEK_ERROR ;
+} /* sf_seek */
+
+/*------------------------------------------------------------------------------
+*/
+
+const char*
+sf_get_string (SNDFILE *sndfile, int str_type)
+{	SF_PRIVATE 	*psf ;
+
+	if ((psf = (SF_PRIVATE*) sndfile) == NULL)
+		return NULL ;
+	if (psf->Magick != SNDFILE_MAGICK)
+		return NULL ;
+
+	return psf_get_string (psf, str_type) ;
+} /* sf_get_string */
+
+int
+sf_set_string (SNDFILE *sndfile, int str_type, const char* str)
+{	SF_PRIVATE 	*psf ;
+
+	VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ;
+
+	return psf_set_string (psf, str_type, str) ;
+} /* sf_get_string */
+
+/*------------------------------------------------------------------------------
+*/
+
+int
+sf_current_byterate (SNDFILE *sndfile)
+{	SF_PRIVATE 	*psf ;
+
+	if ((psf = (SF_PRIVATE*) sndfile) == NULL)
+		return -1 ;
+	if (psf->Magick != SNDFILE_MAGICK)
+		return -1 ;
+
+	/* This should cover all PCM and floating point formats. */
+	if (psf->bytewidth)
+		return psf->sf.samplerate * psf->sf.channels * psf->bytewidth ;
+
+	if (psf->byterate)
+		return psf->byterate (psf) ;
+
+	switch (SF_CODEC (psf->sf.format))
+	{	case SF_FORMAT_IMA_ADPCM :
+		case SF_FORMAT_MS_ADPCM :
+		case SF_FORMAT_VOX_ADPCM :
+			return (psf->sf.samplerate * psf->sf.channels) / 2 ;
+
+		case SF_FORMAT_GSM610 :
+			return (psf->sf.samplerate * psf->sf.channels * 13000) / 8000 ;
+
+		case SF_FORMAT_NMS_ADPCM_16:
+			return psf->sf.samplerate / 4 + 10 ;
+
+		case SF_FORMAT_NMS_ADPCM_24:
+			return psf->sf.samplerate * 3 / 8 + 10 ;
+
+		case SF_FORMAT_NMS_ADPCM_32:
+			return psf->sf.samplerate / 2 + 10 ;
+
+		case SF_FORMAT_G721_32 :	/* 32kbs G721 ADPCM encoding. */
+			return (psf->sf.samplerate * psf->sf.channels) / 2 ;
+
+		case SF_FORMAT_G723_24 :	/* 24kbs G723 ADPCM encoding. */
+			return (psf->sf.samplerate * psf->sf.channels * 3) / 8 ;
+
+		case SF_FORMAT_G723_40 :	/* 40kbs G723 ADPCM encoding. */
+			return (psf->sf.samplerate * psf->sf.channels * 5) / 8 ;
+
+		default :
+			break ;
+		} ;
+
+	return -1 ;
+} /* sf_current_byterate */
+
+/*==============================================================================
+*/
+
+sf_count_t
+sf_read_raw		(SNDFILE *sndfile, void *ptr, sf_count_t bytes)
+{	SF_PRIVATE 	*psf ;
+	sf_count_t	count, extra ;
+	int			bytewidth, blockwidth ;
+
+	if (bytes == 0)
+		return 0 ;
+
+	VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ;
+
+	bytewidth = (psf->bytewidth > 0) ? psf->bytewidth : 1 ;
+	blockwidth = (psf->blockwidth > 0) ? psf->blockwidth : 1 ;
+
+	if (psf->file.mode == SFM_WRITE)
+	{	psf->error = SFE_NOT_READMODE ;
+		return	0 ;
+		} ;
+
+	if (bytes < 0 || psf->read_current >= psf->sf.frames)
+	{	psf_memset (ptr, 0, bytes) ;
+		return 0 ;
+		} ;
+
+	if (bytes % (psf->sf.channels * bytewidth))
+	{	psf->error = SFE_BAD_READ_ALIGN ;
+		return 0 ;
+		} ;
+
+	if (psf->last_op != SFM_READ)
+		if (psf->seek (psf, SFM_READ, psf->read_current) < 0)
+			return 0 ;
+
+	count = psf_fread (ptr, 1, bytes, psf) ;
+
+	if (psf->read_current + count / blockwidth <= psf->sf.frames)
+		psf->read_current += count / blockwidth ;
+	else
+	{	count = (psf->sf.frames - psf->read_current) * blockwidth ;
+		extra = bytes - count ;
+		psf_memset (((char *) ptr) + count, 0, extra) ;
+		psf->read_current = psf->sf.frames ;
+		} ;
+
+	psf->last_op = SFM_READ ;
+
+	return count ;
+} /* sf_read_raw */
+
+/*------------------------------------------------------------------------------
+*/
+
+sf_count_t
+sf_read_short	(SNDFILE *sndfile, short *ptr, sf_count_t len)
+{	SF_PRIVATE 	*psf ;
+	sf_count_t	count, extra ;
+
+	if (len == 0)
+		return 0 ;
+
+	VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ;
+
+	if (len <= 0)
+	{	psf->error = SFE_NEGATIVE_RW_LEN ;
+		return 0 ;
+		} ;
+
+	if (psf->file.mode == SFM_WRITE)
+	{	psf->error = SFE_NOT_READMODE ;
+		return 0 ;
+		} ;
+
+	if (len % psf->sf.channels)
+	{	psf->error = SFE_BAD_READ_ALIGN ;
+		return 0 ;
+		} ;
+
+	if (psf->read_current >= psf->sf.frames)
+	{	psf_memset (ptr, 0, len * sizeof (short)) ;
+		return 0 ; /* End of file. */
+		} ;
+
+	if (psf->read_short == NULL || psf->seek == NULL)
+	{	psf->error = SFE_UNIMPLEMENTED ;
+		return	0 ;
+		} ;
+
+	if (psf->last_op != SFM_READ)
+		if (psf->seek (psf, SFM_READ, psf->read_current) < 0)
+			return 0 ;
+
+	count = psf->read_short (psf, ptr, len) ;
+
+	if (psf->read_current + count / psf->sf.channels <= psf->sf.frames)
+		psf->read_current += count / psf->sf.channels ;
+	else
+	{	count = (psf->sf.frames - psf->read_current) * psf->sf.channels ;
+		extra = len - count ;
+		psf_memset (ptr + count, 0, extra * sizeof (short)) ;
+		psf->read_current = psf->sf.frames ;
+		} ;
+
+	psf->last_op = SFM_READ ;
+
+	return count ;
+} /* sf_read_short */
+
+sf_count_t
+sf_readf_short		(SNDFILE *sndfile, short *ptr, sf_count_t frames)
+{	SF_PRIVATE 	*psf ;
+	sf_count_t	count, extra ;
+
+	if (frames == 0)
+		return 0 ;
+
+	VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ;
+
+	if (frames <= 0)
+	{	psf->error = SFE_NEGATIVE_RW_LEN ;
+		return 0 ;
+		} ;
+
+	if (psf->file.mode == SFM_WRITE)
+	{	psf->error = SFE_NOT_READMODE ;
+		return 0 ;
+		} ;
+
+	if (psf->read_current >= psf->sf.frames)
+	{	psf_memset (ptr, 0, frames * psf->sf.channels * sizeof (short)) ;
+		return 0 ; /* End of file. */
+		} ;
+
+	if (psf->read_short == NULL || psf->seek == NULL)
+	{	psf->error = SFE_UNIMPLEMENTED ;
+		return 0 ;
+		} ;
+
+	if (psf->last_op != SFM_READ)
+		if (psf->seek (psf, SFM_READ, psf->read_current) < 0)
+			return 0 ;
+
+	count = psf->read_short (psf, ptr, frames * psf->sf.channels) ;
+
+	if (psf->read_current + count / psf->sf.channels <= psf->sf.frames)
+		psf->read_current += count / psf->sf.channels ;
+	else
+	{	count = (psf->sf.frames - psf->read_current) * psf->sf.channels ;
+		extra = frames * psf->sf.channels - count ;
+		psf_memset (ptr + count, 0, extra * sizeof (short)) ;
+		psf->read_current = psf->sf.frames ;
+		} ;
+
+	psf->last_op = SFM_READ ;
+
+	return count / psf->sf.channels ;
+} /* sf_readf_short */
+
+/*------------------------------------------------------------------------------
+*/
+
+sf_count_t
+sf_read_int		(SNDFILE *sndfile, int *ptr, sf_count_t len)
+{	SF_PRIVATE 	*psf ;
+	sf_count_t	count, extra ;
+
+	if (len == 0)
+		return 0 ;
+
+	VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ;
+
+	if (len <= 0)
+	{	psf->error = SFE_NEGATIVE_RW_LEN ;
+		return 0 ;
+		} ;
+
+	if (psf->file.mode == SFM_WRITE)
+	{	psf->error = SFE_NOT_READMODE ;
+		return 0 ;
+		} ;
+
+	if (len % psf->sf.channels)
+	{	psf->error = SFE_BAD_READ_ALIGN ;
+		return 0 ;
+		} ;
+
+	if (psf->read_current >= psf->sf.frames)
+	{	psf_memset (ptr, 0, len * sizeof (int)) ;
+		return 0 ;
+		} ;
+
+	if (psf->read_int == NULL || psf->seek == NULL)
+	{	psf->error = SFE_UNIMPLEMENTED ;
+		return 0 ;
+		} ;
+
+	if (psf->last_op != SFM_READ)
+		if (psf->seek (psf, SFM_READ, psf->read_current) < 0)
+			return 0 ;
+
+	count = psf->read_int (psf, ptr, len) ;
+
+	if (psf->read_current + count / psf->sf.channels <= psf->sf.frames)
+		psf->read_current += count / psf->sf.channels ;
+	else
+	{	count = (psf->sf.frames - psf->read_current) * psf->sf.channels ;
+		extra = len - count ;
+		psf_memset (ptr + count, 0, extra * sizeof (int)) ;
+		psf->read_current = psf->sf.frames ;
+		} ;
+
+	psf->last_op = SFM_READ ;
+
+	return count ;
+} /* sf_read_int */
+
+sf_count_t
+sf_readf_int	(SNDFILE *sndfile, int *ptr, sf_count_t frames)
+{	SF_PRIVATE 	*psf ;
+	sf_count_t	count, extra ;
+
+	if (frames == 0)
+		return 0 ;
+
+	VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ;
+
+	if (frames <= 0)
+	{	psf->error = SFE_NEGATIVE_RW_LEN ;
+		return 0 ;
+		} ;
+
+	if (psf->file.mode == SFM_WRITE)
+	{	psf->error = SFE_NOT_READMODE ;
+		return 0 ;
+		} ;
+
+	if (psf->read_current >= psf->sf.frames)
+	{	psf_memset (ptr, 0, frames * psf->sf.channels * sizeof (int)) ;
+		return 0 ;
+		} ;
+
+	if (psf->read_int == NULL || psf->seek == NULL)
+	{	psf->error = SFE_UNIMPLEMENTED ;
+		return	0 ;
+		} ;
+
+	if (psf->last_op != SFM_READ)
+		if (psf->seek (psf, SFM_READ, psf->read_current) < 0)
+			return 0 ;
+
+	count = psf->read_int (psf, ptr, frames * psf->sf.channels) ;
+
+	if (psf->read_current + count / psf->sf.channels <= psf->sf.frames)
+		psf->read_current += count / psf->sf.channels ;
+	else
+	{	count = (psf->sf.frames - psf->read_current) * psf->sf.channels ;
+		extra = frames * psf->sf.channels - count ;
+		psf_memset (ptr + count, 0, extra * sizeof (int)) ;
+		psf->read_current = psf->sf.frames ;
+		} ;
+
+	psf->last_op = SFM_READ ;
+
+	return count / psf->sf.channels ;
+} /* sf_readf_int */
+
+/*------------------------------------------------------------------------------
+*/
+
+sf_count_t
+sf_read_float	(SNDFILE *sndfile, float *ptr, sf_count_t len)
+{	SF_PRIVATE 	*psf ;
+	sf_count_t	count, extra ;
+
+	if (len == 0)
+		return 0 ;
+
+	VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ;
+
+	if (len <= 0)
+	{	psf->error = SFE_NEGATIVE_RW_LEN ;
+		return 0 ;
+		} ;
+
+	if (psf->file.mode == SFM_WRITE)
+	{	psf->error = SFE_NOT_READMODE ;
+		return 0 ;
+		} ;
+
+	if (len % psf->sf.channels)
+	{	psf->error = SFE_BAD_READ_ALIGN ;
+		return 0 ;
+		} ;
+
+	if (psf->read_current >= psf->sf.frames)
+	{	psf_memset (ptr, 0, len * sizeof (float)) ;
+		return 0 ;
+		} ;
+
+	if (psf->read_float == NULL || psf->seek == NULL)
+	{	psf->error = SFE_UNIMPLEMENTED ;
+		return	0 ;
+		} ;
+
+	if (psf->last_op != SFM_READ)
+		if (psf->seek (psf, SFM_READ, psf->read_current) < 0)
+			return 0 ;
+
+	count = psf->read_float (psf, ptr, len) ;
+
+	if (psf->read_current + count / psf->sf.channels <= psf->sf.frames)
+		psf->read_current += count / psf->sf.channels ;
+	else
+	{	count = (psf->sf.frames - psf->read_current) * psf->sf.channels ;
+		extra = len - count ;
+		psf_memset (ptr + count, 0, extra * sizeof (float)) ;
+		psf->read_current = psf->sf.frames ;
+		} ;
+
+	psf->last_op = SFM_READ ;
+
+	return count ;
+} /* sf_read_float */
+
+sf_count_t
+sf_readf_float	(SNDFILE *sndfile, float *ptr, sf_count_t frames)
+{	SF_PRIVATE 	*psf ;
+	sf_count_t	count, extra ;
+
+	if (frames == 0)
+		return 0 ;
+
+	VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ;
+
+	if (frames <= 0)
+	{	psf->error = SFE_NEGATIVE_RW_LEN ;
+		return 0 ;
+		} ;
+
+	if (psf->file.mode == SFM_WRITE)
+	{	psf->error = SFE_NOT_READMODE ;
+		return 0 ;
+		} ;
+
+	if (psf->read_current >= psf->sf.frames)
+	{	psf_memset (ptr, 0, frames * psf->sf.channels * sizeof (float)) ;
+		return 0 ;
+		} ;
+
+	if (psf->read_float == NULL || psf->seek == NULL)
+	{	psf->error = SFE_UNIMPLEMENTED ;
+		return	0 ;
+		} ;
+
+	if (psf->last_op != SFM_READ)
+		if (psf->seek (psf, SFM_READ, psf->read_current) < 0)
+			return 0 ;
+
+	count = psf->read_float (psf, ptr, frames * psf->sf.channels) ;
+
+	if (psf->read_current + count / psf->sf.channels <= psf->sf.frames)
+		psf->read_current += count / psf->sf.channels ;
+	else
+	{	count = (psf->sf.frames - psf->read_current) * psf->sf.channels ;
+		extra = frames * psf->sf.channels - count ;
+		psf_memset (ptr + count, 0, extra * sizeof (float)) ;
+		psf->read_current = psf->sf.frames ;
+		} ;
+
+	psf->last_op = SFM_READ ;
+
+	return count / psf->sf.channels ;
+} /* sf_readf_float */
+
+/*------------------------------------------------------------------------------
+*/
+
+sf_count_t
+sf_read_double	(SNDFILE *sndfile, double *ptr, sf_count_t len)
+{	SF_PRIVATE 	*psf ;
+	sf_count_t	count, extra ;
+
+	if (len == 0)
+		return 0 ;
+
+	VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ;
+
+	if (len <= 0)
+	{	psf->error = SFE_NEGATIVE_RW_LEN ;
+		return 0 ;
+		} ;
+
+	if (psf->file.mode == SFM_WRITE)
+	{	psf->error = SFE_NOT_READMODE ;
+		return 0 ;
+		} ;
+
+	if (len % psf->sf.channels)
+	{	psf->error = SFE_BAD_READ_ALIGN ;
+		return 0 ;
+		} ;
+
+	if (psf->read_current >= psf->sf.frames)
+	{	psf_memset (ptr, 0, len * sizeof (double)) ;
+		return 0 ;
+		} ;
+
+	if (psf->read_double == NULL || psf->seek == NULL)
+	{	psf->error = SFE_UNIMPLEMENTED ;
+		return	0 ;
+		} ;
+
+	if (psf->last_op != SFM_READ)
+		if (psf->seek (psf, SFM_READ, psf->read_current) < 0)
+			return 0 ;
+
+	count = psf->read_double (psf, ptr, len) ;
+
+	if (psf->read_current + count / psf->sf.channels <= psf->sf.frames)
+		psf->read_current += count / psf->sf.channels ;
+	else
+	{	count = (psf->sf.frames - psf->read_current) * psf->sf.channels ;
+		extra = len - count ;
+		psf_memset (ptr + count, 0, extra * sizeof (double)) ;
+		psf->read_current = psf->sf.frames ;
+		} ;
+
+	psf->last_op = SFM_READ ;
+
+	return count ;
+} /* sf_read_double */
+
+sf_count_t
+sf_readf_double	(SNDFILE *sndfile, double *ptr, sf_count_t frames)
+{	SF_PRIVATE 	*psf ;
+	sf_count_t	count, extra ;
+
+	if (frames == 0)
+		return 0 ;
+
+	VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ;
+
+	if (frames <= 0)
+	{	psf->error = SFE_NEGATIVE_RW_LEN ;
+		return 0 ;
+		} ;
+
+	if (psf->file.mode == SFM_WRITE)
+	{	psf->error = SFE_NOT_READMODE ;
+		return 0 ;
+		} ;
+
+	if (psf->read_current >= psf->sf.frames)
+	{	psf_memset (ptr, 0, frames * psf->sf.channels * sizeof (double)) ;
+		return 0 ;
+		} ;
+
+	if (psf->read_double == NULL || psf->seek == NULL)
+	{	psf->error = SFE_UNIMPLEMENTED ;
+		return	0 ;
+		} ;
+
+	if (psf->last_op != SFM_READ)
+		if (psf->seek (psf, SFM_READ, psf->read_current) < 0)
+			return 0 ;
+
+	count = psf->read_double (psf, ptr, frames * psf->sf.channels) ;
+
+	if (psf->read_current + count / psf->sf.channels <= psf->sf.frames)
+		psf->read_current += count / psf->sf.channels ;
+	else
+	{	count = (psf->sf.frames - psf->read_current) * psf->sf.channels ;
+		extra = frames * psf->sf.channels - count ;
+		psf_memset (ptr + count, 0, extra * sizeof (double)) ;
+		psf->read_current = psf->sf.frames ;
+		} ;
+
+	psf->last_op = SFM_READ ;
+
+	return count / psf->sf.channels ;
+} /* sf_readf_double */
+
+/*------------------------------------------------------------------------------
+*/
+
+sf_count_t
+sf_write_raw	(SNDFILE *sndfile, const void *ptr, sf_count_t len)
+{	SF_PRIVATE 	*psf ;
+	sf_count_t	count ;
+	int			bytewidth, blockwidth ;
+
+	if (len == 0)
+		return 0 ;
+
+	VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ;
+
+	if (len <= 0)
+	{	psf->error = SFE_NEGATIVE_RW_LEN ;
+		return 0 ;
+		} ;
+
+	bytewidth = (psf->bytewidth > 0) ? psf->bytewidth : 1 ;
+	blockwidth = (psf->blockwidth > 0) ? psf->blockwidth : 1 ;
+
+	if (psf->file.mode == SFM_READ)
+	{	psf->error = SFE_NOT_WRITEMODE ;
+		return 0 ;
+		} ;
+
+	if (len % (psf->sf.channels * bytewidth))
+	{	psf->error = SFE_BAD_WRITE_ALIGN ;
+		return 0 ;
+		} ;
+
+	if (psf->last_op != SFM_WRITE)
+		if (psf->seek (psf, SFM_WRITE, psf->write_current) < 0)
+			return 0 ;
+
+	if (psf->have_written == SF_FALSE && psf->write_header != NULL)
+	{	if ((psf->error = psf->write_header (psf, SF_FALSE)))
+			return 0 ;
+		} ;
+	psf->have_written = SF_TRUE ;
+
+	count = psf_fwrite (ptr, 1, len, psf) ;
+
+	psf->write_current += count / blockwidth ;
+
+	psf->last_op = SFM_WRITE ;
+
+	if (psf->write_current > psf->sf.frames)
+	{	psf->sf.frames = psf->write_current ;
+		psf->dataend = 0 ;
+		} ;
+
+	if (psf->auto_header && psf->write_header != NULL)
+		psf->write_header (psf, SF_TRUE) ;
+
+	return count ;
+} /* sf_write_raw */
+
+/*------------------------------------------------------------------------------
+*/
+
+sf_count_t
+sf_write_short	(SNDFILE *sndfile, const short *ptr, sf_count_t len)
+{	SF_PRIVATE 	*psf ;
+	sf_count_t	count ;
+
+	if (len == 0)
+		return 0 ;
+
+	VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ;
+
+	if (len <= 0)
+	{	psf->error = SFE_NEGATIVE_RW_LEN ;
+		return 0 ;
+		} ;
+
+	if (psf->file.mode == SFM_READ)
+	{	psf->error = SFE_NOT_WRITEMODE ;
+		return 0 ;
+		} ;
+
+	if (len % psf->sf.channels)
+	{	psf->error = SFE_BAD_WRITE_ALIGN ;
+		return 0 ;
+		} ;
+
+	if (psf->write_short == NULL || psf->seek == NULL)
+	{	psf->error = SFE_UNIMPLEMENTED ;
+		return 0 ;
+		} ;
+
+	if (psf->last_op != SFM_WRITE)
+		if (psf->seek (psf, SFM_WRITE, psf->write_current) < 0)
+			return 0 ;
+
+	if (psf->have_written == SF_FALSE && psf->write_header != NULL)
+	{	if ((psf->error = psf->write_header (psf, SF_FALSE)))
+			return 0 ;
+		} ;
+	psf->have_written = SF_TRUE ;
+
+	count = psf->write_short (psf, ptr, len) ;
+
+	psf->write_current += count / psf->sf.channels ;
+
+	psf->last_op = SFM_WRITE ;
+
+	if (psf->write_current > psf->sf.frames)
+	{	psf->sf.frames = psf->write_current ;
+		psf->dataend = 0 ;
+		} ;
+
+	if (psf->auto_header && psf->write_header != NULL)
+		psf->write_header (psf, SF_TRUE) ;
+
+	return count ;
+} /* sf_write_short */
+
+sf_count_t
+sf_writef_short	(SNDFILE *sndfile, const short *ptr, sf_count_t frames)
+{	SF_PRIVATE 	*psf ;
+	sf_count_t	count ;
+
+	if (frames == 0)
+		return 0 ;
+
+	VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ;
+
+	if (frames <= 0)
+	{	psf->error = SFE_NEGATIVE_RW_LEN ;
+		return 0 ;
+		} ;
+
+	if (psf->file.mode == SFM_READ)
+	{	psf->error = SFE_NOT_WRITEMODE ;
+		return 0 ;
+		} ;
+
+	if (psf->write_short == NULL || psf->seek == NULL)
+	{	psf->error = SFE_UNIMPLEMENTED ;
+		return 0 ;
+		} ;
+
+	if (psf->last_op != SFM_WRITE)
+		if (psf->seek (psf, SFM_WRITE, psf->write_current) < 0)
+			return 0 ;
+
+	if (psf->have_written == SF_FALSE && psf->write_header != NULL)
+	{	if ((psf->error = psf->write_header (psf, SF_FALSE)))
+			return 0 ;
+		} ;
+	psf->have_written = SF_TRUE ;
+
+	count = psf->write_short (psf, ptr, frames * psf->sf.channels) ;
+
+	psf->write_current += count / psf->sf.channels ;
+
+	psf->last_op = SFM_WRITE ;
+
+	if (psf->write_current > psf->sf.frames)
+	{	psf->sf.frames = psf->write_current ;
+		psf->dataend = 0 ;
+		} ;
+
+	if (psf->auto_header && psf->write_header != NULL)
+		psf->write_header (psf, SF_TRUE) ;
+
+	return count / psf->sf.channels ;
+} /* sf_writef_short */
+
+/*------------------------------------------------------------------------------
+*/
+
+sf_count_t
+sf_write_int	(SNDFILE *sndfile, const int *ptr, sf_count_t len)
+{	SF_PRIVATE 	*psf ;
+	sf_count_t	count ;
+
+	if (len == 0)
+		return 0 ;
+
+	VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ;
+
+	if (len <= 0)
+	{	psf->error = SFE_NEGATIVE_RW_LEN ;
+		return 0 ;
+		} ;
+
+	if (psf->file.mode == SFM_READ)
+	{	psf->error = SFE_NOT_WRITEMODE ;
+		return 0 ;
+		} ;
+
+	if (len % psf->sf.channels)
+	{	psf->error = SFE_BAD_WRITE_ALIGN ;
+		return 0 ;
+		} ;
+
+	if (psf->write_int == NULL || psf->seek == NULL)
+	{	psf->error = SFE_UNIMPLEMENTED ;
+		return 0 ;
+		} ;
+
+	if (psf->last_op != SFM_WRITE)
+		if (psf->seek (psf, SFM_WRITE, psf->write_current) < 0)
+			return 0 ;
+
+	if (psf->have_written == SF_FALSE && psf->write_header != NULL)
+	{	if ((psf->error = psf->write_header (psf, SF_FALSE)))
+			return 0 ;
+		} ;
+	psf->have_written = SF_TRUE ;
+
+	count = psf->write_int (psf, ptr, len) ;
+
+	psf->write_current += count / psf->sf.channels ;
+
+	psf->last_op = SFM_WRITE ;
+
+	if (psf->write_current > psf->sf.frames)
+	{	psf->sf.frames = psf->write_current ;
+		psf->dataend = 0 ;
+		} ;
+
+	if (psf->auto_header && psf->write_header != NULL)
+		psf->write_header (psf, SF_TRUE) ;
+
+	return count ;
+} /* sf_write_int */
+
+sf_count_t
+sf_writef_int	(SNDFILE *sndfile, const int *ptr, sf_count_t frames)
+{	SF_PRIVATE 	*psf ;
+	sf_count_t	count ;
+
+	if (frames == 0)
+		return 0 ;
+
+	VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ;
+
+	if (frames <= 0)
+	{	psf->error = SFE_NEGATIVE_RW_LEN ;
+		return 0 ;
+		} ;
+
+	if (psf->file.mode == SFM_READ)
+	{	psf->error = SFE_NOT_WRITEMODE ;
+		return 0 ;
+		} ;
+
+	if (psf->write_int == NULL || psf->seek == NULL)
+	{	psf->error = SFE_UNIMPLEMENTED ;
+		return 0 ;
+		} ;
+
+	if (psf->last_op != SFM_WRITE)
+		if (psf->seek (psf, SFM_WRITE, psf->write_current) < 0)
+			return 0 ;
+
+	if (psf->have_written == SF_FALSE && psf->write_header != NULL)
+	{	if ((psf->error = psf->write_header (psf, SF_FALSE)))
+			return 0 ;
+		} ;
+	psf->have_written = SF_TRUE ;
+
+	count = psf->write_int (psf, ptr, frames * psf->sf.channels) ;
+
+	psf->write_current += count / psf->sf.channels ;
+
+	psf->last_op = SFM_WRITE ;
+
+	if (psf->write_current > psf->sf.frames)
+	{	psf->sf.frames = psf->write_current ;
+		psf->dataend = 0 ;
+		} ;
+
+	if (psf->auto_header && psf->write_header != NULL)
+		psf->write_header (psf, SF_TRUE) ;
+
+	return count / psf->sf.channels ;
+} /* sf_writef_int */
+
+/*------------------------------------------------------------------------------
+*/
+
+sf_count_t
+sf_write_float	(SNDFILE *sndfile, const float *ptr, sf_count_t len)
+{	SF_PRIVATE 	*psf ;
+	sf_count_t	count ;
+
+	if (len == 0)
+		return 0 ;
+
+	VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ;
+
+	if (len <= 0)
+	{	psf->error = SFE_NEGATIVE_RW_LEN ;
+		return 0 ;
+		} ;
+
+	if (psf->file.mode == SFM_READ)
+	{	psf->error = SFE_NOT_WRITEMODE ;
+		return 0 ;
+		} ;
+
+	if (len % psf->sf.channels)
+	{	psf->error = SFE_BAD_WRITE_ALIGN ;
+		return 0 ;
+		} ;
+
+	if (psf->write_float == NULL || psf->seek == NULL)
+	{	psf->error = SFE_UNIMPLEMENTED ;
+		return 0 ;
+		} ;
+
+	if (psf->last_op != SFM_WRITE)
+		if (psf->seek (psf, SFM_WRITE, psf->write_current) < 0)
+			return 0 ;
+
+	if (psf->have_written == SF_FALSE && psf->write_header != NULL)
+	{	if ((psf->error = psf->write_header (psf, SF_FALSE)))
+			return 0 ;
+		} ;
+	psf->have_written = SF_TRUE ;
+
+	count = psf->write_float (psf, ptr, len) ;
+
+	psf->write_current += count / psf->sf.channels ;
+
+	psf->last_op = SFM_WRITE ;
+
+	if (psf->write_current > psf->sf.frames)
+	{	psf->sf.frames = psf->write_current ;
+		psf->dataend = 0 ;
+		} ;
+
+	if (psf->auto_header && psf->write_header != NULL)
+		psf->write_header (psf, SF_TRUE) ;
+
+	return count ;
+} /* sf_write_float */
+
+sf_count_t
+sf_writef_float	(SNDFILE *sndfile, const float *ptr, sf_count_t frames)
+{	SF_PRIVATE 	*psf ;
+	sf_count_t	count ;
+
+	if (frames == 0)
+		return 0 ;
+
+	VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ;
+
+	if (frames <= 0)
+	{	psf->error = SFE_NEGATIVE_RW_LEN ;
+		return 0 ;
+		} ;
+
+	if (psf->file.mode == SFM_READ)
+	{	psf->error = SFE_NOT_WRITEMODE ;
+		return 0 ;
+		} ;
+
+	if (psf->write_float == NULL || psf->seek == NULL)
+	{	psf->error = SFE_UNIMPLEMENTED ;
+		return 0 ;
+		} ;
+
+	if (psf->last_op != SFM_WRITE)
+		if (psf->seek (psf, SFM_WRITE, psf->write_current) < 0)
+			return 0 ;
+
+	if (psf->have_written == SF_FALSE && psf->write_header != NULL)
+	{	if ((psf->error = psf->write_header (psf, SF_FALSE)))
+			return 0 ;
+		} ;
+	psf->have_written = SF_TRUE ;
+
+	count = psf->write_float (psf, ptr, frames * psf->sf.channels) ;
+
+	psf->write_current += count / psf->sf.channels ;
+
+	psf->last_op = SFM_WRITE ;
+
+	if (psf->write_current > psf->sf.frames)
+	{	psf->sf.frames = psf->write_current ;
+		psf->dataend = 0 ;
+		} ;
+
+	if (psf->auto_header && psf->write_header != NULL)
+		psf->write_header (psf, SF_TRUE) ;
+
+	return count / psf->sf.channels ;
+} /* sf_writef_float */
+
+/*------------------------------------------------------------------------------
+*/
+
+sf_count_t
+sf_write_double	(SNDFILE *sndfile, const double *ptr, sf_count_t len)
+{	SF_PRIVATE 	*psf ;
+	sf_count_t	count ;
+
+	if (len == 0)
+		return 0 ;
+
+	VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ;
+
+	if (len <= 0)
+	{	psf->error = SFE_NEGATIVE_RW_LEN ;
+		return 0 ;
+		} ;
+
+	if (psf->file.mode == SFM_READ)
+	{	psf->error = SFE_NOT_WRITEMODE ;
+		return 0 ;
+		} ;
+
+	if (len % psf->sf.channels)
+	{	psf->error = SFE_BAD_WRITE_ALIGN ;
+		return	0 ;
+		} ;
+
+	if (psf->write_double == NULL || psf->seek == NULL)
+	{	psf->error = SFE_UNIMPLEMENTED ;
+		return 0 ;
+		} ;
+
+	if (psf->last_op != SFM_WRITE)
+		if (psf->seek (psf, SFM_WRITE, psf->write_current) < 0)
+			return 0 ;
+
+	if (psf->have_written == SF_FALSE && psf->write_header != NULL)
+	{	if ((psf->error = psf->write_header (psf, SF_FALSE)))
+			return 0 ;
+		} ;
+	psf->have_written = SF_TRUE ;
+
+	count = psf->write_double (psf, ptr, len) ;
+
+	psf->write_current += count / psf->sf.channels ;
+
+	psf->last_op = SFM_WRITE ;
+
+	if (psf->write_current > psf->sf.frames)
+	{	psf->sf.frames = psf->write_current ;
+		psf->dataend = 0 ;
+		} ;
+
+	if (psf->auto_header && psf->write_header != NULL)
+		psf->write_header (psf, SF_TRUE) ;
+
+	return count ;
+} /* sf_write_double */
+
+sf_count_t
+sf_writef_double	(SNDFILE *sndfile, const double *ptr, sf_count_t frames)
+{	SF_PRIVATE 	*psf ;
+	sf_count_t	count ;
+
+	if (frames == 0)
+		return 0 ;
+
+	VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ;
+
+	if (frames <= 0)
+	{	psf->error = SFE_NEGATIVE_RW_LEN ;
+		return 0 ;
+		} ;
+
+	if (psf->file.mode == SFM_READ)
+	{	psf->error = SFE_NOT_WRITEMODE ;
+		return 0 ;
+		} ;
+
+	if (psf->write_double == NULL || psf->seek == NULL)
+	{	psf->error = SFE_UNIMPLEMENTED ;
+		return 0 ;
+		} ;
+
+	if (psf->last_op != SFM_WRITE)
+		if (psf->seek (psf, SFM_WRITE, psf->write_current) < 0)
+			return 0 ;
+
+	if (psf->have_written == SF_FALSE && psf->write_header != NULL)
+	{	if ((psf->error = psf->write_header (psf, SF_FALSE)))
+			return 0 ;
+		} ;
+	psf->have_written = SF_TRUE ;
+
+	count = psf->write_double (psf, ptr, frames * psf->sf.channels) ;
+
+	psf->write_current += count / psf->sf.channels ;
+
+	psf->last_op = SFM_WRITE ;
+
+	if (psf->write_current > psf->sf.frames)
+	{	psf->sf.frames = psf->write_current ;
+		psf->dataend = 0 ;
+		} ;
+
+	if (psf->auto_header && psf->write_header != NULL)
+		psf->write_header (psf, SF_TRUE) ;
+
+	return count / psf->sf.channels ;
+} /* sf_writef_double */
+
+/*=========================================================================
+** Private functions.
+*/
+
+static int
+try_resource_fork (SF_PRIVATE * psf)
+{	int old_error = psf->error ;
+
+	/* Set READ mode now, to see if resource fork exists. */
+	psf->rsrc.mode = SFM_READ ;
+	if (psf_open_rsrc (psf) != 0)
+	{	psf->error = old_error ;
+		return 0 ;
+		} ;
+
+	/* More checking here. */
+	psf_log_printf (psf, "Resource fork : %s\n", psf->rsrc.path) ;
+
+	return SF_FORMAT_SD2 ;
+} /* try_resource_fork */
+
+static int
+format_from_extension (SF_PRIVATE *psf)
+{	char *cptr ;
+	char buffer [16] ;
+	int format = 0 ;
+
+	if ((cptr = strrchr (psf->file.name, '.')) == NULL)
+		return 0 ;
+
+	cptr ++ ;
+	if (strlen (cptr) > sizeof (buffer) - 1)
+		return 0 ;
+
+	psf_strlcpy (buffer, sizeof (buffer), cptr) ;
+	buffer [sizeof (buffer) - 1] = 0 ;
+
+	/* Convert everything in the buffer to lower case. */
+	cptr = buffer ;
+	while (*cptr)
+	{	*cptr = tolower (*cptr) ;
+		cptr ++ ;
+		} ;
+
+	cptr = buffer ;
+
+	if (strcmp (cptr, "au") == 0)
+	{	psf->sf.channels = 1 ;
+		psf->sf.samplerate = 8000 ;
+		format = SF_FORMAT_RAW | SF_FORMAT_ULAW ;
+		}
+	else if (strcmp (cptr, "snd") == 0)
+	{	psf->sf.channels = 1 ;
+		psf->sf.samplerate = 8000 ;
+		format = SF_FORMAT_RAW | SF_FORMAT_ULAW ;
+		}
+
+	else if (strcmp (cptr, "vox") == 0 || strcmp (cptr, "vox8") == 0)
+	{	psf->sf.channels = 1 ;
+		psf->sf.samplerate = 8000 ;
+		format = SF_FORMAT_RAW | SF_FORMAT_VOX_ADPCM ;
+		}
+	else if (strcmp (cptr, "vox6") == 0)
+	{	psf->sf.channels = 1 ;
+		psf->sf.samplerate = 6000 ;
+		format = SF_FORMAT_RAW | SF_FORMAT_VOX_ADPCM ;
+		}
+	else if (strcmp (cptr, "gsm") == 0)
+	{	psf->sf.channels = 1 ;
+		psf->sf.samplerate = 8000 ;
+		format = SF_FORMAT_RAW | SF_FORMAT_GSM610 ;
+		}
+	else if (strcmp (cptr, "mp3") == 0)
+	{	/*
+		 * MPEG streams are quite tollerate of crap. If we couldn't identify a
+		 * MP3 stream, but it has a .mp3 extension, let libmpg123 have a try.
+		 */
+		format = SF_FORMAT_MPEG ;
+		}
+
+	/* For RAW files, make sure the dataoffset if set correctly. */
+	if ((SF_CONTAINER (format)) == SF_FORMAT_RAW)
+		psf->dataoffset = 0 ;
+
+	return format ;
+} /* format_from_extension */
+
+static int
+identify_mpeg (uint32_t marker)
+{	if ((marker & MAKE_MARKER (0xFF, 0xE0, 0, 0)) == MAKE_MARKER (0xFF, 0xE0, 0, 0) && /* Frame sync */
+		(marker & MAKE_MARKER (0, 0x18, 0, 0)) != MAKE_MARKER (0, 0x08, 0, 0) && /* Valid MPEG version */
+		(marker & MAKE_MARKER (0, 0x06, 0, 0)) != MAKE_MARKER (0, 0, 0, 0) && /* Valid layer description */
+		(marker & MAKE_MARKER (0, 0, 0xF0, 0)) != MAKE_MARKER (0, 0, 0xF0, 0) && /* Valid bitrate */
+		(marker & MAKE_MARKER (0, 0, 0x0C, 0)) != MAKE_MARKER (0, 0, 0x0C, 0)) /* Valid samplerate */
+		return SF_FORMAT_MPEG ;
+	return 0 ;
+} /* identify_mpeg */
+
+static int
+guess_file_type (SF_PRIVATE *psf)
+{	uint32_t buffer [3], format ;
+
+retry:
+	if (psf_binheader_readf (psf, "b", &buffer, SIGNED_SIZEOF (buffer)) != SIGNED_SIZEOF (buffer))
+	{	psf->error = SFE_BAD_FILE_READ ;
+		return 0 ;
+		} ;
+
+	if ((buffer [0] == MAKE_MARKER ('R', 'I', 'F', 'F') || buffer [0] == MAKE_MARKER ('R', 'I', 'F', 'X'))
+			&& buffer [2] == MAKE_MARKER ('W', 'A', 'V', 'E'))
+		return SF_FORMAT_WAV ;
+
+	if (buffer [0] == MAKE_MARKER ('F', 'O', 'R', 'M'))
+	{	if (buffer [2] == MAKE_MARKER ('A', 'I', 'F', 'F') || buffer [2] == MAKE_MARKER ('A', 'I', 'F', 'C'))
+			return SF_FORMAT_AIFF ;
+		if (buffer [2] == MAKE_MARKER ('8', 'S', 'V', 'X') || buffer [2] == MAKE_MARKER ('1', '6', 'S', 'V'))
+			return SF_FORMAT_SVX ;
+		return 0 ;
+		} ;
+
+	if (buffer [0] == MAKE_MARKER ('.', 's', 'n', 'd') || buffer [0] == MAKE_MARKER ('d', 'n', 's', '.'))
+		return SF_FORMAT_AU ;
+
+	if ((buffer [0] == MAKE_MARKER ('f', 'a', 'p', ' ') || buffer [0] == MAKE_MARKER (' ', 'p', 'a', 'f')))
+		return SF_FORMAT_PAF ;
+
+	if (buffer [0] == MAKE_MARKER ('N', 'I', 'S', 'T'))
+		return SF_FORMAT_NIST ;
+
+	if (buffer [0] == MAKE_MARKER ('C', 'r', 'e', 'a') && buffer [1] == MAKE_MARKER ('t', 'i', 'v', 'e'))
+		return SF_FORMAT_VOC ;
+
+	if ((buffer [0] & MAKE_MARKER (0xFF, 0xFF, 0xF8, 0xFF)) == MAKE_MARKER (0x64, 0xA3, 0x00, 0x00) ||
+		(buffer [0] & MAKE_MARKER (0xFF, 0xF8, 0xFF, 0xFF)) == MAKE_MARKER (0x00, 0x00, 0xA3, 0x64))
+		return SF_FORMAT_IRCAM ;
+
+	if (buffer [0] == MAKE_MARKER ('r', 'i', 'f', 'f'))
+		return SF_FORMAT_W64 ;
+
+	if (buffer [0] == MAKE_MARKER (0, 0, 0x03, 0xE8) && buffer [1] == MAKE_MARKER (0, 0, 0, 1) &&
+								buffer [2] == MAKE_MARKER (0, 0, 0, 1))
+		return SF_FORMAT_MAT4 ;
+
+	if (buffer [0] == MAKE_MARKER (0, 0, 0, 0) && buffer [1] == MAKE_MARKER (1, 0, 0, 0) &&
+								buffer [2] == MAKE_MARKER (1, 0, 0, 0))
+		return SF_FORMAT_MAT4 ;
+
+	if (buffer [0] == MAKE_MARKER ('M', 'A', 'T', 'L') && buffer [1] == MAKE_MARKER ('A', 'B', ' ', '5'))
+		return SF_FORMAT_MAT5 ;
+
+	if (buffer [0] == MAKE_MARKER ('P', 'V', 'F', '1'))
+		return SF_FORMAT_PVF ;
+
+	if (buffer [0] == MAKE_MARKER ('E', 'x', 't', 'e') && buffer [1] == MAKE_MARKER ('n', 'd', 'e', 'd') &&
+								buffer [2] == MAKE_MARKER (' ', 'I', 'n', 's'))
+		return SF_FORMAT_XI ;
+
+	if (buffer [0] == MAKE_MARKER ('c', 'a', 'f', 'f') && buffer [2] == MAKE_MARKER ('d', 'e', 's', 'c'))
+		return SF_FORMAT_CAF ;
+
+	if (buffer [0] == MAKE_MARKER ('O', 'g', 'g', 'S'))
+		return SF_FORMAT_OGG ;
+
+	if (buffer [0] == MAKE_MARKER ('A', 'L', 'a', 'w') && buffer [1] == MAKE_MARKER ('S', 'o', 'u', 'n')
+			&& buffer [2] == MAKE_MARKER ('d', 'F', 'i', 'l'))
+		return SF_FORMAT_WVE ;
+
+	if (buffer [0] == MAKE_MARKER ('D', 'i', 'a', 'm') && buffer [1] == MAKE_MARKER ('o', 'n', 'd', 'W')
+			&& buffer [2] == MAKE_MARKER ('a', 'r', 'e', ' '))
+		return SF_FORMAT_DWD ;
+
+	if (buffer [0] == MAKE_MARKER ('L', 'M', '8', '9') || buffer [0] == MAKE_MARKER ('5', '3', 0, 0))
+		return SF_FORMAT_TXW ;
+
+	if ((buffer [0] & MAKE_MARKER (0xFF, 0xFF, 0x80, 0xFF)) == MAKE_MARKER (0xF0, 0x7E, 0, 0x01))
+		return SF_FORMAT_SDS ;
+
+	if ((buffer [0] & MAKE_MARKER (0xFF, 0xFF, 0, 0)) == MAKE_MARKER (1, 4, 0, 0))
+		return SF_FORMAT_MPC2K ;
+
+	if (buffer [0] == MAKE_MARKER ('C', 'A', 'T', ' ') && buffer [2] == MAKE_MARKER ('R', 'E', 'X', '2'))
+		return SF_FORMAT_REX2 ;
+
+	if (buffer [0] == MAKE_MARKER (0x30, 0x26, 0xB2, 0x75) && buffer [1] == MAKE_MARKER (0x8E, 0x66, 0xCF, 0x11))
+		return 0 /*-SF_FORMAT_WMA-*/ ;
+
+	/* HMM (Hidden Markov Model) Tool Kit. */
+	if (buffer [2] == MAKE_MARKER (0, 2, 0, 0) && 2 * ((int64_t) BE2H_32 (buffer [0])) + 12 == psf->filelength)
+		return SF_FORMAT_HTK ;
+
+	if (buffer [0] == MAKE_MARKER ('f', 'L', 'a', 'C'))
+		return SF_FORMAT_FLAC ;
+
+	if (buffer [0] == MAKE_MARKER ('2', 'B', 'I', 'T'))
+		return SF_FORMAT_AVR ;
+
+	if (buffer [0] == MAKE_MARKER ('R', 'F', '6', '4') && buffer [2] == MAKE_MARKER ('W', 'A', 'V', 'E'))
+		return SF_FORMAT_RF64 ;
+
+	if (buffer [0] == MAKE_MARKER ('I', 'D', '3', 2) || buffer [0] == MAKE_MARKER ('I', 'D', '3', 3)
+			|| buffer [0] == MAKE_MARKER ('I', 'D', '3', 4))
+	{	psf_log_printf (psf, "Found 'ID3' marker.\n") ;
+		if (id3_skip (psf))
+			goto retry ;
+		return 0 ;
+		} ;
+
+	/* ID3v2 tags + MPEG */
+	if (psf->id3_header.len > 0 && (format = identify_mpeg (buffer [0])) != 0)
+		return format ;
+
+	/* Turtle Beach SMP 16-bit */
+	if (buffer [0] == MAKE_MARKER ('S', 'O', 'U', 'N') && buffer [1] == MAKE_MARKER ('D', ' ', 'S', 'A'))
+		return 0 ;
+
+	/* Yamaha sampler format. */
+	if (buffer [0] == MAKE_MARKER ('S', 'Y', '8', '0') || buffer [0] == MAKE_MARKER ('S', 'Y', '8', '5'))
+		return 0 ;
+
+	if (buffer [0] == MAKE_MARKER ('a', 'j', 'k', 'g'))
+		return 0 /*-SF_FORMAT_SHN-*/ ;
+
+	/* This must be (almost) the last one. */
+	if (psf->filelength > 0 && (format = try_resource_fork (psf)) != 0)
+		return format ;
+
+	/* MPEG with no ID3v2 tags. Only have the MPEG sync header for
+	 * identification and it is quite brief, and prone to false positives.
+	 * Check for this last, even after resource forks. */
+	if (psf->id3_header.len == 0 && (format = identify_mpeg (buffer [0])) != 0)
+		return format ;
+
+	return 0 ;
+} /* guess_file_type */
+
+
+static int
+validate_sfinfo (SF_INFO *sfinfo)
+{	if (sfinfo->samplerate < 1)
+		return 0 ;
+	if (sfinfo->frames < 0)
+		return 0 ;
+	if ((sfinfo->channels < 1) || (sfinfo->channels > SF_MAX_CHANNELS))
+		return 0 ;
+	if ((SF_CONTAINER (sfinfo->format)) == 0)
+		return 0 ;
+	if ((SF_CODEC (sfinfo->format)) == 0)
+		return 0 ;
+	if (sfinfo->sections < 1)
+		return 0 ;
+	return 1 ;
+} /* validate_sfinfo */
+
+static int
+validate_psf (SF_PRIVATE *psf)
+{
+	if (psf->datalength < 0)
+	{	psf_log_printf (psf, "Invalid SF_PRIVATE field : datalength == %D.\n", psf->datalength) ;
+		return 0 ;
+		} ;
+	if (psf->dataoffset < 0)
+	{	psf_log_printf (psf, "Invalid SF_PRIVATE field : dataoffset == %D.\n", psf->dataoffset) ;
+		return 0 ;
+		} ;
+	if (psf->blockwidth && psf->blockwidth != psf->sf.channels * psf->bytewidth)
+	{	psf_log_printf (psf, "Invalid SF_PRIVATE field : channels * bytewidth == %d.\n",
+								psf->sf.channels * psf->bytewidth) ;
+		return 0 ;
+		} ;
+	return 1 ;
+} /* validate_psf */
+
+static void
+save_header_info (SF_PRIVATE *psf)
+{	snprintf (sf_parselog, sizeof (sf_parselog), "%s", psf->parselog.buf) ;
+} /* save_header_info */
+
+/*==============================================================================
+*/
+
+static int
+psf_close (SF_PRIVATE *psf)
+{	uint32_t k ;
+	int	error = 0 ;
+
+	if (psf->codec_close)
+	{	error = psf->codec_close (psf) ;
+		/* To prevent it being called in psf->container_close(). */
+		psf->codec_close = NULL ;
+		} ;
+
+	if (psf->container_close)
+		error = psf->container_close (psf) ;
+
+	error = psf_fclose (psf) ;
+	psf_close_rsrc (psf) ;
+
+	/* For an ISO C compliant implementation it is ok to free a NULL pointer. */
+	free (psf->header.ptr) ;
+	free (psf->container_data) ;
+	free (psf->codec_data) ;
+	free (psf->interleave) ;
+	free (psf->dither) ;
+	free (psf->peak_info) ;
+	free (psf->broadcast_16k) ;
+	free (psf->loop_info) ;
+	free (psf->instrument) ;
+	free (psf->cues) ;
+	free (psf->channel_map) ;
+	free (psf->format_desc) ;
+	free (psf->strings.storage) ;
+
+	if (psf->wchunks.chunks)
+		for (k = 0 ; k < psf->wchunks.used ; k++)
+			free (psf->wchunks.chunks [k].data) ;
+	free (psf->rchunks.chunks) ;
+	free (psf->wchunks.chunks) ;
+	free (psf->iterator) ;
+	free (psf->cart_16k) ;
+
+	free (psf) ;
+
+	return error ;
+} /* psf_close */
+
+SNDFILE *
+psf_open_file (SF_PRIVATE *psf, SF_INFO *sfinfo)
+{	int		error, format ;
+
+	sf_errno = error = 0 ;
+	sf_parselog [0] = 0 ;
+
+	if (psf->error)
+	{	error = psf->error ;
+		goto error_exit ;
+		} ;
+
+	if (psf->file.mode != SFM_READ && psf->file.mode != SFM_WRITE && psf->file.mode != SFM_RDWR)
+	{	error = SFE_BAD_OPEN_MODE ;
+		goto error_exit ;
+		} ;
+
+	if (sfinfo == NULL)
+	{	error = SFE_BAD_SF_INFO_PTR ;
+		goto error_exit ;
+		} ;
+
+	if (psf->file.mode == SFM_READ)
+	{	if ((SF_CONTAINER (sfinfo->format)) == SF_FORMAT_RAW)
+		{	if (sf_format_check (sfinfo) == 0)
+			{	error = SFE_RAW_BAD_FORMAT ;
+				goto error_exit ;
+				} ;
+			}
+		else
+			memset (sfinfo, 0, sizeof (SF_INFO)) ;
+		} ;
+
+	memcpy (&psf->sf, sfinfo, sizeof (SF_INFO)) ;
+
+	psf->Magick 		= SNDFILE_MAGICK ;
+	psf->norm_float 	= SF_TRUE ;
+	psf->norm_double	= SF_TRUE ;
+	psf->dataoffset		= -1 ;
+	psf->datalength		= -1 ;
+	psf->read_current	= -1 ;
+	psf->write_current	= -1 ;
+	psf->auto_header 	= SF_FALSE ;
+	psf->rwf_endian		= SF_ENDIAN_LITTLE ;
+	psf->seek			= psf_default_seek ;
+	psf->float_int_mult = 0 ;
+	psf->float_max		= -1.0 ;
+
+	/* An attempt at a per SF_PRIVATE unique id. */
+	psf->unique_id		= psf_rand_int32 () ;
+
+	psf->sf.sections = 1 ;
+
+	psf->is_pipe = psf_is_pipe (psf) ;
+
+	if (psf->is_pipe)
+	{	psf->sf.seekable = SF_FALSE ;
+		psf->filelength = SF_COUNT_MAX ;
+		}
+	else
+	{	psf->sf.seekable = SF_TRUE ;
+
+		/* File is open, so get the length. */
+		psf->filelength = psf_get_filelen (psf) ;
+		} ;
+
+	if (psf->fileoffset > 0)
+	{	switch (psf->file.mode)
+		{	case SFM_READ :
+				if (psf->filelength < 44)
+				{	psf_log_printf (psf, "Short filelength: %D (fileoffset: %D)\n", psf->filelength, psf->fileoffset) ;
+					error = SFE_BAD_OFFSET ;
+					goto error_exit ;
+					} ;
+				break ;
+
+			case SFM_WRITE :
+				psf->fileoffset = 0 ;
+				psf_fseek (psf, 0, SEEK_END) ;
+				psf->fileoffset = psf_ftell (psf) ;
+				break ;
+
+			case SFM_RDWR :
+				error = SFE_NO_EMBEDDED_RDWR ;
+				goto error_exit ;
+			} ;
+
+		psf_log_printf (psf, "Embedded file offset : %D\n", psf->fileoffset) ;
+		} ;
+
+	if (psf->filelength == SF_COUNT_MAX)
+		psf_log_printf (psf, "Length : unknown\n") ;
+	else
+		psf_log_printf (psf, "Length : %D\n", psf->filelength) ;
+
+	if (psf->file.mode == SFM_WRITE || (psf->file.mode == SFM_RDWR && psf->filelength == 0))
+	{	/* If the file is being opened for write or RDWR and the file is currently
+		** empty, then the SF_INFO struct must contain valid data.
+		*/
+		if ((SF_CONTAINER (psf->sf.format)) == 0)
+		{	error = SFE_ZERO_MAJOR_FORMAT ;
+			goto error_exit ;
+			} ;
+		if ((SF_CODEC (psf->sf.format)) == 0)
+		{	error = SFE_ZERO_MINOR_FORMAT ;
+			goto error_exit ;
+			} ;
+
+		if (sf_format_check (&psf->sf) == 0)
+		{	error = SFE_BAD_OPEN_FORMAT ;
+			goto error_exit ;
+			} ;
+		}
+	else if ((SF_CONTAINER (psf->sf.format)) != SF_FORMAT_RAW)
+	{	/* If type RAW has not been specified then need to figure out file type. */
+		psf->sf.format = guess_file_type (psf) ;
+
+		if (psf->sf.format == 0)
+			psf->sf.format = format_from_extension (psf) ;
+		} ;
+
+	/* Prevent unnecessary seeks */
+	psf->last_op = psf->file.mode ;
+
+	/* Set bytewidth if known. */
+	switch (SF_CODEC (psf->sf.format))
+	{	case SF_FORMAT_PCM_S8 :
+		case SF_FORMAT_PCM_U8 :
+		case SF_FORMAT_ULAW :
+		case SF_FORMAT_ALAW :
+		case SF_FORMAT_DPCM_8 :
+				psf->bytewidth = 1 ;
+				break ;
+
+		case SF_FORMAT_PCM_16 :
+		case SF_FORMAT_DPCM_16 :
+				psf->bytewidth = 2 ;
+				break ;
+
+		case SF_FORMAT_PCM_24 :
+				psf->bytewidth = 3 ;
+				break ;
+
+		case SF_FORMAT_PCM_32 :
+		case SF_FORMAT_FLOAT :
+				psf->bytewidth = 4 ;
+				break ;
+
+		case SF_FORMAT_DOUBLE :
+				psf->bytewidth = 8 ;
+				break ;
+		} ;
+
+	/* Call the initialisation function for the relevant file type. */
+	switch (SF_CONTAINER (psf->sf.format))
+	{	case	SF_FORMAT_WAV :
+		case	SF_FORMAT_WAVEX :
+				error = wav_open (psf) ;
+				break ;
+
+		case	SF_FORMAT_AIFF :
+				error = aiff_open (psf) ;
+				break ;
+
+		case	SF_FORMAT_AU :
+				error = au_open (psf) ;
+				break ;
+
+		case	SF_FORMAT_RAW :
+				error = raw_open (psf) ;
+				break ;
+
+		case	SF_FORMAT_W64 :
+				error = w64_open (psf) ;
+				break ;
+
+		case	SF_FORMAT_RF64 :
+				error = rf64_open (psf) ;
+				break ;
+
+		/* Lite remove start */
+		case	SF_FORMAT_PAF :
+				error = paf_open (psf) ;
+				break ;
+
+		case	SF_FORMAT_SVX :
+				error = svx_open (psf) ;
+				break ;
+
+		case	SF_FORMAT_NIST :
+				error = nist_open (psf) ;
+				break ;
+
+		case	SF_FORMAT_IRCAM :
+				error = ircam_open (psf) ;
+				break ;
+
+		case	SF_FORMAT_VOC :
+				error = voc_open (psf) ;
+				break ;
+
+		case	SF_FORMAT_SDS :
+				error = sds_open (psf) ;
+				break ;
+
+		case	SF_FORMAT_OGG :
+				error = ogg_open (psf) ;
+				break ;
+
+		case	SF_FORMAT_TXW :
+				error = txw_open (psf) ;
+				break ;
+
+		case	SF_FORMAT_WVE :
+				error = wve_open (psf) ;
+				break ;
+
+		case	SF_FORMAT_DWD :
+				error = dwd_open (psf) ;
+				break ;
+
+		case	SF_FORMAT_MAT4 :
+				error = mat4_open (psf) ;
+				break ;
+
+		case	SF_FORMAT_MAT5 :
+				error = mat5_open (psf) ;
+				break ;
+
+		case	SF_FORMAT_PVF :
+				error = pvf_open (psf) ;
+				break ;
+
+		case	SF_FORMAT_XI :
+				error = xi_open (psf) ;
+				break ;
+
+		case	SF_FORMAT_HTK :
+				error = htk_open (psf) ;
+				break ;
+
+		case	SF_FORMAT_SD2 :
+				error = sd2_open (psf) ;
+				break ;
+
+		case	SF_FORMAT_REX2 :
+				error = rx2_open (psf) ;
+				break ;
+
+		case	SF_FORMAT_AVR :
+				error = avr_open (psf) ;
+				break ;
+
+		case	SF_FORMAT_FLAC :
+				error = flac_open (psf) ;
+				break ;
+
+		case	SF_FORMAT_CAF :
+				error = caf_open (psf) ;
+				break ;
+
+		case	SF_FORMAT_MPC2K :
+				error = mpc2k_open (psf) ;
+				break ;
+
+		case	SF_FORMAT_MPEG :
+				error = mpeg_open (psf) ;
+				break ;
+
+		/* Lite remove end */
+
+		default :
+				error = SF_ERR_UNRECOGNISED_FORMAT ;
+		} ;
+
+	if (error)
+		goto error_exit ;
+
+	/* For now, check whether embedding is supported. */
+	format = SF_CONTAINER (psf->sf.format) ;
+	if (psf->fileoffset > 0)
+	{	switch (format)
+		{	case SF_FORMAT_WAV :
+			case SF_FORMAT_WAVEX :
+			case SF_FORMAT_AIFF :
+			case SF_FORMAT_AU :
+				/* Actual embedded files. */
+				break ;
+
+			case SF_FORMAT_MPEG :
+			case SF_FORMAT_FLAC :
+				/* Flac with an ID3v2 header? */
+				break ;
+
+			default :
+				error = SFE_NO_EMBED_SUPPORT ;
+				goto error_exit ;
+			} ;
+		} ;
+
+	if (psf->fileoffset > 0)
+		psf_log_printf (psf, "Embedded file length : %D\n", psf->filelength) ;
+
+	if (psf->file.mode == SFM_RDWR && sf_format_check (&psf->sf) == 0)
+	{	error = SFE_BAD_MODE_RW ;
+		goto error_exit ;
+		} ;
+
+	if (validate_sfinfo (&psf->sf) == 0)
+	{	psf_log_SF_INFO (psf) ;
+		save_header_info (psf) ;
+		error = SFE_BAD_SF_INFO ;
+		goto error_exit ;
+		} ;
+
+	if (validate_psf (psf) == 0)
+	{	save_header_info (psf) ;
+		error = SFE_INTERNAL ;
+		goto error_exit ;
+		} ;
+
+	psf->read_current = 0 ;
+	psf->write_current = 0 ;
+	if (psf->file.mode == SFM_RDWR)
+	{	psf->write_current = psf->sf.frames ;
+		psf->have_written = psf->sf.frames > 0 ? SF_TRUE : SF_FALSE ;
+		} ;
+
+	memcpy (sfinfo, &psf->sf, sizeof (SF_INFO)) ;
+
+	if (psf->file.mode == SFM_WRITE)
+	{	/* Zero out these fields. */
+		sfinfo->frames = 0 ;
+		sfinfo->sections = 0 ;
+		sfinfo->seekable = 0 ;
+		} ;
+
+	return (SNDFILE *) psf ;
+
+error_exit :
+	sf_errno = error ;
+
+	if (error == SFE_SYSTEM)
+		snprintf (sf_syserr, sizeof (sf_syserr), "%s", psf->syserr) ;
+	snprintf (sf_parselog, sizeof (sf_parselog), "%s", psf->parselog.buf) ;
+
+	switch (error)
+	{	case SF_ERR_SYSTEM :
+		case SF_ERR_UNSUPPORTED_ENCODING :
+		case SFE_UNIMPLEMENTED :
+			break ;
+
+		case SFE_RAW_BAD_FORMAT :
+			break ;
+
+		default :
+			if (psf->file.mode == SFM_READ)
+			{	psf_log_printf (psf, "Parse error : %s\n", sf_error_number (error)) ;
+				error = SF_ERR_MALFORMED_FILE ;
+				} ;
+		} ;
+
+	psf_close (psf) ;
+	return NULL ;
+} /* psf_open_file */
+
+/*==============================================================================
+** Chunk getting and setting.
+** This works for AIFF, CAF, RF64 and WAV.
+** It doesn't work for W64 because W64 uses weird GUID style chunk markers.
+*/
+
+int
+sf_set_chunk (SNDFILE * sndfile, const SF_CHUNK_INFO * chunk_info)
+{	SF_PRIVATE 	*psf ;
+
+	VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ;
+
+	if (chunk_info == NULL || chunk_info->data == NULL)
+		return SFE_BAD_CHUNK_PTR ;
+
+	if (psf->set_chunk)
+		return psf->set_chunk (psf, chunk_info) ;
+
+	return SFE_BAD_CHUNK_FORMAT ;
+} /* sf_set_chunk */
+
+SF_CHUNK_ITERATOR *
+sf_get_chunk_iterator (SNDFILE * sndfile, const SF_CHUNK_INFO * chunk_info)
+{	SF_PRIVATE 	*psf ;
+
+	VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ;
+
+	if (chunk_info)
+		return psf_get_chunk_iterator (psf, chunk_info->id) ;
+
+	return psf_get_chunk_iterator (psf, NULL) ;
+} /* sf_get_chunk_iterator */
+
+SF_CHUNK_ITERATOR *
+sf_next_chunk_iterator (SF_CHUNK_ITERATOR * iterator)
+{	SF_PRIVATE 	*psf ;
+	SNDFILE	*sndfile = iterator ? iterator->sndfile : NULL ;
+
+	VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ;
+
+	if (psf->next_chunk_iterator)
+		return psf->next_chunk_iterator (psf, iterator) ;
+
+	return NULL ;
+} /* sf_get_chunk_iterator_next */
+
+int
+sf_get_chunk_size (const SF_CHUNK_ITERATOR * iterator, SF_CHUNK_INFO * chunk_info)
+{	SF_PRIVATE 	*psf ;
+	SNDFILE	*sndfile = iterator ? iterator->sndfile : NULL ;
+
+	VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ;
+
+	if (chunk_info == NULL)
+		return SFE_BAD_CHUNK_PTR ;
+
+	if (psf->get_chunk_size)
+		return psf->get_chunk_size (psf, iterator, chunk_info) ;
+
+	return SFE_BAD_CHUNK_FORMAT ;
+} /* sf_get_chunk_size */
+
+int
+sf_get_chunk_data (const SF_CHUNK_ITERATOR * iterator, SF_CHUNK_INFO * chunk_info)
+{	SF_PRIVATE	*psf ;
+	SNDFILE	*sndfile = iterator ? iterator->sndfile : NULL ;
+
+	VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ;
+
+	if (chunk_info == NULL || chunk_info->data == NULL)
+		return SFE_BAD_CHUNK_PTR ;
+
+	if (psf->get_chunk_data)
+		return psf->get_chunk_data (psf, iterator, chunk_info) ;
+
+	return SFE_BAD_CHUNK_FORMAT ;
+} /* sf_get_chunk_data */
diff --git a/src/strings.c b/src/strings.c
new file mode 100644
index 0000000..3dfe5de
--- /dev/null
+++ b/src/strings.c
@@ -0,0 +1,190 @@
+/*
+** Copyright (C) 2001-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<string.h>
+#include	<math.h>
+
+#include	"sndfile.h"
+#include	"common.h"
+
+#define STRINGS_DEBUG 0
+
+int
+psf_store_string (SF_PRIVATE *psf, int str_type, const char *str)
+{	char	new_str [128] ;
+	size_t	str_len ;
+	int		k, str_flags ;
+
+	if (str == NULL)
+		return SFE_STR_BAD_STRING ;
+
+	str_len = strlen (str) ;
+
+	/* A few extra checks for write mode. */
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	if ((psf->strings.flags & SF_STR_ALLOW_START) == 0)
+			return SFE_STR_NO_SUPPORT ;
+		if (psf->have_written && (psf->strings.flags & SF_STR_ALLOW_END) == 0)
+			return SFE_STR_NO_SUPPORT ;
+		/* Only allow zero length strings for software. */
+		if (str_type != SF_STR_SOFTWARE && str_len == 0)
+			return SFE_STR_BAD_STRING ;
+		} ;
+
+	/* Find the next free slot in table. */
+	for (k = 0 ; k < SF_MAX_STRINGS ; k++)
+	{	/* If we find a matching entry clear it. */
+		if (psf->strings.data [k].type == str_type)
+			psf->strings.data [k].type = -1 ;
+
+		if (psf->strings.data [k].type == 0)
+			break ;
+		} ;
+
+	/* Determine flags */
+	str_flags = SF_STR_LOCATE_START ;
+	if (psf->file.mode == SFM_RDWR || psf->have_written)
+	{	if ((psf->strings.flags & SF_STR_ALLOW_END) == 0)
+			return SFE_STR_NO_ADD_END ;
+		str_flags = SF_STR_LOCATE_END ;
+		} ;
+
+	/* More sanity checking. */
+	if (k >= SF_MAX_STRINGS)
+		return SFE_STR_MAX_COUNT ;
+
+	if (k == 0 && psf->strings.storage_used != 0)
+	{	psf_log_printf (psf, "SFE_STR_WEIRD : k == 0 && psf->strings.storage_used != 0\n") ;
+		return SFE_STR_WEIRD ;
+		} ;
+
+	if (k != 0 && psf->strings.storage_used == 0)
+	{	psf_log_printf (psf, "SFE_STR_WEIRD : k != 0 && psf->strings.storage_used == 0\n") ;
+		return SFE_STR_WEIRD ;
+		} ;
+
+	/* Special case for the first string. */
+	if (k == 0)
+		psf->strings.storage_used = 0 ;
+
+	switch (str_type)
+	{	case SF_STR_SOFTWARE :
+				/* In write mode, want to append libsndfile-version to string. */
+				if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+				{	if (strstr (str, PACKAGE_NAME) == NULL)
+					{	/*
+						** If the supplied string does not already contain a
+						** libsndfile-X.Y.Z component, then add it.
+						*/
+						if (strlen (str) == 0)
+							snprintf (new_str, sizeof (new_str), "%s-%s", PACKAGE_NAME, PACKAGE_VERSION) ;
+						else
+							snprintf (new_str, sizeof (new_str), "%s (%s-%s)", str, PACKAGE_NAME, PACKAGE_VERSION) ;
+						}
+					else
+						snprintf (new_str, sizeof (new_str), "%s", str) ;
+
+					str = new_str ;
+					} ;
+				break ;
+
+		case SF_STR_TITLE :
+		case SF_STR_COPYRIGHT :
+		case SF_STR_ARTIST :
+		case SF_STR_COMMENT :
+		case SF_STR_DATE :
+		case SF_STR_ALBUM :
+		case SF_STR_LICENSE :
+		case SF_STR_TRACKNUMBER :
+		case SF_STR_GENRE :
+				break ;
+
+		default :
+			psf_log_printf (psf, "%s : SFE_STR_BAD_TYPE\n", __func__) ;
+			return SFE_STR_BAD_TYPE ;
+		} ;
+
+	/* Plus one to catch string terminator. */
+	str_len = strlen (str) + 1 ;
+
+	if (psf->strings.storage_used + str_len + 1 > psf->strings.storage_len)
+	{	char * temp = psf->strings.storage ;
+		size_t newlen = 2 * psf->strings.storage_len + str_len + 1 ;
+
+		newlen = newlen < 256 ? 256 : newlen ;
+
+		char * new_storage = realloc(temp, newlen);
+		if (new_storage == NULL)
+		{
+			return SFE_MALLOC_FAILED ;
+			} else {
+			psf->strings.storage = new_storage;
+			} ;
+
+		psf->strings.storage_len = newlen ;
+		} ;
+
+	psf->strings.data [k].type = str_type ;
+	psf->strings.data [k].offset = psf->strings.storage_used ;
+	psf->strings.data [k].flags = str_flags ;
+
+	memcpy (psf->strings.storage + psf->strings.storage_used, str, str_len) ;
+	psf->strings.storage_used += str_len ;
+
+	psf->strings.flags |= str_flags ;
+
+#if STRINGS_DEBUG
+	printf ("storage_used         : %zd / %zd\n", psf->strings.storage_used, psf->strings.storage_len) ;
+	psf_hexdump (psf->strings.storage, psf->strings.storage_used) ;
+#endif
+
+	return 0 ;
+} /* psf_store_string */
+
+int
+psf_set_string (SF_PRIVATE *psf, int str_type, const char *str)
+{	if (psf->file.mode == SFM_READ)
+		return SFE_STR_NOT_WRITE ;
+
+	return psf_store_string (psf, str_type, str) ;
+} /* psf_set_string */
+
+const char*
+psf_get_string (SF_PRIVATE *psf, int str_type)
+{	int k ;
+
+	for (k = 0 ; k < SF_MAX_STRINGS ; k++)
+		if (str_type == psf->strings.data [k].type)
+			return psf->strings.storage + psf->strings.data [k].offset ;
+
+	return NULL ;
+} /* psf_get_string */
+
+int
+psf_location_string_count (const SF_PRIVATE * psf, int location)
+{	int k, count = 0 ;
+
+	for (k = 0 ; k < SF_MAX_STRINGS ; k++)
+		if (psf->strings.data [k].type > 0 && psf->strings.data [k].flags & location)
+			count ++ ;
+
+	return count ;
+} /* psf_location_string_count */
diff --git a/src/svx.c b/src/svx.c
new file mode 100644
index 0000000..342b006
--- /dev/null
+++ b/src/svx.c
@@ -0,0 +1,402 @@
+/*
+** Copyright (C) 1999-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdarg.h>
+
+#include "sndfile.h"
+#include "sfendian.h"
+#include "common.h"
+
+
+/*------------------------------------------------------------------------------
+ * Macros to handle big/little endian issues.
+*/
+
+#define FORM_MARKER	(MAKE_MARKER ('F', 'O', 'R', 'M'))
+#define SVX8_MARKER	(MAKE_MARKER ('8', 'S', 'V', 'X'))
+#define SV16_MARKER	(MAKE_MARKER ('1', '6', 'S', 'V'))
+#define VHDR_MARKER	(MAKE_MARKER ('V', 'H', 'D', 'R'))
+#define BODY_MARKER	(MAKE_MARKER ('B', 'O', 'D', 'Y'))
+
+#define ATAK_MARKER	(MAKE_MARKER ('A', 'T', 'A', 'K'))
+#define RLSE_MARKER	(MAKE_MARKER ('R', 'L', 'S', 'E'))
+
+#define c_MARKER	(MAKE_MARKER ('(', 'c', ')', ' '))
+#define NAME_MARKER	(MAKE_MARKER ('N', 'A', 'M', 'E'))
+#define AUTH_MARKER	(MAKE_MARKER ('A', 'U', 'T', 'H'))
+#define ANNO_MARKER	(MAKE_MARKER ('A', 'N', 'N', 'O'))
+#define CHAN_MARKER	(MAKE_MARKER ('C', 'H', 'A', 'N'))
+
+/*------------------------------------------------------------------------------
+ * Typedefs for file chunks.
+*/
+
+typedef struct
+{	unsigned int	oneShotHiSamples, repeatHiSamples, samplesPerHiCycle ;
+	unsigned short	samplesPerSec ;
+	unsigned char	octave, compression ;
+	unsigned int	volume ;
+} VHDR_CHUNK ;
+
+enum {
+	HAVE_FORM	= 0x01,
+
+	HAVE_SVX	= 0x02,
+	HAVE_VHDR	= 0x04,
+	HAVE_BODY	= 0x08
+} ;
+
+/*------------------------------------------------------------------------------
+ * Private static functions.
+*/
+
+static int	svx_close	(SF_PRIVATE *psf) ;
+static int	svx_write_header (SF_PRIVATE *psf, int calc_length) ;
+static int 	svx_read_header	(SF_PRIVATE *psf) ;
+
+/*------------------------------------------------------------------------------
+** Public function.
+*/
+
+int
+svx_open	(SF_PRIVATE *psf)
+{	int error ;
+
+	if (psf->file.mode == SFM_READ || (psf->file.mode == SFM_RDWR && psf->filelength > 0))
+	{	if ((error = svx_read_header (psf)))
+			return error ;
+
+		psf->endian = SF_ENDIAN_BIG ;			/* All SVX files are big endian. */
+
+		psf->blockwidth = psf->sf.channels * psf->bytewidth ;
+		if (psf->blockwidth)
+			psf->sf.frames = psf->datalength / psf->blockwidth ;
+
+		psf_fseek (psf, psf->dataoffset, SEEK_SET) ;
+		} ;
+
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	if (psf->is_pipe)
+			return SFE_NO_PIPE_WRITE ;
+
+		if ((SF_CONTAINER (psf->sf.format)) != SF_FORMAT_SVX)
+			return	SFE_BAD_OPEN_FORMAT ;
+
+		psf->endian = SF_ENDIAN (psf->sf.format) ;
+
+		if (psf->endian == SF_ENDIAN_LITTLE || (CPU_IS_LITTLE_ENDIAN && psf->endian == SF_ENDIAN_CPU))
+			return SFE_BAD_ENDIAN ;
+
+		psf->endian = SF_ENDIAN_BIG ;			/* All SVX files are big endian. */
+
+		error = svx_write_header (psf, SF_FALSE) ;
+		if (error)
+			return error ;
+
+		psf->write_header = svx_write_header ;
+		} ;
+
+	psf->container_close = svx_close ;
+
+	if ((error = pcm_init (psf)))
+		return error ;
+
+	return 0 ;
+} /* svx_open */
+
+/*------------------------------------------------------------------------------
+*/
+
+static int
+svx_read_header	(SF_PRIVATE *psf)
+{	VHDR_CHUNK		vhdr ;
+	uint32_t		chunk_size, marker ;
+	int				filetype = 0, parsestage = 0, done = 0 ;
+	int 			bytecount = 0, channels ;
+
+	if (psf->filelength > 0xFFFFFFFFLL)
+		psf_log_printf (psf, "Warning : filelength > 0xffffffff. This is bad!!!!\n") ;
+
+	memset (&vhdr, 0, sizeof (vhdr)) ;
+	psf_binheader_readf (psf, "p", 0) ;
+
+	/* Set default number of channels. Modify later if necessary */
+	psf->sf.channels = 1 ;
+
+	psf->sf.format = SF_FORMAT_SVX ;
+
+	while (! done)
+	{	psf_binheader_readf (psf, "Em4", &marker, &chunk_size) ;
+
+		switch (marker)
+		{	case FORM_MARKER :
+					if (parsestage)
+						return SFE_SVX_NO_FORM ;
+
+					if (chunk_size != psf->filelength - 2 * sizeof (chunk_size))
+						psf_log_printf (psf, "FORM : %u (should be %u)\n", chunk_size, (uint32_t) psf->filelength - 2 * sizeof (chunk_size)) ;
+					else
+						psf_log_printf (psf, "FORM : %u\n", chunk_size) ;
+					parsestage |= HAVE_FORM ;
+
+					psf_binheader_readf (psf, "m", &marker) ;
+
+					filetype = marker ;
+					psf_log_printf (psf, " %M\n", marker) ;
+					parsestage |= HAVE_SVX ;
+					break ;
+
+			case VHDR_MARKER :
+					if (! (parsestage & (HAVE_FORM | HAVE_SVX)))
+						return SFE_SVX_NO_FORM ;
+
+					psf_log_printf (psf, " VHDR : %d\n", chunk_size) ;
+
+					psf_binheader_readf (psf, "E4442114", &(vhdr.oneShotHiSamples), &(vhdr.repeatHiSamples),
+						&(vhdr.samplesPerHiCycle), &(vhdr.samplesPerSec), &(vhdr.octave), &(vhdr.compression),
+						&(vhdr.volume)) ;
+
+					psf_log_printf (psf, "  OneShotHiSamples  : %d\n", vhdr.oneShotHiSamples) ;
+					psf_log_printf (psf, "  RepeatHiSamples   : %d\n", vhdr.repeatHiSamples) ;
+					psf_log_printf (psf, "  samplesPerHiCycle : %d\n", vhdr.samplesPerHiCycle) ;
+					psf_log_printf (psf, "  Sample Rate       : %d\n", vhdr.samplesPerSec) ;
+					psf_log_printf (psf, "  Octave            : %d\n", vhdr.octave) ;
+
+					psf_log_printf (psf, "  Compression       : %d => ", vhdr.compression) ;
+
+					switch (vhdr.compression)
+					{	case 0 : psf_log_printf (psf, "None.\n") ;
+								break ;
+						case 1 : psf_log_printf (psf, "Fibonacci delta\n") ;
+								break ;
+						case 2 : psf_log_printf (psf, "Exponential delta\n") ;
+								break ;
+						} ;
+
+					psf_log_printf (psf, "  Volume            : %d\n", vhdr.volume) ;
+
+					psf->sf.samplerate 	= vhdr.samplesPerSec ;
+
+					if (filetype == SVX8_MARKER)
+					{	psf->sf.format |= SF_FORMAT_PCM_S8 ;
+						psf->bytewidth = 1 ;
+						}
+					else if (filetype == SV16_MARKER)
+					{	psf->sf.format |= SF_FORMAT_PCM_16 ;
+						psf->bytewidth = 2 ;
+						} ;
+
+					parsestage |= HAVE_VHDR ;
+					break ;
+
+			case BODY_MARKER :
+					if (! (parsestage & HAVE_VHDR))
+						return SFE_SVX_NO_BODY ;
+
+					psf->datalength = chunk_size ;
+
+					psf->dataoffset = psf_ftell (psf) ;
+					if (psf->dataoffset < 0)
+						return SFE_SVX_NO_BODY ;
+
+					if (psf->datalength > psf->filelength - psf->dataoffset)
+					{	psf_log_printf (psf, " BODY : %D (should be %D)\n", psf->datalength, psf->filelength - psf->dataoffset) ;
+						psf->datalength = psf->filelength - psf->dataoffset ;
+						}
+					else
+						psf_log_printf (psf, " BODY : %D\n", psf->datalength) ;
+
+					parsestage |= HAVE_BODY ;
+
+					if (! psf->sf.seekable)
+						break ;
+
+					psf_fseek (psf, psf->datalength, SEEK_CUR) ;
+					break ;
+
+			case NAME_MARKER :
+					if (! (parsestage & HAVE_SVX))
+						return SFE_SVX_NO_FORM ;
+
+					psf_log_printf (psf, " %M : %u\n", marker, chunk_size) ;
+
+					if (strlen (psf->file.name) != chunk_size)
+					{	if (chunk_size > sizeof (psf->file.name) - 1)
+							return SFE_SVX_BAD_NAME_LENGTH ;
+
+						psf_binheader_readf (psf, "b", psf->file.name, chunk_size) ;
+						psf->file.name [chunk_size] = 0 ;
+						}
+					else
+						psf_binheader_readf (psf, "j", chunk_size) ;
+					break ;
+
+			case ANNO_MARKER :
+					if (! (parsestage & HAVE_SVX))
+						return SFE_SVX_NO_FORM ;
+
+					psf_log_printf (psf, " %M : %u\n", marker, chunk_size) ;
+
+					psf_binheader_readf (psf, "j", chunk_size) ;
+					break ;
+
+			case CHAN_MARKER :
+					if (! (parsestage & HAVE_SVX))
+						return SFE_SVX_NO_FORM ;
+
+					psf_log_printf (psf, " %M : %u\n", marker, chunk_size) ;
+
+					bytecount += psf_binheader_readf (psf, "E4", &channels) ;
+
+					if (channels == 2 || channels == 4)
+						psf_log_printf (psf, "  Channels : %d => mono\n", channels) ;
+					else if (channels == 6)
+					{	psf->sf.channels = 2 ;
+						psf_log_printf (psf, "  Channels : %d => stereo\n", channels) ;
+						}
+					else
+						psf_log_printf (psf, "  Channels : %d *** assuming mono\n", channels) ;
+
+					psf_binheader_readf (psf, "j", chunk_size - bytecount) ;
+					break ;
+
+
+			case AUTH_MARKER :
+			case c_MARKER :
+					if (! (parsestage & HAVE_SVX))
+						return SFE_SVX_NO_FORM ;
+
+					psf_log_printf (psf, " %M : %u\n", marker, chunk_size) ;
+
+					psf_binheader_readf (psf, "j", chunk_size) ;
+					break ;
+
+			default :
+					if (chunk_size >= 0xffff0000)
+					{	done = SF_TRUE ;
+						psf_log_printf (psf, "*** Unknown chunk marker (%X) at position %D with length %u. Exiting parser.\n", marker, psf_ftell (psf) - 8, chunk_size) ;
+						break ;
+						} ;
+
+					if (psf_isprint ((marker >> 24) & 0xFF) && psf_isprint ((marker >> 16) & 0xFF)
+						&& psf_isprint ((marker >> 8) & 0xFF) && psf_isprint (marker & 0xFF))
+					{	psf_log_printf (psf, "%M : %u (unknown marker)\n", marker, chunk_size) ;
+						psf_binheader_readf (psf, "j", chunk_size) ;
+						break ;
+						} ;
+					if ((chunk_size = psf_ftell (psf)) & 0x03)
+					{	psf_log_printf (psf, "  Unknown chunk marker at position %d. Resynching.\n", chunk_size - 4) ;
+
+						chunk_size = chunk_size & 3 ;
+						psf_binheader_readf (psf, "j", 4 - chunk_size) ;
+						break ;
+						} ;
+					psf_log_printf (psf, "*** Unknown chunk marker (%X) at position %D. Exiting parser.\n", marker, psf_ftell (psf) - 8) ;
+					done = SF_TRUE ;
+			} ;	/* switch (marker) */
+
+		if (! psf->sf.seekable && (parsestage & HAVE_BODY))
+			break ;
+
+		if (psf_ftell (psf) >= psf->filelength - SIGNED_SIZEOF (chunk_size))
+			break ;
+		} ; /* while (1) */
+
+	if (vhdr.compression)
+		return SFE_SVX_BAD_COMP ;
+
+	if (psf->dataoffset <= 0)
+		return SFE_SVX_NO_DATA ;
+
+	return 0 ;
+} /* svx_read_header */
+
+static int
+svx_close (SF_PRIVATE *psf)
+{
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+		svx_write_header (psf, SF_TRUE) ;
+
+	return 0 ;
+} /* svx_close */
+
+static int
+svx_write_header (SF_PRIVATE *psf, int calc_length)
+{	static	char 	annotation	[] = "libsndfile by Erik de Castro Lopo\0\0\0" ;
+	sf_count_t	current ;
+
+	current = psf_ftell (psf) ;
+
+	if (calc_length)
+	{	psf->filelength = psf_get_filelen (psf) ;
+
+		psf->datalength = psf->filelength - psf->dataoffset ;
+
+		if (psf->dataend)
+			psf->datalength -= psf->filelength - psf->dataend ;
+
+		psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ;
+		} ;
+
+	psf->header.ptr [0] = 0 ;
+	psf->header.indx = 0 ;
+	psf_fseek (psf, 0, SEEK_SET) ;
+
+	/* FORM marker and FORM size. */
+	psf_binheader_writef (psf, "Etm8", BHWm (FORM_MARKER), BHW8 ((psf->filelength < 8) ?
+			psf->filelength * 0 : psf->filelength - 8)) ;
+
+	psf_binheader_writef (psf, "m", BHWm ((psf->bytewidth == 1) ? SVX8_MARKER : SV16_MARKER)) ;
+
+	/* VHDR chunk. */
+	psf_binheader_writef (psf, "Em4", BHWm (VHDR_MARKER), BHW4 (sizeof (VHDR_CHUNK))) ;
+	/* VHDR : oneShotHiSamples, repeatHiSamples, samplesPerHiCycle */
+	psf_binheader_writef (psf, "E444", BHW4 (psf->sf.frames), BHW4 (0), BHW4 (0)) ;
+	/* VHDR : samplesPerSec, octave, compression */
+	psf_binheader_writef (psf, "E211", BHW2 (psf->sf.samplerate), BHW1 (1), BHW1 (0)) ;
+	/* VHDR : volume */
+	psf_binheader_writef (psf, "E4", BHW4 ((psf->bytewidth == 1) ? 0xFF : 0xFFFF)) ;
+
+	if (psf->sf.channels == 2)
+		psf_binheader_writef (psf, "Em44", BHWm (CHAN_MARKER), BHW4 (4), BHW4 (6)) ;
+
+	/* Filename and annotation strings. */
+	psf_binheader_writef (psf, "Emsms", BHWm (NAME_MARKER), BHWs (psf->file.name), BHWm (ANNO_MARKER), BHWs (annotation)) ;
+
+	/* BODY marker and size. */
+	psf_binheader_writef (psf, "Etm8", BHWm (BODY_MARKER), BHW8 ((psf->datalength < 0) ?
+			psf->datalength * 0 : psf->datalength)) ;
+
+	psf_fwrite (psf->header.ptr, psf->header.indx, 1, psf) ;
+
+	if (psf->error)
+		return psf->error ;
+
+	psf->dataoffset = psf->header.indx ;
+
+	if (current > 0)
+		psf_fseek (psf, current, SEEK_SET) ;
+
+	return psf->error ;
+} /* svx_write_header */
+
diff --git a/src/test_audio_detect.c b/src/test_audio_detect.c
new file mode 100644
index 0000000..20926df
--- /dev/null
+++ b/src/test_audio_detect.c
@@ -0,0 +1,113 @@
+/*
+** Copyright (C) 2007-2011 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#else
+#include "sf_unistd.h"
+#endif
+
+#include <string.h>
+#include <errno.h>
+
+#include "common.h"
+#include "sfendian.h"
+
+#include "test_main.h"
+
+static unsigned char float_le_mono [] =
+{	0x36, 0x86, 0x21, 0x44, 0xB5, 0xB4, 0x49, 0x44, 0xA2, 0xC0, 0x71, 0x44, 0x7B, 0xD1, 0x8C, 0x44,
+	0x54, 0xAA, 0xA0, 0x44, 0x60, 0x67, 0xB4, 0x44, 0x22, 0x05, 0xC8, 0x44, 0x29, 0x80, 0xDB, 0x44,
+	0x04, 0xD5, 0xEE, 0x44, 0x27, 0x00, 0x01, 0x45, 0x50, 0x7F, 0x0A, 0x45, 0x53, 0xE6, 0x13, 0x45,
+	0x85, 0x33, 0x1D, 0x45, 0x43, 0x65, 0x26, 0x45, 0xEC, 0x79, 0x2F, 0x45, 0xE3, 0x6F, 0x38, 0x45,
+	0x98, 0x45, 0x41, 0x45, 0x77, 0xF9, 0x49, 0x45, 0xF6, 0x89, 0x52, 0x45, 0x8F, 0xF5, 0x5A, 0x45,
+	0xC9, 0x3A, 0x63, 0x45, 0x28, 0x58, 0x6B, 0x45, 0x3C, 0x4C, 0x73, 0x45, 0x9F, 0x15, 0x7B, 0x45,
+	0x75, 0x59, 0x81, 0x45, 0x64, 0x11, 0x85, 0x45, 0xF1, 0xB1, 0x88, 0x45, 0x78, 0x3A, 0x8C, 0x45,
+	0x58, 0xAA, 0x8F, 0x45, 0xF2, 0x00, 0x93, 0x45, 0xB2, 0x3D, 0x96, 0x45, 0x01, 0x60, 0x99, 0x45,
+	0x50, 0x67, 0x9C, 0x45, 0x15, 0x53, 0x9F, 0x45, 0xCC, 0x22, 0xA2, 0x45, 0xF0, 0xD5, 0xA4, 0x45,
+	0x07, 0x6C, 0xA7, 0x45, 0x9C, 0xE4, 0xA9, 0x45, 0x3D, 0x3F, 0xAC, 0x45, 0x7A, 0x7B, 0xAE, 0x45,
+	0xF2, 0x98, 0xB0, 0x45, 0x3C, 0x97, 0xB2, 0x45, 0x02, 0x76, 0xB4, 0x45, 0xEC, 0x34, 0xB6, 0x45,
+	0xA8, 0xD3, 0xB7, 0x45, 0xEB, 0x51, 0xB9, 0x45, 0x6F, 0xAF, 0xBA, 0x45, 0xF5, 0xEB, 0xBB, 0x45,
+	0x41, 0x07, 0xBD, 0x45, 0x21, 0x01, 0xBE, 0x45, 0x64, 0xD9, 0xBE, 0x45, 0xE3, 0x8F, 0xBF, 0x45,
+	0x7E, 0x24, 0xC0, 0x45, 0x15, 0x97, 0xC0, 0x45, 0x92, 0xE7, 0xC0, 0x45, 0xE8, 0x15, 0xC1, 0x45,
+	0x7E, 0x24, 0xC0, 0x45, 0x15, 0x97, 0xC0, 0x45, 0x92, 0xE7, 0xC0, 0x45, 0xE8, 0x15, 0xC1, 0x45,
+	0x7E, 0x24, 0xC0, 0x45, 0x15, 0x97, 0xC0, 0x45, 0x92, 0xE7, 0xC0, 0x45, 0xE8, 0x15, 0xC1, 0x45,
+} ;
+
+static unsigned char int24_32_le_stereo [] =
+{
+	0x00, 0xE7, 0xFB, 0xFF, 0x00, 0x7C, 0xFD, 0xFF, 0x00, 0xA2, 0xFC, 0xFF, 0x00, 0x2B, 0xFC, 0xFF,
+	0x00, 0xF3, 0xFD, 0xFF, 0x00, 0x19, 0xFB, 0xFF, 0x00, 0xA5, 0xFE, 0xFF, 0x00, 0x8D, 0xFA, 0xFF,
+	0x00, 0x91, 0xFF, 0xFF, 0x00, 0xB5, 0xFA, 0xFF, 0x00, 0x91, 0x00, 0x00, 0x00, 0x5E, 0xFB, 0xFF,
+	0x00, 0xD9, 0x01, 0x00, 0x00, 0x82, 0xFB, 0xFF, 0x00, 0xDF, 0x03, 0x00, 0x00, 0x44, 0xFC, 0xFF,
+	0x00, 0x1C, 0x05, 0x00, 0x00, 0x77, 0xFC, 0xFF, 0x00, 0x8D, 0x06, 0x00, 0x00, 0x4F, 0xFC, 0xFF,
+	0x00, 0x84, 0x07, 0x00, 0x00, 0x74, 0xFC, 0xFF, 0x00, 0x98, 0x08, 0x00, 0x00, 0x33, 0xFD, 0xFF,
+	0x00, 0xB9, 0x09, 0x00, 0x00, 0x48, 0xFF, 0xFF, 0x00, 0xD1, 0x0A, 0x00, 0x00, 0x10, 0x02, 0x00,
+	0x00, 0x28, 0x0C, 0x00, 0x00, 0xA2, 0x05, 0x00, 0x00, 0xA7, 0x0C, 0x00, 0x00, 0x45, 0x08, 0x00,
+	0x00, 0x44, 0x0D, 0x00, 0x00, 0x1A, 0x0A, 0x00, 0x00, 0x65, 0x0D, 0x00, 0x00, 0x51, 0x0B, 0x00,
+	0x00, 0x8B, 0x0D, 0x00, 0x00, 0x18, 0x0B, 0x00, 0x00, 0x37, 0x0E, 0x00, 0x00, 0x24, 0x0B, 0x00,
+	0x00, 0x00, 0x0F, 0x00, 0x00, 0xDD, 0x0A, 0x00, 0x00, 0x83, 0x10, 0x00, 0x00, 0x31, 0x0A, 0x00,
+	0x00, 0x07, 0x12, 0x00, 0x00, 0xC0, 0x08, 0x00, 0x00, 0xF7, 0x12, 0x00, 0x00, 0x47, 0x06, 0x00,
+	0x00, 0xDD, 0x12, 0x00, 0x00, 0x6A, 0x03, 0x00, 0x00, 0xD5, 0x11, 0x00, 0x00, 0x99, 0x00, 0x00,
+	0x00, 0x01, 0x10, 0x00, 0x00, 0xC5, 0xFE, 0xFF, 0x00, 0xF4, 0x0D, 0x00, 0x00, 0x97, 0xFD, 0xFF,
+	0x00, 0x62, 0x0B, 0x00, 0x00, 0x75, 0xFC, 0xFF, 0x00, 0xE9, 0x08, 0x00, 0x00, 0xC0, 0xFB, 0xFF,
+	0x00, 0x80, 0x06, 0x00, 0x00, 0x3C, 0xFB, 0xFF, 0x00, 0xDA, 0x03, 0x00, 0x00, 0xE4, 0xFA, 0xFF,
+	0x00, 0xEB, 0x01, 0x00, 0x00, 0x21, 0xFB, 0xFF, 0x00, 0x20, 0x00, 0x00, 0x00, 0xE7, 0xFB, 0xFF,
+} ;
+
+
+void
+test_audio_detect (void)
+{
+	SF_PRIVATE psf ;
+	AUDIO_DETECT ad ;
+	int errors = 0 ;
+
+	print_test_name ("Testing audio detect") ;
+
+	memset (&psf, 0, sizeof (psf)) ;
+
+	ad.endianness = SF_ENDIAN_LITTLE ;
+	ad.channels = 1 ;
+	if (audio_detect (&psf, &ad, float_le_mono, sizeof (float_le_mono)) != SF_FORMAT_FLOAT)
+	{	puts ("    float_le_mono") ;
+		errors ++ ;
+		} ;
+
+	ad.endianness = SF_ENDIAN_LITTLE ;
+	ad.channels = 2 ;
+	if (audio_detect (&psf, &ad, int24_32_le_stereo, sizeof (int24_32_le_stereo)) != SF_FORMAT_PCM_32)
+	{	if (errors == 0) puts ("\nFailed tests :\n") ;
+		puts ("    int24_32_le_stereo") ;
+		errors ++ ;
+		} ;
+
+	if (errors != 0)
+	{	printf ("\n    Errors : %d\n\n", errors) ;
+		exit (1) ;
+		} ;
+
+	puts ("ok") ;
+
+	return ;
+} /* test_audio_detect */
diff --git a/src/test_binheader_writef.c b/src/test_binheader_writef.c
new file mode 100644
index 0000000..8b1f9c1
--- /dev/null
+++ b/src/test_binheader_writef.c
@@ -0,0 +1,61 @@
+/*
+** Copyright (C) 2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include "common.h"
+
+#include "test_main.h"
+
+void
+test_binheader_writef (void)
+{	char buffer [18] ;
+	SF_PRIVATE	sf_private, *psf ;
+	int			k, errors = 0 ;
+
+	print_test_name ("Testing binheader_writef") ;
+
+	memset (&sf_private, 0, sizeof (sf_private)) ;
+
+	psf = &sf_private ;
+	for (k = 0 ; errors == 0 && k < 10 ; k++)
+	{	psf_strlcpy (buffer, sizeof (buffer), "abcdefghijklmnop") ;
+		buffer [k] = 0 ;
+
+		psf_binheader_writef (psf, "Ep", BHWp (buffer)) ;
+
+		if ((psf->header.indx & 1) != 0)
+			errors = 1 ;
+		} ;
+
+	free (psf->header.ptr) ;
+
+	if (errors)
+	{	puts ("\nExiting due to errors.\n") ;
+		exit (1) ;
+		} ;
+
+	puts ("ok") ;
+} /* test_log_printf */
+
diff --git a/src/test_broadcast_var.c b/src/test_broadcast_var.c
new file mode 100644
index 0000000..5ae81fb
--- /dev/null
+++ b/src/test_broadcast_var.c
@@ -0,0 +1,123 @@
+/*
+** Copyright (C) 2010-2012 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+
+#include "test_main.h"
+
+#define	BCAST_MAX	512
+
+typedef SF_BROADCAST_INFO_VAR (BCAST_MAX) SF_BROADCAST_INFO_512 ;
+
+static void
+fill_coding_history (SF_BROADCAST_INFO_512 * bi)
+{	static const char *lines [] =
+	{	"Lorem ipsum dolor sit amet,\nconsectetur adipiscing elit.",
+		"Donec dignissim erat\nvehicula libero condimentum\ndictum porta augue faucibus.",
+		"Maecenas nec turpis\nsit amet quam\nfaucibus adipiscing.",
+		"Mauris aliquam,\nlectus interdum\ntincidunt luctus.",
+		"\n\n\n\n\n\n\n\n\n\n\n\n",
+		"In auctor lorem\nvel est euismod\ncondimentum.",
+		"\n\n\n\n\n\n\n\n\n\n\n\n",
+		"Ut vitae magna\nid dui placerat vehicula\nin id lectus.",
+		"\n\n\n\n\n\n\n\n\n\n\n\n",
+		"Sed lacus leo,\nmolestie et luctus ac,\ntincidunt sit amet nisi.",
+		"\n\n\n\n\n\n\n\n\n\n\n\n",
+		"Sed ligula neque,\ngravida semper vulputate laoreet,\ngravida eu tellus.",
+		"Donec dolor dolor,\nscelerisque in consequat ornare,\ntempor nec nisl."
+	} ;
+	int k ;
+
+	bi->coding_history [0] = 0 ;
+
+	for (k = 0 ; strlen (bi->coding_history) < bi->coding_history_size - 1 ; k ++)
+		append_snprintf (bi->coding_history, bi->coding_history_size, "%s\n", lines [k % ARRAY_LEN (lines)]) ;
+
+	return ;
+} /* fill_coding_listory */
+
+static void
+test_broadcast_var_set (void)
+{	SF_PRIVATE	sf_private, *psf ;
+	int k ;
+
+	psf = &sf_private ;
+	memset (psf, 0, sizeof (sf_private)) ;
+
+	print_test_name ("Testing broadcast_var_set ") ;
+
+	for (k = 64 ; k < BCAST_MAX ; k++)
+	{
+		SF_BROADCAST_INFO_512 bi ;
+
+		memset (&bi, 0, sizeof (bi)) ;
+
+		bi.coding_history_size = k ;
+		fill_coding_history (&bi) ;
+		bi.coding_history_size -- ;
+
+		broadcast_var_set (psf, (SF_BROADCAST_INFO*) &bi, sizeof (bi)) ;
+		} ;
+
+	if (psf->broadcast_16k != NULL)
+		free (psf->broadcast_16k) ;
+
+	puts ("ok") ;
+} /* test_broadcast_var_set */
+
+static void
+test_broadcast_var_zero (void)
+{	SF_PRIVATE	sf_private, *psf ;
+#ifdef _MSC_VER
+	SF_BROADCAST_INFO_VAR (0) bi ;
+#else
+	SF_BROADCAST_INFO_VAR () bi ;
+#endif
+
+	psf = &sf_private ;
+	memset (psf, 0, sizeof (sf_private)) ;
+	psf->file.mode = SFM_RDWR ;
+
+	print_test_name ("Testing broadcast_var_zero ") ;
+
+	memset (&bi, 0, sizeof (bi)) ;
+
+	broadcast_var_set (psf, (SF_BROADCAST_INFO*) &bi, sizeof (bi)) ;
+
+	if (psf->broadcast_16k->coding_history_size != 0)
+	{	printf ("\n\nLine %d: coding_history_size %d should be zero.\n\n", __LINE__, psf->broadcast_16k->coding_history_size) ;
+		exit (1) ;
+		} ;
+
+	if (psf->broadcast_16k != NULL)
+		free (psf->broadcast_16k) ;
+
+	puts ("ok") ;
+} /* test_broadcast_var_zero */
+
+void
+test_broadcast_var (void)
+{	test_broadcast_var_set () ;
+	test_broadcast_var_zero () ;
+} /* test_broadcast_var */
diff --git a/src/test_cart_var.c b/src/test_cart_var.c
new file mode 100644
index 0000000..532a755
--- /dev/null
+++ b/src/test_cart_var.c
@@ -0,0 +1,91 @@
+/*
+** Copyright (C) 2010-2013 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+
+#include "test_main.h"
+
+#define	CART_MAX	512
+
+typedef SF_CART_INFO_VAR (CART_MAX) SF_CART_INFO_512 ;
+
+static void
+fill_tag_text (SF_CART_INFO_512 * ci)
+{	static const char *lines [] =
+	{	"Lorem ipsum dolor sit amet,\nconsectetur adipiscing elit.",
+		"Donec dignissim erat\nvehicula libero condimentum\ndictum porta augue faucibus.",
+		"Maecenas nec turpis\nsit amet quam\nfaucibus adipiscing.",
+		"Mauris aliquam,\nlectus interdum\ntincidunt luctus.",
+		"\n\n\n\n\n\n\n\n\n\n\n\n",
+		"In auctor lorem\nvel est euismod\ncondimentum.",
+		"\n\n\n\n\n\n\n\n\n\n\n\n",
+		"Ut vitae magna\nid dui placerat vehicula\nin id lectus.",
+		"\n\n\n\n\n\n\n\n\n\n\n\n",
+		"Sed lacus leo,\nmolestie et luctus ac,\ntincidunt sit amet nisi.",
+		"\n\n\n\n\n\n\n\n\n\n\n\n",
+		"Sed ligula neque,\ngravida semper vulputate laoreet,\ngravida eu tellus.",
+		"Donec dolor dolor,\nscelerisque in consequat ornare,\ntempor nec nisl."
+	} ;
+	int k ;
+
+	ci->tag_text [0] = 0 ;
+
+	for (k = 0 ; strlen (ci->tag_text) < ci->tag_text_size - 1 ; k ++)
+		append_snprintf (ci->tag_text, ci->tag_text_size, "%s\n", lines [k % ARRAY_LEN (lines)]) ;
+
+	return ;
+} /* fill_tag_text */
+
+void
+test_cart_var (void)
+{	SF_PRIVATE	sf_private, *psf ;
+	SF_CART_TIMER timer ;
+	int k ;
+
+	psf = &sf_private ;
+	memset (psf, 0, sizeof (sf_private)) ;
+
+	print_test_name ("Testing cart_var_set ") ;
+
+	for (k = 64 ; k < CART_MAX ; k++)
+	{
+		SF_CART_INFO_512 ci ;
+
+		memset (&ci, 0, sizeof (ci)) ;
+
+		memset (&timer, 0, sizeof (timer)) ;
+		memcpy (ci.post_timers, &timer, sizeof (timer)) ;
+
+		ci.tag_text_size = k ;
+		fill_tag_text (&ci) ;
+		ci.tag_text_size -- ;
+
+		cart_var_set (psf, (SF_CART_INFO*) &ci, sizeof (ci)) ;
+		} ;
+
+	if (psf->cart_16k != NULL)
+		free (psf->cart_16k) ;
+
+	puts ("ok") ;
+} /* test_cart_var */
diff --git a/src/test_conversions.c b/src/test_conversions.c
new file mode 100644
index 0000000..f717fa5
--- /dev/null
+++ b/src/test_conversions.c
@@ -0,0 +1,112 @@
+/*
+** Copyright (C) 2006-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include "common.h"
+#include "test_main.h"
+
+
+/*
+** This is a bit rough, but it is the nicest way to do it.
+*/
+
+#define cmp_test(line, ival, tval, str) \
+	if (ival != tval) \
+	{	printf (str, line, ival, tval) ; \
+		exit (1) ; \
+		} ;
+
+static void
+conversion_test (char endian)
+{
+	SF_PRIVATE	sf_private, *psf ;
+	const char * filename = "conversion.bin" ;
+	int64_t i64 = 0x0123456789ABCDEFLL, t64 = 0 ;
+	char format_str [16] ;
+	char test_name [64] ;
+	char i8 = 12, t8 = 0 ;
+	short i16 = 0x123, t16 = 0 ;
+	int i24 = 0x23456, t24 = 0 ;
+	int i32 = 0x0a0b0c0d, t32 = 0 ;
+	int bytes ;
+
+	snprintf (format_str, sizeof (format_str), "%c12348", endian) ;
+
+	snprintf (test_name, sizeof (test_name), "Testing %s conversions", endian == 'e' ? "little endian" : "big endian") ;
+	print_test_name (test_name) ;
+
+	psf = &sf_private ;
+	memset (psf, 0, sizeof (sf_private)) ;
+
+	psf->file.mode = SFM_WRITE ;
+	snprintf (psf->file.path, sizeof (psf->file.path), "%s", filename) ;
+
+	if (psf_fopen (psf) != 0)
+	{	printf ("\n\nError : failed to open file '%s' for write.\n\n", filename) ;
+		exit (1) ;
+		} ;
+
+	psf_binheader_writef (psf, format_str, i8, i16, i24, i32, i64) ;
+	psf_fwrite (psf->header.ptr, 1, psf->header.indx, psf) ;
+	free (psf->header.ptr) ;
+	psf_fclose (psf) ;
+
+	memset (psf, 0, sizeof (sf_private)) ;
+
+	psf->file.mode = SFM_READ ;
+	snprintf (psf->file.path, sizeof (psf->file.path), "%s", filename) ;
+
+	if (psf_fopen (psf) != 0)
+	{	printf ("\n\nError : failed to open file '%s' for read.\n\n", filename) ;
+		exit (1) ;
+		} ;
+
+	bytes = psf_binheader_readf (psf, format_str, &t8, &t16, &t24, &t32, &t64) ;
+	free (psf->header.ptr) ;
+	psf_fclose (psf) ;
+
+	if (bytes != 18)
+	{	printf ("\n\nLine %d : read %d bytes.\n\n", __LINE__, bytes) ;
+		exit (1) ;
+		} ;
+
+	cmp_test (__LINE__, i8, t8, "\n\nLine %d : 8 bit int failed %d -> %d.\n\n") ;
+	cmp_test (__LINE__, i16, t16, "\n\nLine %d : 16 bit int failed 0x%x -> 0x%x.\n\n") ;
+	cmp_test (__LINE__, i24, t24, "\n\nLine %d : 24 bit int failed 0x%x -> 0x%x.\n\n") ;
+	cmp_test (__LINE__, i32, t32, "\n\nLine %d : 32 bit int failed 0x%x -> 0x%x.\n\n") ;
+	cmp_test (__LINE__, i64, t64, "\n\nLine %d : 64 bit int failed 0x%" PRIx64 "x -> 0x%" PRIx64 "x.\n\n") ;
+
+	remove (filename) ;
+	puts ("ok") ;
+} /* conversion_test */
+
+void
+test_conversions (void)
+{
+	conversion_test ('E') ;
+	conversion_test ('e') ;
+} /* test_conversion */
+
diff --git a/src/test_endswap.def b/src/test_endswap.def
new file mode 100644
index 0000000..1d6ab1f
--- /dev/null
+++ b/src/test_endswap.def
@@ -0,0 +1,40 @@
+autogen definitions test_endswap.tpl;
+
+int_type = {
+	name		= short ;
+	value		= '0x3210' ;
+	format		= FMT_SHORT ;
+	} ;
+
+int_type = {
+	name		= int ;
+	value		= '0x76543210' ;
+	format		= FMT_INT ;
+	} ;
+
+int_type = {
+	name		= int64_t ;
+	value		= '0x0807050540302010LL' ;
+	format		= FMT_INT64 ;
+	} ;
+
+int_size = {
+	name		= 16 ;
+	typename	= int16_t ;
+	value		= '0x4142' ;
+	strval		= "AB" ;
+	} ;
+
+int_size = {
+	name		= 32 ;
+	typename	= int32_t ;
+	value		= '0x30313233' ;
+	strval		= "0123" ;
+	} ;
+
+int_size = {
+	name		= 64 ;
+	typename	= int64_t ;
+	value		= '0x3031323334353637' ;
+	strval		= "01234567" ;
+	} ;
diff --git a/src/test_endswap.tpl b/src/test_endswap.tpl
new file mode 100644
index 0000000..348a6be
--- /dev/null
+++ b/src/test_endswap.tpl
@@ -0,0 +1,153 @@
+[+ AutoGen5 template c +]
+/*
+** Copyright (C) 2002-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#else
+#include "sf_unistd.h"
+#endif
+
+#include "common.h"
+#include "sfendian.h"
+
+#include "test_main.h"
+
+#define	FMT_SHORT	"0x%04x\n"
+#define	FMT_INT		"0x%08x\n"
+#define	FMT_INT64	"0x%016" PRIx64 "\n"
+
+/*==============================================================================
+** Test functions.
+*/
+
+[+ FOR int_type +]
+static void
+dump_[+ (get "name") +]_array (const char * name, [+ (get "name") +] * data, int datalen)
+{	int k ;
+
+	printf ("%-6s : ", name) ;
+	for (k = 0 ; k < datalen ; k++)
+		printf ([+ (get "format") +], data [k]) ;
+	putchar ('\n') ;
+} /* dump_[+ (get "name") +]_array */
+
+static void
+test_endswap_[+ (get "name") +] (void)
+{	[+ (get "name") +] orig [4], first [4], second [4] ;
+	int64_t k ;
+
+	printf ("    %-40s : ", "test_endswap_[+ (get "name") +]") ;
+	fflush (stdout) ;
+
+	for (k = 0 ; k < ARRAY_LEN (orig) ; k++)
+		orig [k] = [+ (get "value") +] + k ;
+
+	endswap_[+ (get "name") +]_copy (first, orig, ARRAY_LEN (first)) ;
+	endswap_[+ (get "name") +]_copy (second, first, ARRAY_LEN (second)) ;
+
+	if (memcmp (orig, first, sizeof (orig)) == 0)
+	{	printf ("\n\nLine %d : test 1 : these two array should not be the same:\n\n", __LINE__) ;
+		dump_[+ (get "name") +]_array ("orig", orig, ARRAY_LEN (orig)) ;
+		dump_[+ (get "name") +]_array ("first", first, ARRAY_LEN (first)) ;
+		exit (1) ;
+		} ;
+
+	if (memcmp (orig, second, sizeof (orig)) != 0)
+	{	printf ("\n\nLine %d : test 2 : these two array should be the same:\n\n", __LINE__) ;
+		dump_[+ (get "name") +]_array ("orig", orig, ARRAY_LEN (orig)) ;
+		dump_[+ (get "name") +]_array ("second", second, ARRAY_LEN (second)) ;
+		exit (1) ;
+		} ;
+
+	endswap_[+ (get "name") +]_array (first, ARRAY_LEN (first)) ;
+
+	if (memcmp (orig, first, sizeof (orig)) != 0)
+	{	printf ("\n\nLine %d : test 3 : these two array should be the same:\n\n", __LINE__) ;
+		dump_[+ (get "name") +]_array ("orig", orig, ARRAY_LEN (orig)) ;
+		dump_[+ (get "name") +]_array ("first", first, ARRAY_LEN (first)) ;
+		exit (1) ;
+		} ;
+
+	endswap_[+ (get "name") +]_copy (first, orig, ARRAY_LEN (first)) ;
+	endswap_[+ (get "name") +]_copy (first, first, ARRAY_LEN (first)) ;
+
+	if (memcmp (orig, first, sizeof (orig)) != 0)
+	{	printf ("\n\nLine %d : test 4 : these two array should be the same:\n\n", __LINE__) ;
+		dump_[+ (get "name") +]_array ("orig", orig, ARRAY_LEN (orig)) ;
+		dump_[+ (get "name") +]_array ("first", first, ARRAY_LEN (first)) ;
+		exit (1) ;
+		} ;
+
+	puts ("ok") ;
+} /* test_endswap_[+ (get "name") +] */
+[+ ENDFOR int_type
++]
+
+[+ FOR int_size +]
+static void
+test_psf_put_be[+ (get "name") +] (void)
+{	const char *test = "[+ (get "strval") +]" ;
+	uint8_t array [32] ;
+	int k ;
+
+	printf ("    %-40s : ", __func__) ;
+	fflush (stdout) ;
+
+	for (k = 0 ; k < 10 ; k++)
+	{	memset (array, 0, sizeof (array)) ;
+
+		psf_put_be[+ (get "name") +] (array, k, [+ (get "value") +]) ;
+		if (memcmp (array + k, test, sizeof ([+ (get "typename") +])) != 0)
+		{	printf ("\n\nLine %d : Put failed at index %d.\n", __LINE__, k) ;
+			exit (1) ;
+			} ;
+		if (psf_get_be[+ (get "name") +] (array, k) != [+ (get "value") +])
+		{	printf ("\n\nLine %d : Get failed at index %d.\n", __LINE__, k) ;
+			exit (1) ;
+			} ;
+		} ;
+
+	puts ("ok") ;
+} /* test_psf_put_be[+ (get "name") +] */
+[+ ENDFOR int_size
++]
+
+void
+test_endswap (void)
+{
+[+ FOR int_type
++]	test_endswap_[+ (get "name") +] () ;
+[+ ENDFOR int_type
++]
+
+[+ FOR int_size
++]	test_psf_put_be[+ (get "name") +] () ;
+[+ ENDFOR int_size
++]
+
+} /* test_endswap */
+
diff --git a/src/test_file_io.c b/src/test_file_io.c
new file mode 100644
index 0000000..1e5b0f5
--- /dev/null
+++ b/src/test_file_io.c
@@ -0,0 +1,492 @@
+/*
+** Copyright (C) 2002-2011 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#else
+#include "sf_unistd.h"
+#endif
+
+#include <string.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include "common.h"
+
+#include "test_main.h"
+
+static void make_data (int *data, int len, int seed) ;
+
+static void file_open_test (const char *filename) ;
+static void file_read_write_test (const char *filename) ;
+static void file_truncate_test (const char *filename) ;
+
+static void test_open_or_die (SF_PRIVATE *psf, int linenum) ;
+static void test_close_or_die (SF_PRIVATE *psf, int linenum) ;
+
+static void test_write_or_die	(SF_PRIVATE *psf, void *data, sf_count_t bytes, sf_count_t items, sf_count_t new_position, int linenum) ;
+static void test_read_or_die	(SF_PRIVATE *psf, void *data, sf_count_t bytes, sf_count_t items, sf_count_t new_position, int linenum) ;
+static void test_equal_or_die	(int *array1, int *array2, int len, int linenum) ;
+static void test_seek_or_die (SF_PRIVATE *psf, sf_count_t offset, int whence, sf_count_t new_position, int linenum) ;
+static void test_tell_or_die	(SF_PRIVATE *psf, sf_count_t expected_position, int linenum) ;
+
+
+
+/*==============================================================================
+** Actual test functions.
+*/
+
+static void
+file_open_test (const char *filename)
+{	SF_PRIVATE sf_data, *psf ;
+	int		error ;
+
+	print_test_name ("Testing file open") ;
+
+	memset (&sf_data, 0, sizeof (sf_data)) ;
+	psf = &sf_data ;
+
+	/* Ensure that the file doesn't already exist. */
+	if (unlink (filename) != 0 && errno != ENOENT)
+	{	printf ("\n\nLine %d: unlink failed (%d) : %s\n\n", __LINE__, errno, strerror (errno)) ;
+		exit (1) ;
+		} ;
+
+	psf->file.mode = SFM_READ ;
+	snprintf (psf->file.path, sizeof (psf->file.path), "%s", filename) ;
+
+	/* Test that open for read fails if the file doesn't exist. */
+	error = psf_fopen (psf) ;
+	if (error == 0)
+	{	printf ("\n\nLine %d: psf_fopen() should have failed.\n\n", __LINE__) ;
+		exit (1) ;
+		} ;
+
+	/* Reset error to zero. */
+	psf->error = SFE_NO_ERROR ;
+
+	/* Test file open in write mode. */
+	psf->file.mode = SFM_WRITE ;
+	test_open_or_die (psf, __LINE__) ;
+
+	test_close_or_die (psf, __LINE__) ;
+
+	unlink (psf->file.path) ;
+
+	/* Test file open in read/write mode for a non-existant file. */
+	psf->file.mode = SFM_RDWR ;
+	test_open_or_die (psf, __LINE__) ;
+
+	test_close_or_die (psf, __LINE__) ;
+
+	/* Test file open in read/write mode for an existing file. */
+	psf->file.mode = SFM_RDWR ;
+	test_open_or_die (psf, __LINE__) ;
+
+	test_close_or_die (psf, __LINE__) ;
+
+	unlink (psf->file.path) ;
+	puts ("ok") ;
+} /* file_open_test */
+
+static void
+file_read_write_test (const char *filename)
+{	static int data_out	[512] ;
+	static int data_in	[512] ;
+
+	SF_PRIVATE sf_data, *psf ;
+	sf_count_t retval ;
+
+	/*
+	** Open a new file and write two blocks of data to the file. After each
+	** write, test that psf_get_filelen() returns the new length.
+	*/
+
+	print_test_name ("Testing file write") ;
+
+	memset (&sf_data, 0, sizeof (sf_data)) ;
+	psf = &sf_data ;
+	snprintf (psf->file.path, sizeof (psf->file.path), "%s", filename) ;
+
+	/* Test file open in write mode. */
+	psf->file.mode = SFM_WRITE ;
+	test_open_or_die (psf, __LINE__) ;
+
+	make_data (data_out, ARRAY_LEN (data_out), 1) ;
+	test_write_or_die (psf, data_out, sizeof (data_out [0]), ARRAY_LEN (data_out), sizeof (data_out), __LINE__) ;
+
+	if ((retval = psf_get_filelen (psf)) != sizeof (data_out))
+	{	printf ("\n\nLine %d: file length after write is not correct (%" PRId64 " should be %zd).\n\n", __LINE__, retval, sizeof (data_out)) ;
+		if (retval == 0)
+			printf ("An fsync() may be necessary before fstat() in psf_get_filelen().\n\n") ;
+		exit (1) ;
+		} ;
+
+	make_data (data_out, ARRAY_LEN (data_out), 2) ;
+	test_write_or_die (psf, data_out, ARRAY_LEN (data_out), sizeof (data_out [0]), 2 * sizeof (data_out), __LINE__) ;
+
+	if ((retval = psf_get_filelen (psf)) != 2 * sizeof (data_out))
+	{	printf ("\n\nLine %d: file length after write is not correct. (%" PRId64 " should be %zd)\n\n", __LINE__, retval, 2 * sizeof (data_out)) ;
+		exit (1) ;
+		} ;
+
+	test_close_or_die (psf, __LINE__) ;
+	puts ("ok") ;
+
+	/*
+	** Now open the file in read mode, check the file length and check
+	** that the data is correct.
+	*/
+
+	print_test_name ("Testing file read") ;
+
+	/* Test file open in write mode. */
+	psf->file.mode = SFM_READ ;
+	test_open_or_die (psf, __LINE__) ;
+
+	make_data (data_out, ARRAY_LEN (data_out), 1) ;
+	test_read_or_die (psf, data_in, 1, sizeof (data_in), sizeof (data_in), __LINE__) ;
+	test_equal_or_die	(data_out, data_in, ARRAY_LEN (data_out), __LINE__) ;
+
+	make_data (data_out, ARRAY_LEN (data_out), 2) ;
+	test_read_or_die (psf, data_in, sizeof (data_in [0]), ARRAY_LEN (data_in), 2 * sizeof (data_in), __LINE__) ;
+	test_equal_or_die	(data_out, data_in, ARRAY_LEN (data_out), __LINE__) ;
+
+	test_close_or_die (psf, __LINE__) ;
+
+	puts ("ok") ;
+
+	/*
+	** Open the file in read/write mode, seek around a bit and then seek to
+	** the end of the file and write another block of data (3rd block). Then
+	** go back and check that all three blocks are correct.
+	*/
+
+	print_test_name ("Testing file seek") ;
+
+	/* Test file open in read/write mode. */
+	psf->file.mode = SFM_RDWR ;
+	test_open_or_die (psf, __LINE__) ;
+
+	test_seek_or_die (psf, 0, SEEK_SET, 0, __LINE__) ;
+	test_seek_or_die (psf, 0, SEEK_END, 2 * SIGNED_SIZEOF (data_out), __LINE__) ;
+	test_seek_or_die (psf, -1 * SIGNED_SIZEOF (data_out), SEEK_CUR, (sf_count_t) sizeof (data_out), __LINE__) ;
+
+	test_seek_or_die (psf, SIGNED_SIZEOF (data_out), SEEK_CUR, 2 * SIGNED_SIZEOF (data_out), __LINE__) ;
+	make_data (data_out, ARRAY_LEN (data_out), 3) ;
+	test_write_or_die (psf, data_out, sizeof (data_out [0]), ARRAY_LEN (data_out), 3 * sizeof (data_out), __LINE__) ;
+
+	test_seek_or_die (psf, 0, SEEK_SET, 0, __LINE__) ;
+	make_data (data_out, ARRAY_LEN (data_out), 1) ;
+	test_read_or_die (psf, data_in, 1, sizeof (data_in), sizeof (data_in), __LINE__) ;
+	test_equal_or_die	(data_out, data_in, ARRAY_LEN (data_out), __LINE__) ;
+
+	test_seek_or_die (psf, 2 * SIGNED_SIZEOF (data_out), SEEK_SET, 2 * SIGNED_SIZEOF (data_out), __LINE__) ;
+	make_data (data_out, ARRAY_LEN (data_out), 3) ;
+	test_read_or_die (psf, data_in, 1, sizeof (data_in), 3 * sizeof (data_in), __LINE__) ;
+	test_equal_or_die	(data_out, data_in, ARRAY_LEN (data_out), __LINE__) ;
+
+	test_seek_or_die (psf, SIGNED_SIZEOF (data_out), SEEK_SET, SIGNED_SIZEOF (data_out), __LINE__) ;
+	make_data (data_out, ARRAY_LEN (data_out), 2) ;
+	test_read_or_die (psf, data_in, 1, sizeof (data_in), 2 * sizeof (data_in), __LINE__) ;
+	test_equal_or_die	(data_out, data_in, ARRAY_LEN (data_out), __LINE__) ;
+
+	test_close_or_die (psf, __LINE__) ;
+	puts ("ok") ;
+
+	/*
+	** Now test operations with a non-zero psf->fileoffset field. This field
+	** sets an artificial file start positions so that a seek to the start of
+	** the file will actually be a seek to the value given by psf->fileoffset.
+	*/
+
+	print_test_name ("Testing file offset") ;
+
+	/* Test file open in read/write mode. */
+	psf->file.mode = SFM_RDWR ;
+	psf->fileoffset = sizeof (data_out [0]) * ARRAY_LEN (data_out) ;
+	test_open_or_die (psf, __LINE__) ;
+
+	if ((retval = psf_get_filelen (psf)) != 3 * sizeof (data_out))
+	{	printf ("\n\nLine %d: file length after write is not correct. (%" PRId64 " should be %zd)\n\n", __LINE__, retval, 3 * sizeof (data_out)) ;
+		exit (1) ;
+		} ;
+
+	test_seek_or_die (psf, SIGNED_SIZEOF (data_out), SEEK_SET, SIGNED_SIZEOF (data_out), __LINE__) ;
+	make_data (data_out, ARRAY_LEN (data_out), 5) ;
+	test_write_or_die (psf, data_out, sizeof (data_out [0]), ARRAY_LEN (data_out), 2 * sizeof (data_out), __LINE__) ;
+	test_close_or_die (psf, __LINE__) ;
+
+	/* final test with psf->fileoffset == 0. */
+
+	psf->file.mode = SFM_RDWR ;
+	psf->fileoffset = 0 ;
+	test_open_or_die (psf, __LINE__) ;
+
+	if ((retval = psf_get_filelen (psf)) != 3 * sizeof (data_out))
+	{	printf ("\n\nLine %d: file length after write is not correct. (%" PRId64 " should be %zd)\n\n", __LINE__, retval, 3 * sizeof (data_out)) ;
+		exit (1) ;
+		} ;
+
+	make_data (data_out, ARRAY_LEN (data_out), 1) ;
+	test_read_or_die (psf, data_in, 1, sizeof (data_in), sizeof (data_in), __LINE__) ;
+	test_equal_or_die	(data_out, data_in, ARRAY_LEN (data_out), __LINE__) ;
+
+	make_data (data_out, ARRAY_LEN (data_out), 2) ;
+	test_read_or_die (psf, data_in, 1, sizeof (data_in), 2 * sizeof (data_in), __LINE__) ;
+	test_equal_or_die	(data_out, data_in, ARRAY_LEN (data_out), __LINE__) ;
+
+	make_data (data_out, ARRAY_LEN (data_out), 5) ;
+	test_read_or_die (psf, data_in, 1, sizeof (data_in), 3 * sizeof (data_in), __LINE__) ;
+	test_equal_or_die	(data_out, data_in, ARRAY_LEN (data_out), __LINE__) ;
+
+	test_close_or_die (psf, __LINE__) ;
+
+	puts ("ok") ;
+} /* file_read_write_test */
+
+static void
+file_truncate_test (const char *filename)
+{	SF_PRIVATE sf_data, *psf ;
+	unsigned char buffer [256] ;
+	int k ;
+
+	/*
+	** Open a new file and write two blocks of data to the file. After each
+	** write, test that psf_get_filelen() returns the new length.
+	*/
+
+	print_test_name ("Testing file truncate") ;
+
+	memset (&sf_data, 0, sizeof (sf_data)) ;
+	memset (buffer, 0xEE, sizeof (buffer)) ;
+
+	psf = &sf_data ;
+	snprintf (psf->file.path, sizeof (psf->file.path), "%s", filename) ;
+
+	/*
+	** Open the file write mode, write 0xEE data and then extend the file
+	** using truncate (the extended data should be 0x00).
+	*/
+	psf->file.mode = SFM_WRITE ;
+	test_open_or_die (psf, __LINE__) ;
+	test_write_or_die (psf, buffer, sizeof (buffer) / 2, 1, sizeof (buffer) / 2, __LINE__) ;
+	psf_ftruncate (psf, sizeof (buffer)) ;
+	test_close_or_die (psf, __LINE__) ;
+
+	/* Open the file in read mode and check the data. */
+	psf->file.mode = SFM_READ ;
+	test_open_or_die (psf, __LINE__) ;
+	test_read_or_die (psf, buffer, sizeof (buffer), 1, sizeof (buffer), __LINE__) ;
+	test_close_or_die (psf, __LINE__) ;
+
+	for (k = 0 ; k < SIGNED_SIZEOF (buffer) / 2 ; k++)
+		if (buffer [k] != 0xEE)
+		{	printf ("\n\nLine %d : buffer [%d] = %hhu (should be 0xEE)\n\n", __LINE__, k, buffer [k]) ;
+			exit (1) ;
+			} ;
+
+	for (k = SIGNED_SIZEOF (buffer) / 2 ; k < SIGNED_SIZEOF (buffer) ; k++)
+		if (buffer [k] != 0)
+		{	printf ("\n\nLine %d : buffer [%d] = %hhu (should be 0)\n\n", __LINE__, k, buffer [k]) ;
+			exit (1) ;
+			} ;
+
+	/* Open the file in read/write and shorten the file using truncate. */
+	psf->file.mode = SFM_RDWR ;
+	test_open_or_die (psf, __LINE__) ;
+	psf_ftruncate (psf, sizeof (buffer) / 4) ;
+	test_close_or_die (psf, __LINE__) ;
+
+	/* Check the file length. */
+	psf->file.mode = SFM_READ ;
+	test_open_or_die (psf, __LINE__) ;
+	test_seek_or_die (psf, 0, SEEK_END, SIGNED_SIZEOF (buffer) / 4, __LINE__) ;
+	test_close_or_die (psf, __LINE__) ;
+
+	puts ("ok") ;
+} /* file_truncate_test */
+
+static void
+file_seek_with_offset_test (const char *filename)
+{	SF_PRIVATE sf_data, *psf ;
+	sf_count_t real_end ;
+	const size_t fileoffset = 64 ;
+
+	print_test_name ("Testing seek with offset") ;
+
+	/* Open the file created by the previous test for reading. */
+	memset (&sf_data, 0, sizeof (sf_data)) ;
+	psf = &sf_data ;
+	psf->file.mode = SFM_READ ;
+	snprintf (psf->file.path, sizeof (psf->file.path), "%s", filename) ;
+	test_open_or_die (psf, __LINE__) ;
+
+	/* Gather basic info before setting offset. */
+	real_end = psf_fseek (psf, 0, SEEK_END) ;
+	test_tell_or_die (psf, real_end, __LINE__) ;
+
+	/* Set the fileoffset (usually in a real system this is due to an id3 tag). */
+	psf->fileoffset = fileoffset ;
+
+	/* Check tell respects offset. */
+	test_tell_or_die (psf, real_end - fileoffset, __LINE__) ;
+
+	/* Check seeking works as expected. */
+	test_seek_or_die (psf, 0, SEEK_SET, 0, __LINE__) ;
+	test_seek_or_die (psf, 0, SEEK_CUR, 0, __LINE__) ;
+	test_seek_or_die (psf, 0, SEEK_CUR, 0, __LINE__) ;
+	test_seek_or_die (psf, 0, SEEK_END, real_end - fileoffset, __LINE__) ;
+
+	test_close_or_die (psf, __LINE__) ;
+
+	puts ("ok") ;
+} /* file_seek_with_offset_test */
+
+/*==============================================================================
+** Testing helper functions.
+*/
+
+static void
+test_open_or_die (SF_PRIVATE *psf, int linenum)
+{	int		error ;
+
+	/* Test that open for read fails if the file doesn't exist. */
+	error = psf_fopen (psf) ;
+	if (error)
+	{	printf ("\n\nLine %d: psf_fopen() failed : %s\n\n", linenum, strerror (errno)) ;
+		exit (1) ;
+		} ;
+
+} /* test_open_or_die */
+
+static void
+test_close_or_die (SF_PRIVATE *psf, int linenum)
+{
+	psf_fclose (psf) ;
+	if (psf_file_valid (psf))
+	{	printf ("\n\nLine %d: psf->file.filedes should not be valid.\n\n", linenum) ;
+		exit (1) ;
+		} ;
+
+} /* test_close_or_die */
+
+static void
+test_write_or_die (SF_PRIVATE *psf, void *data, sf_count_t bytes, sf_count_t items, sf_count_t new_position, int linenum)
+{	sf_count_t	retval ;
+
+	retval = psf_fwrite (data, bytes, items, psf) ;
+	if (retval != items)
+	{	printf ("\n\nLine %d: psf_write() returned %" PRId64 " (should be %" PRId64 ")\n\n", linenum, retval, items) ;
+		exit (1) ;
+		} ;
+
+	if ((retval = psf_ftell (psf)) != new_position)
+	{	printf ("\n\nLine %d: file length after write is not correct. (%" PRId64 " should be %" PRId64 ")\n\n", linenum, retval, new_position) ;
+		exit (1) ;
+		} ;
+
+	return ;
+} /* test_write_or_die */
+
+static void
+test_read_or_die (SF_PRIVATE *psf, void *data, sf_count_t bytes, sf_count_t items, sf_count_t new_position, int linenum)
+{	sf_count_t	retval ;
+
+	retval = psf_fread (data, bytes, items, psf) ;
+	if (retval != items)
+	{	printf ("\n\nLine %d: psf_write() returned %" PRId64 " (should be %" PRId64 ")\n\n", linenum, retval, items) ;
+		exit (1) ;
+		} ;
+
+	if ((retval = psf_ftell (psf)) != new_position)
+	{	printf ("\n\nLine %d: file length after write is not correct. (%" PRId64 " should be %" PRId64 ")\n\n", linenum, retval, new_position) ;
+		exit (1) ;
+		} ;
+
+	return ;
+} /* test_write_or_die */
+
+static void
+test_seek_or_die (SF_PRIVATE *psf, sf_count_t offset, int whence, sf_count_t new_position, int linenum)
+{	sf_count_t retval ;
+
+	retval = psf_fseek (psf, offset, whence) ;
+
+	if (retval != new_position)
+	{	printf ("\n\nLine %d: psf_fseek() failed. New position is %" PRId64 " (should be %" PRId64 ").\n\n",
+			linenum, retval, new_position) ;
+		exit (1) ;
+		} ;
+
+} /* test_seek_or_die */
+
+static void
+test_tell_or_die	(SF_PRIVATE *psf, sf_count_t expected_position, int linenum)
+{
+	sf_count_t retval ;
+
+	retval = psf_ftell (psf) ;
+
+	if (retval != expected_position)
+	{	printf ("\n\nLine %d: psf_ftell() failed. Position reported as %" PRId64 " (should be %" PRId64 ").\n\n",
+			linenum, retval, expected_position) ;
+		exit (1) ;
+		} ;
+}
+
+static void
+test_equal_or_die	(int *array1, int *array2, int len, int linenum)
+{	int k ;
+
+	for (k = 0 ; k < len ; k++)
+		if (array1 [k] != array2 [k])
+			printf ("\n\nLine %d: error at index %d (%d != %d).\n\n",
+				linenum, k, array1 [k], array2 [k]) ;
+
+	return ;
+} /* test_equal_or_die */
+
+static void
+make_data (int *data, int len, int seed)
+{	int k ;
+
+	srand (seed * 3333333 + 14756123) ;
+
+	for (k = 0 ; k < len ; k++)
+		data [k] = rand () ;
+
+} /* make_data */
+
+void
+test_file_io (void)
+{	const char *filename = "file_io.dat" ;
+
+	file_open_test	(filename) ;
+	file_read_write_test	(filename) ;
+	file_seek_with_offset_test (filename) ;
+	file_truncate_test (filename) ;
+
+	unlink (filename) ;
+} /* main */
+
diff --git a/src/test_float.c b/src/test_float.c
new file mode 100644
index 0000000..0787989
--- /dev/null
+++ b/src/test_float.c
@@ -0,0 +1,104 @@
+/*
+** Copyright (C) 2006-2012 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <math.h>
+
+#include "common.h"
+#include "test_main.h"
+
+void
+test_float_convert (void)
+{	static float data [] =
+	{	0.0, 1.0, -1.0, 1.0 * M_PI, -1.0 * M_PI,
+		1e9, -1e9, 1e-9, -1e-9, 1e-10, -1e-10,
+		1e-19, -1e-19, 1e19, -1e19, 1e-20, -1e-20,
+		} ;
+
+	int k ;
+
+	print_test_name (__func__) ;
+
+	for (k = 0 ; k < ARRAY_LEN (data) ; k++)
+	{	unsigned char bytes [4] ;
+		float test ;
+
+		float32_le_write (data [k], bytes) ;
+		test = float32_le_read (bytes) ;
+
+		if (fabs (data [k] - test) > 1e-20)
+		{	printf ("\n\nLine %d : Test %d, little endian error %.15g -> %.15g.\n\n", __LINE__, k, data [k], test) ;
+			exit (1) ;
+			} ;
+
+		float32_be_write (data [k], bytes) ;
+		test = float32_be_read (bytes) ;
+
+		if (fabs (data [k] - test) > 1e-20)
+		{	printf ("\n\nLine %d : Test %d, big endian error %.15g -> %.15g.\n\n", __LINE__, k, data [k], test) ;
+			exit (1) ;
+			} ;
+
+		} ;
+
+	puts ("ok") ;
+} /* test_float_convert */
+
+void
+test_double_convert (void)
+{	static double data [] =
+	{	0.0, 1.0, -1.0, 1.0 * M_PI, -1.0 * M_PI,
+		1e9, -1e9, 1e-9, -1e-9, 1e-10, -1e-10,
+		1e-19, -1e-19, 1e19, -1e19, 1e-20, -1e-20,
+		} ;
+
+	int k ;
+
+	print_test_name (__func__) ;
+
+	for (k = 0 ; k < ARRAY_LEN (data) ; k++)
+	{	unsigned char bytes [8] ;
+		double test ;
+
+		double64_le_write (data [k], bytes) ;
+		test = double64_le_read (bytes) ;
+
+		if (fabs (data [k] - test) > 1e-20)
+		{	printf ("\n\nLine %d : Test %d, little endian error %.15g -> %.15g.\n\n", __LINE__, k, data [k], test) ;
+			exit (1) ;
+			} ;
+
+		double64_be_write (data [k], bytes) ;
+		test = double64_be_read (bytes) ;
+
+		if (fabs (data [k] - test) > 1e-20)
+		{	printf ("\n\nLine %d : Test %d, big endian error %.15g -> %.15g.\n\n", __LINE__, k, data [k], test) ;
+			exit (1) ;
+			} ;
+
+		} ;
+
+	puts ("ok") ;
+} /* test_double_convert */
+
diff --git a/src/test_ima_oki_adpcm.c b/src/test_ima_oki_adpcm.c
new file mode 100644
index 0000000..6c937d1
--- /dev/null
+++ b/src/test_ima_oki_adpcm.c
@@ -0,0 +1,157 @@
+/*
+** Copyright (C) 2007-2011 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (c) 2007 <robs@users.sourceforge.net>
+**
+** This library is free software; you can redistribute it and/or modify it
+** under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or (at
+** your option) any later version.
+**
+** This library 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 Lesser
+** General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this library.  If not, write to the Free Software Foundation,
+** Fifth Floor, 51 Franklin Street, Boston, MA 02111-1301, USA.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+
+#include "test_main.h"
+
+#include "ima_oki_adpcm.c"
+
+static const unsigned char test_codes [] =
+{	0x08, 0x08, 0x04, 0x7f, 0x72, 0xf7, 0x9f, 0x7c, 0xd7, 0xbc, 0x7a, 0xa7, 0xb8,
+	0x4b, 0x0b, 0x38, 0xf6, 0x9d, 0x7a, 0xd7, 0xbc, 0x7a, 0xd7, 0xa8, 0x6c, 0x81,
+	0x98, 0xe4, 0x0e, 0x7a, 0xd7, 0x9e, 0x7b, 0xc7, 0xab, 0x7a, 0x85, 0xc0, 0xb3,
+	0x8f, 0x58, 0xd7, 0xad, 0x7a, 0xd7, 0xad, 0x7a, 0x87, 0xd0, 0x2b, 0x0e, 0x48,
+	0xd7, 0xad, 0x78, 0xf7, 0xbc, 0x7a, 0xb7, 0xa8, 0x4b, 0x88, 0x18, 0xd5, 0x8d,
+	0x6a, 0xa4, 0x98, 0x08, 0x00, 0x80, 0x88,
+} ;
+
+static const short test_pcm [] =
+{	32, 0, 32, 0, 32, 320, 880, -336, 2304, 4192, -992, 10128, 5360, -16352,
+	30208, 2272, -31872, 14688, -7040, -32432, 14128, -1392, -15488, 22960,
+	1232, -1584, 21488, -240, 2576, -15360, 960, -1152, -30032, 10320, 1008,
+	-30032, 16528, 1008, -30032, 16528, -5200, -30592, 15968, 448, -30592,
+	15968, 448, -2368, 30960, 3024, -80, 8384, 704, -1616, -29168, -1232, 1872,
+	-32768, 13792, -1728, -32768, 13792, 4480, -32192, 14368, -7360, -32752,
+	13808, -1712, -21456, 16992, 1472, -1344, 26848, -1088, 2016, -17728, 208,
+	-2112, -32768, 1376, -1728, -32768, 13792, -1728, -32768, 13792, -1728,
+	-32768, 13792, -1728, -32768, 13792, -1728, -4544, 32767, -1377, 1727,
+	15823, -2113, 207, -27345, 591, -2513, -32768, 13792, -1728, -32768, 13792,
+	10688, -31632, 14928, -6800, -32192, 14368, -1152, -20896, 17552, 2032,
+	-784, 22288, 560, -2256, -4816, 2176, 64, -21120, 9920, 6816, -24224, 16128,
+	608, -13488, 9584, 272, -2544, 16, -2304, -192, 1728, -16, 1568, 128, -1184,
+} ;
+
+
+static void
+test_oki_adpcm (void)
+{
+	IMA_OKI_ADPCM adpcm ;
+	unsigned char code ;
+	int i, j ;
+
+	print_test_name ("Testing ima/oki encoder") ;
+
+	ima_oki_adpcm_init (&adpcm, IMA_OKI_ADPCM_TYPE_OKI) ;
+	for (i = 0 ; i < ARRAY_LEN (test_codes) ; i++)
+		for (j = 0, code = test_codes [i] ; j < 2 ; j++, code <<= 4)
+			if (adpcm_decode (&adpcm, code >> 4) != test_pcm [2 * i + j])
+			{	printf ("\n\nFail at i = %d, j = %d.\n\n", i, j) ;
+				exit (1) ;
+				} ;
+
+	puts ("ok") ;
+
+	print_test_name ("Testing ima/oki decoder") ;
+
+	ima_oki_adpcm_init (&adpcm, IMA_OKI_ADPCM_TYPE_OKI) ;
+	for (i = 0 ; i < ARRAY_LEN (test_pcm) - 1 ; i += 2)
+	{	code = adpcm_encode (&adpcm, test_pcm [i]) ;
+		code = (code << 4) | adpcm_encode (&adpcm, test_pcm [i + 1]) ;
+		if (code != test_codes [i / 2])
+			{	printf ("\n\nFail at i = %d, %d should be %d\n\n", i, code, test_codes [i / 2]) ;
+				exit (1) ;
+				} ;
+		} ;
+
+	puts ("ok") ;
+} /* test_oki_adpcm */
+
+static void
+test_oki_adpcm_block (void)
+{
+	IMA_OKI_ADPCM adpcm ;
+	int k ;
+
+	if (ARRAY_LEN (adpcm.pcm) < ARRAY_LEN (test_pcm))
+	{	printf ("\n\nLine %d : ARRAY_LEN (adpcm->pcm) > ARRAY_LEN (test_pcm) (%d > %d).\n\n", __LINE__, ARRAY_LEN (adpcm.pcm), ARRAY_LEN (test_pcm)) ;
+		exit (1) ;
+		} ;
+
+	if (ARRAY_LEN (adpcm.codes) < ARRAY_LEN (test_codes))
+	{	printf ("\n\nLine %d : ARRAY_LEN (adcodes->codes) > ARRAY_LEN (test_codes).n", __LINE__) ;
+		exit (1) ;
+		} ;
+
+	print_test_name ("Testing ima/oki block encoder") ;
+
+	ima_oki_adpcm_init (&adpcm, IMA_OKI_ADPCM_TYPE_OKI) ;
+
+	memcpy (adpcm.pcm, test_pcm, sizeof (adpcm.pcm [0]) * ARRAY_LEN (test_pcm)) ;
+	adpcm.pcm_count = ARRAY_LEN (test_pcm) ;
+	adpcm.code_count = 13 ;
+
+	ima_oki_adpcm_encode_block (&adpcm) ;
+
+	if (adpcm.code_count * 2 != ARRAY_LEN (test_pcm))
+	{	printf ("\n\nLine %d : %d * 2 != %d\n\n", __LINE__, adpcm.code_count * 2, ARRAY_LEN (test_pcm)) ;
+		exit (1) ;
+		} ;
+
+	for (k = 0 ; k < ARRAY_LEN (test_codes) ; k++)
+		if (adpcm.codes [k] != test_codes [k])
+		{	printf ("\n\nLine %d : Fail at k = %d, %d should be %d\n\n", __LINE__, k, adpcm.codes [k], test_codes [k]) ;
+			exit (1) ;
+			} ;
+
+	puts ("ok") ;
+
+	print_test_name ("Testing ima/oki block decoder") ;
+
+	ima_oki_adpcm_init (&adpcm, IMA_OKI_ADPCM_TYPE_OKI) ;
+
+	memcpy (adpcm.codes, test_codes, sizeof (adpcm.codes [0]) * ARRAY_LEN (test_codes)) ;
+	adpcm.code_count = ARRAY_LEN (test_codes) ;
+	adpcm.pcm_count = 13 ;
+
+	ima_oki_adpcm_decode_block (&adpcm) ;
+
+	if (adpcm.pcm_count != 2 * ARRAY_LEN (test_codes))
+	{	printf ("\n\nLine %d : %d * 2 != %d\n\n", __LINE__, adpcm.pcm_count, 2 * ARRAY_LEN (test_codes)) ;
+		exit (1) ;
+		} ;
+
+	for (k = 0 ; k < ARRAY_LEN (test_pcm) ; k++)
+		if (adpcm.pcm [k] != test_pcm [k])
+		{	printf ("\n\nLine %d : Fail at i = %d, %d should be %d.\n\n", __LINE__, k, adpcm.pcm [k], test_pcm [k]) ;
+			exit (1) ;
+			} ;
+
+	puts ("ok") ;
+} /* test_oki_adpcm_block */
+
+void
+test_ima_oki_adpcm (void)
+{
+	test_oki_adpcm () ;
+	test_oki_adpcm_block () ;
+} /* main */
+
diff --git a/src/test_log_printf.c b/src/test_log_printf.c
new file mode 100644
index 0000000..ada6237
--- /dev/null
+++ b/src/test_log_printf.c
@@ -0,0 +1,125 @@
+/*
+** Copyright (C) 2003-2012 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include "common.h"
+
+#include "test_main.h"
+
+#define	CMP_0_ARGS(line, err, fmt)	\
+	{	psf->parselog.indx = 0 ;			\
+		snprintf (buffer, sizeof (buffer), (fmt)) ;	\
+		psf_log_printf (psf, (fmt)) ;				\
+		err += compare_strings_or_die (line, fmt, buffer, psf->parselog.buf) ;	\
+		}
+
+#define	CMP_2_ARGS(line, err, fmt, a)	\
+	{	psf->parselog.indx = 0 ;				\
+		snprintf (buffer, sizeof (buffer), (fmt), (a), (a)) ;	\
+		psf_log_printf (psf, (fmt), (a), (a)) ;					\
+		err += compare_strings_or_die (line, fmt, buffer, psf->parselog.buf) ;	\
+		}
+
+#define	CMP_4_ARGS(line, err, fmt, a)	\
+	{	psf->parselog.indx = 0 ;				\
+		snprintf (buffer, sizeof (buffer), (fmt), (a), (a), (a), (a)) ;	\
+		psf_log_printf (psf, (fmt), (a), (a), (a), (a)) ;				\
+		err += compare_strings_or_die (line, fmt, buffer, psf->parselog.buf) ;	\
+		}
+
+#define	CMP_5_ARGS(line, err, fmt, a)	\
+	{	psf->parselog.indx = 0 ;				\
+		snprintf (buffer, sizeof (buffer), (fmt), (a), (a), (a), (a), (a)) ;	\
+		psf_log_printf (psf, (fmt), (a), (a), (a), (a), (a)) ;					\
+		err += compare_strings_or_die (line, fmt, buffer, psf->parselog.buf) ;		\
+		}
+
+#define	CMP_6_ARGS(line, err, fmt, a)	\
+	{	psf->parselog.indx = 0 ;				\
+		snprintf (buffer, sizeof (buffer), (fmt), (a), (a), (a), (a), (a), (a)) ;	\
+		psf_log_printf (psf, (fmt), (a), (a), (a), (a), (a), (a)) ;					\
+		err += compare_strings_or_die (line, fmt, buffer, psf->parselog.buf) ;			\
+		}
+
+static int
+compare_strings_or_die (int linenum, const char *fmt, const char* s1, const char* s2)
+{	int errors = 0 ;
+/*-puts (s1) ;puts (s2) ;-*/
+
+	if (strcmp (s1, s2) != 0)
+	{	printf ("\n\nLine %d: string compare mismatch:\n\t", linenum) ;
+		printf ("\"%s\"\n", fmt) ;
+		printf ("\t\"%s\"\n\t\"%s\"\n", s1, s2) ;
+		errors ++ ;
+		} ;
+
+	return errors ;
+} /* compare_strings_or_die */
+
+void
+test_log_printf (void)
+{	static char buffer [2048] ;
+	SF_PRIVATE	sf_private, *psf ;
+	int			k, errors = 0 ;
+	int			int_values [] = { 0, 1, 12, 123, 1234, 123456, -1, -12, -123, -1234, -123456 } ;
+
+	print_test_name ("Testing psf_log_printf") ;
+
+	psf = &sf_private ;
+	memset (psf, 0, sizeof (sf_private)) ;
+
+	CMP_0_ARGS (__LINE__, errors, " ->%%<- ") ;
+
+	/* Test printing of ints. */
+	for (k = 0 ; k < ARRAY_LEN (int_values) ; k++)
+		CMP_6_ARGS (__LINE__, errors, "int A : %d, % d, %4d, % 4d, %04d, % 04d", int_values [k]) ;
+
+	for (k = 0 ; k < ARRAY_LEN (int_values) ; k++)
+		CMP_5_ARGS (__LINE__, errors, "int B : %+d, %+4d, %+04d, %-d, %-4d", int_values [k]) ;
+
+	for (k = 0 ; k < ARRAY_LEN (int_values) ; k++)
+		CMP_2_ARGS (__LINE__, errors, "int C : %- d, %- 4d", int_values [k]) ;
+
+	/* Test printing of unsigned ints. */
+	for (k = 0 ; k < ARRAY_LEN (int_values) ; k++)
+		CMP_4_ARGS (__LINE__, errors, "D : %u, %4u, %04u, %0u", int_values [k]) ;
+
+	/* Test printing of hex ints. */
+	for (k = 0 ; k < ARRAY_LEN (int_values) ; k++)
+		CMP_4_ARGS (__LINE__, errors, "E : %X, %4X, %04X, %0X", int_values [k]) ;
+
+	/* Test printing of strings. */
+	CMP_4_ARGS (__LINE__, errors, "B %s, %3s, %8s, %-8s", "str") ;
+
+	CMP_4_ARGS (__LINE__, errors, "B %.2s, %.8s, %-8.8s, %-4.2s", "str") ;
+
+	if (errors)
+	{	puts ("\nExiting due to errors.\n") ;
+		exit (1) ;
+		} ;
+
+	puts ("ok") ;
+} /* test_log_printf */
+
diff --git a/src/test_main.c b/src/test_main.c
new file mode 100644
index 0000000..f5a501d
--- /dev/null
+++ b/src/test_main.c
@@ -0,0 +1,70 @@
+/*
+** Copyright (C) 2008-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#if defined (HAVE_SYS_TYPES_H) && (HAVE_SYS_TYPES_H == 1)
+#include <sys/types.h>
+#endif
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include "test_main.h"
+
+static void
+test_file_offsets_are_64_bit (void)
+{
+	print_test_name ("File offsets are 64 bit") ;
+
+	// The Windows specific code path uses the 64 bit file I/O APIs.
+	if (! USE_WINDOWS_API && sizeof (off_t) != 8)
+	{	printf ("\n\nError : sizeof (off_t) is %zd (should be 8).\n\n", sizeof (off_t)) ;
+		exit (1) ;
+		} ;
+
+	puts ("ok") ;
+} /* test_file_offsets_are_64_bit */
+
+int
+main (void)
+{
+	test_file_offsets_are_64_bit () ;
+	test_conversions () ;
+	test_endswap () ;
+	test_float_convert () ;
+	test_double_convert () ;
+
+	test_log_printf () ;
+	test_binheader_writef () ;
+	test_file_io () ;
+
+	test_audio_detect () ;
+	test_ima_oki_adpcm () ;
+
+	test_psf_strlcpy_crlf () ;
+	test_broadcast_var () ;
+	test_cart_var () ;
+
+	test_nms_adpcm () ;
+
+	return 0 ;
+} /* main */
+
diff --git a/src/test_main.h b/src/test_main.h
new file mode 100644
index 0000000..d08fb2e
--- /dev/null
+++ b/src/test_main.h
@@ -0,0 +1,44 @@
+/*
+** Copyright (C) 2008-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+static inline void
+print_test_name (const char * name)
+{	printf ("    %-40s : ", name) ;
+	fflush (stdout) ;
+} /* print_test_name */
+
+
+
+void test_conversions (void) ;
+void test_endswap (void) ;
+void test_log_printf (void) ;
+void test_binheader_writef (void) ;
+void test_file_io (void) ;
+
+void test_float_convert (void) ;
+void test_double_convert (void) ;
+
+void test_audio_detect (void) ;
+void test_ima_oki_adpcm (void) ;
+
+void test_psf_strlcpy_crlf (void) ;
+void test_broadcast_var (void) ;
+
+void test_cart_var (void) ;
+
+void test_nms_adpcm (void) ;
diff --git a/src/test_nms_adpcm.c b/src/test_nms_adpcm.c
new file mode 100644
index 0000000..8558a04
--- /dev/null
+++ b/src/test_nms_adpcm.c
@@ -0,0 +1,400 @@
+/*
+** Copyright (C) 2007-2018 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2017-2018 Arthur Taylor <art@ified.ca>
+**
+** This library is free software; you can redistribute it and/or modify it
+** under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or (at
+** your option) any later version.
+**
+** This library 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 Lesser
+** General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this library.  If not, write to the Free Software Foundation,
+** Fifth Floor, 51 Franklin Street, Boston, MA 02111-1301, USA.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+
+#include "test_main.h"
+
+#include "nms_adpcm.c"
+
+static const short pcm_data_src [] =
+{	505, 743, 805, 409, 101, -552, -709, -679,
+	-624, -1174, -1050, 32, -401, -769, -679, 47,
+	-3273, -4425, -2158, -176, 824, 1057, 1245, 805,
+	2414, 3282, 1739, -624, -1197, -1663, -913, 603,
+	549, -614, 707, 3314, 2864, 1127, -294, -919,
+	-1251, -147, 30, -750, -131, 1394, 2197, 1829,
+	1387, 417, 391, 533, 581, 179, -210, -210,
+	-884, -937, -1373, -1338, -1811, -2727, -2536, -1552,
+	-651, -3556, -7713, -9083, -6182, 1070, 4983, 5341,
+	4596, 4682, 6488, 5197, 2401, -2702, -5261, -4036,
+	-1995, 463, 1056, 2436, 3238, 4395, 4478, 1130,
+	-2383, -4349, -4547, -3631, -1396, 1029, 2589, 3948,
+	4966, 4312, 2362, 492, -1289, -2259, -1659, -597,
+	239, 1433, 2353, 2512, 1763, 610, -291, -640,
+	7, 132, 432, 438, -1070, -1202, -1071, -1841,
+	-462, 225, -4465, -11313, -10492, -4522, -2096, -7013,
+	-11770, -2546, 7687, 12765, 12014, 5324, 1645, 1191,
+	3800, -187, -6689, -7778, -4631, 2487, 7352, 7928,
+	4317, 2424, 3784, 2301, -1713, -6668, -8345, -6453,
+	-2303, 2269, 3232, 4114, 5054, 5054, 3768, 1060,
+	-1793, -3302, -2059, -86, 1153, 1690, 2869, 3841,
+	3551, 1919, -197, -1391, -847, 128, 746, 1111,
+	431, 559, 1086, 138, -1539, -2758, -1886, 1351,
+	2407, -1883, -8356, -10999, -9917, -7329, -4295, -3209,
+	-11616, -15352, 1032, 12603, 13233, 9059, 4019, 1858,
+	3368, 7454, -56, -8600, -7278, -818, 5478, 7039,
+	5630, 1186, 1634, 5422, 2518, -3182, -8271, -7889,
+	-4399, -129, 3205, 2933, 3661, 5886, 6543, 3798,
+	374, -2722, -3378, -1804, -24, 385, 1663, 3595,
+	4749, 3865, 1402, -851, -1932, -1394, -725, -219,
+	290, 658, 1074, 1638, 536, 204, -340, 408,
+	1835, 1261, -2872, -4840, -5978, -8177, -7644, -6554,
+	-8093, -6174, -7796, -17019, -12355, 1280, 12576, 11868,
+	10710, 8578, 5605, 9675, 7123, -977, -8770, -6740,
+	-1327, 2905, 6386, 5026, 3809, 5137, 6392, 2463,
+	-4924, -8830, -9572, -6122, -1608, 1677, 3379, 5660,
+	8236, 7225, 4470, 295, -2628, -3572, -2107, -666,
+	951, 3101, 5049, 4759, 2367, -140, -2078, -2471,
+	-2332, -1547, -798, 410, 1825, 3329, 3092, 352,
+	-3310, -3307, -1229, -415, 532, 2091, 465, -1430
+} ;
+
+/* pcm_data encoded as 16kbs from a known reference */
+static const unsigned short test_codes_16 [] =
+{	0x5777, 0xfff0, 0xdcd0, 0x672d, 0x1826, 0xc11c, 0x0822, 0xffee,
+	0x3ddc, 0x6372, 0x0116, 0xc8d8, 0x6780, 0x8624, 0x3323, 0x33ef,
+	0xd865, 0x4cd8, 0x3372, 0x1096, 0x0049, 0xa911, 0x1288, 0xa74d,
+	0x3fee, 0xcc45, 0x52de, 0x6a72, 0x9118, 0xe291, 0x60a2, 0x3164,
+	0x73fe, 0xeddf, 0x57b5, 0x185a, 0xe889, 0x460e, 0x2646, 0x8d87,
+	0xe5ba, 0x004c
+} ;
+
+/* pcm_data encoded as 24kbs from a known reference */
+static const unsigned short test_codes_24 [] =
+{	0x7776, 0x2fec, 0xceb0, 0xffd0, 0x3241, 0x650a, 0x0a26, 0x61ba,
+	0xa10b, 0x3912, 0x39a8, 0xebfa, 0x1fff, 0x8552, 0x2342, 0x0204,
+	0x454b, 0xccbb, 0x4318, 0xaa00, 0x1642, 0x3031, 0xfc8f, 0x38ff,
+	0xf604, 0x4924, 0x2ddb, 0x0469, 0xbcaa, 0x83b6, 0x0049, 0x8828,
+	0x2266, 0x3801, 0x873d, 0xcb86, 0x0eff, 0xef64, 0xd402, 0x44fa,
+	0x2867, 0xd1d0, 0xa109, 0x2a11, 0x8a64, 0x4018, 0x1357, 0xd5a5,
+	0x4bfc, 0xcbfe, 0x070a, 0x6307, 0x1858, 0x624b, 0xf9a9, 0x783b,
+	0x0880, 0x1652, 0xc893, 0x641c, 0xf30d, 0x004c
+} ;
+
+/* pcm_data encoded as 32kbs from a known reference */
+static const unsigned short test_codes_32 [] =
+{	0x7772, 0x0cdc, 0xbec2, 0xacb2, 0xff90, 0x1220, 0x551c, 0xcc84,
+	0x2c47, 0x30aa, 0xa10b, 0x0663, 0x2812, 0x28a9, 0xf9ba, 0xceb9,
+	0x1fec, 0x9553, 0x2361, 0x9ed8, 0x8314, 0x564b, 0xddba, 0x1346,
+	0x6308, 0xab00, 0x0721, 0x2908, 0x3820, 0xf89c, 0x38ff, 0xa2bf,
+	0xc535, 0x2933, 0x5de9, 0x8633, 0x8569, 0xbeca, 0x1186, 0x5528,
+	0xd000, 0xaa21, 0x0473, 0x2800, 0x1112, 0xa64d, 0xdc17, 0x8eeb,
+	0xccac, 0xfe74, 0xc501, 0x63f9, 0x2040, 0x3a73, 0xc9b9, 0x9188,
+	0x7318, 0x0a81, 0x9a65, 0x5188, 0x00ba, 0x2256, 0xd5b6, 0x4bfa,
+	0xbeac, 0xe8fe, 0x343b, 0x7117, 0x9ca4, 0x915a, 0x563d, 0xcad0,
+	0xa837, 0x302a, 0x1a2a, 0x3561, 0x98a9, 0xb9b5, 0x578a, 0xc48b,
+	0x25f0, 0x1000
+} ;
+
+
+/* test_codes_16 decoded by a known reference */
+const short pcm_data_out16 [] =
+{	12, 16, 24, 36, 52, -68, -104, -156,
+	-224, -309, -433, 12, -449, -618, -851, 32,
+	-871, -1176, -1586, 60, 1172, 634, 1566, 983,
+	1995, 2586, 1718, -1152, -815, -2313, -1610, 1261,
+	1056, -253, 522, 1799, 2506, 1518, 72, -329,
+	-1510, -76, 337, -1144, 68, 1369, 2200, 1337,
+	1016, 60, 405, 461, 433, 389, -36, -164,
+	-550, -871, -1212, -1626, -2136, -2791, -2064, -1642,
+	-485, -1566, -2538, -3445, -4481, 650, 4381, 3799,
+	5807, 4742, 6674, 5590, 2072, -2228, -5650, -4983,
+	-1698, 441, 1333, 3064, 2855, 3815, 5108, 2156,
+	-2228, -3321, -5028, -4405, -1550, 771, 3232, 3273,
+	4008, 5008, 2024, 859, -654, -2746, -1694, -136,
+	68, 1409, 1759, 2453, 2016, 522, -514, -445,
+	0, 305, 493, 518, -232, -1076, -1116, -1321,
+	-506, 365, -140, -1132, -2076, -2895, -2357, -2477,
+	-3325, -2859, 666, 4449, 7164, 6244, 1847, 1365,
+	3827, -779, -7682, -8951, -3811, 1718, 6566, 7120,
+	4674, 1959, 1819, 2032, -1104, -5220, -8518, -7626,
+	-2385, 2714, 3510, 3871, 4831, 4024, 4156, 1590,
+	-1694, -3437, -2393, 96, 959, 1847, 2775, 3638,
+	3072, 1734, -204, -1730, -718, -92, 453, 807,
+	220, 514, 1349, -40, -1285, -2477, -1566, 1273,
+	2586, 546, -2887, -5534, -6883, -7461, -5281, -2224,
+	-2361, -5104, -48, 9228, 12140, 9048, 3614, 1927,
+	4618, 6004, -148, -9871, -5582, -489, 6835, 6746,
+	6839, 2851, 3028, 4566, 1461, -2028, -6883, -7642,
+	-5321, -610, 3385, 3461, 3088, 3389, 4570, 4321,
+	-389, -2630, -3369, -1706, -136, 220, 1594, 3024,
+	4622, 4232, 1265, -943, -2273, -1638, -726, -232,
+	365, 538, 995, 1530, 289, 453, -68, 12,
+	1184, 1562, 92, -2558, -4859, -6277, -7096, -5461,
+	-4811, -6020, -8851, -12594, -11501, -943, 12927, 10449,
+	8935, 10389, 5662, 5755, 9108, 1827, -10224, -7807,
+	-148, 3429, 7722, 5212, 4734, 3847, 5570, 3433,
+	-3931, -8244, -8461, -5397, -1710, 1919, 3787, 4558,
+	5040, 5722, 4811, -441, -3140, -4180, -2397, -493,
+	1309, 3064, 4116, 5040, 2759, -730, -2445, -2847,
+	-2080, -1682, -1124, 706, 2032, 3325, 3248, 425,
+	-3586, -2987, -1397, -188, 144, 1506, 4, -2028
+} ;
+
+/* test_codes_24 decoded by a known reference */
+static const short pcm_data_out24 [] =
+{	16, 32, 68, 140, 116, -232, -510, -650,
+	-771, -1329, -1052, -152, -317, -907, -710, -104,
+	-1144, -2132, -2598, -301, 662, 827, 1469, 702,
+	2401, 2987, 1574, -244, -1481, -1365, -903, 738,
+	369, -469, 473, 1630, 3124, 1542, -582, -1172,
+	-1381, -317, 4, -610, -40, 1236, 1843, 1493,
+	1349, 417, 389, 630, 686, 188, -228, -168,
+	-742, -795, -1530, -1473, -1903, -3008, -2907, -1317,
+	-445, -2309, -4919, -8939, -5867, 1204, 5293, 5337,
+	4871, 4562, 5602, 5104, 2485, -2337, -5594, -4240,
+	-1694, 867, 1281, 2622, 3638, 4228, 4654, 1405,
+	-1947, -4112, -4184, -3582, -1570, 1325, 2538, 4036,
+	5144, 4630, 2718, 518, -1373, -2397, -1642, -453,
+	349, 1566, 2558, 2493, 1927, 662, -365, -610,
+	-136, 188, 453, 437, -385, -1281, -1196, -1534,
+	-369, 265, -899, -3445, -7176, -4538, -2726, -5650,
+	-13152, -1694, 7040, 11489, 12224, 5971, 1971, 1779,
+	3457, -373, -6040, -7714, -5008, 2594, 7658, 8156,
+	4461, 2333, 4369, 2867, -1919, -7180, -8465, -6409,
+	-2618, 2152, 3120, 4208, 5570, 5558, 4120, 690,
+	-2088, -3345, -1975, -208, 1180, 1738, 2144, 3289,
+	3686, 1819, -417, -1534, -875, 88, 678, 967,
+	437, 558, 951, 20, -1638, -2558, -1967, 558,
+	2289, 465, -4449, -11080, -8931, -6248, -4208, -3337,
+	-6493, -14550, -5068, 12305, 13261, 9742, 4261, 1851,
+	3016, 6971, 441, -9554, -7096, -975, 5188, 6658,
+	5409, 1341, 855, 6164, 1726, -2381, -7991, -7212,
+	-4799, -433, 3236, 3273, 3253, 4445, 6706, 3329,
+	582, -2602, -3028, -1614, -152, 196, 1598, 3638,
+	5144, 4016, 1586, -1004, -2016, -1401, -682, -128,
+	273, 614, 963, 1614, 425, 269, -449, 277,
+	1746, 1240, -1510, -4598, -6397, -8008, -7602, -7152,
+	-7393, -6738, -8606, -15385, -13385, 1192, 12212, 11152,
+	9967, 8622, 5240, 6939, 7369, -2216, -9602, -7425,
+	-999, 3228, 6329, 4702, 4305, 4550, 6216, 3072,
+	-4983, -9313, -9437, -5586, -1987, 2088, 3184, 4662,
+	8244, 6598, 4606, -277, -2718, -3188, -2321, -437,
+	835, 2855, 4638, 4943, 2116, -393, -2269, -2502,
+	-2445, -1630, -646, 469, 1927, 3188, 2943, 502,
+	-3148, -3100, -1144, -642, 658, 1843, 449, -1445
+} ;
+
+/* test_codes_32 decoded by a known reference */
+static const short pcm_data_out32 [] =
+{	20, 96, 417, 433, 140, -506, -742, -714,
+	-598, -1092, -1044, 56, -445, -702, -622, 76,
+	-1116, -4293, -2429, -433, 606, 1196, 1357, 650,
+	2465, 3040, 1730, -682, -1381, -1759, -867, 518,
+	614, -698, 751, 2172, 3216, 1369, -562, -1076,
+	-1293, -116, -12, -803, -176, 1297, 2228, 1759,
+	1257, 425, 453, 614, 622, 188, -212, -220,
+	-975, -951, -1441, -1309, -1698, -2578, -2405, -1650,
+	-590, -2293, -7052, -8506, -5907, 1100, 5192, 5305,
+	4244, 4425, 6779, 5313, 2152, -2654, -5598, -3803,
+	-2176, 301, 1080, 2281, 3361, 4485, 4690, 1269,
+	-2253, -4477, -4562, -3598, -1345, 1108, 2638, 3783,
+	4819, 4401, 2357, 409, -1180, -2204, -1730, -662,
+	168, 1566, 2550, 2333, 1879, 485, -293, -690,
+	-28, 176, 445, 413, -767, -1088, -1204, -1847,
+	-481, 261, -1321, -8714, -10646, -4265, -1979, -7100,
+	-11678, -1911, 7449, 13333, 11991, 5244, 1935, 1072,
+	3638, -4, -6377, -7650, -4819, 2674, 7148, 8036,
+	4325, 2433, 3855, 2204, -1638, -6361, -8192, -6634,
+	-2184, 2144, 3357, 4164, 4783, 5168, 3835, 1100,
+	-1670, -3224, -2140, -144, 1120, 1755, 2530, 3626,
+	3678, 1771, -281, -1289, -875, 48, 755, 1112,
+	449, 546, 1140, 232, -1530, -2783, -1871, 1128,
+	2216, -1899, -8606, -11333, -10140, -7546, -4357, -2979,
+	-6044, -14851, -3726, 13136, 13477, 9534, 3871, 1489,
+	3526, 7012, 80, -8188, -7140, -1120, 5783, 7060,
+	5823, 1337, 1108, 5566, 2345, -3373, -8140, -7919,
+	-4566, 76, 3060, 2795, 3385, 5907, 6558, 3638,
+	257, -2630, -3401, -1807, -116, 349, 1610, 3417,
+	4750, 3967, 1489, -907, -1923, -1385, -666, -265,
+	253, 682, 1084, 1586, 538, 184, -381, 433,
+	1875, 1289, -1574, -4538, -6168, -8196, -7887, -6750,
+	-7526, -6060, -8148, -16036, -12546, 895, 12991, 12060,
+	10827, 8931, 5321, 8646, 7654, -473, -8582, -6614,
+	-1321, 2803, 6542, 5184, 3847, 4943, 6397, 2148,
+	-4999, -8799, -9614, -5931, -1574, 1546, 3493, 5397,
+	7879, 6919, 4610, 160, -2538, -3582, -2052, -578,
+	1060, 2987, 4843, 4791, 2421, -116, -1987, -2518,
+	-2333, -1534, -855, 365, 1779, 3389, 3080, 477,
+	-3281, -3120, -1188, -265, 638, 2224, 333, -1377
+} ;
+
+
+static void
+test_nms_adpcm_32 (void)
+{
+	struct nms_adpcm_state nms ;
+	int16_t *buffer ;
+	unsigned char code ;
+	int i, j, sl ;
+
+	buffer = (int16_t *) malloc (sizeof (int16_t) * NMS_SAMPLES_PER_BLOCK) ;
+
+	print_test_name ("Testing nms adpcm 32kbs encoder") ;
+
+	nms_adpcm_codec_init (&nms, NMS32) ;
+	for (i = 0 ; i * NMS_BLOCK_SHORTS_32 < ARRAY_LEN (test_codes_32) ; i ++)
+	{	/* Unpack the reference */
+		nms_adpcm_block_unpack_32 (&(test_codes_32 [i * NMS_BLOCK_SHORTS_32]), buffer, NULL) ;
+		for (j = 0 ; j < NMS_SAMPLES_PER_BLOCK ; j++)
+		{	sl = pcm_data_src [i * NMS_SAMPLES_PER_BLOCK + j] ;
+			code = nms_adpcm_encode_sample (&nms, sl) ;
+			if (code != buffer [j])
+			{	printf ("\n\nFail at sample %d (block %d, sample %d). Expected 0x%x got 0x%x\n\n",
+					i * NMS_SAMPLES_PER_BLOCK + j, i, j, buffer [j], code) ;
+				exit (1) ;
+				}
+			}
+		}
+
+	puts ("ok") ;
+
+	print_test_name ("Testing nms adpcm 32kbs decoder") ;
+
+	nms_adpcm_codec_init (&nms, NMS32) ;
+	for (i = 0 ; i * NMS_BLOCK_SHORTS_32 < ARRAY_LEN (test_codes_32) ; i ++)
+	{	/* Unpack the code */
+		nms_adpcm_block_unpack_32 (&(test_codes_32 [i * NMS_BLOCK_SHORTS_32]), buffer, NULL) ;
+		for (j = 0 ; j < NMS_SAMPLES_PER_BLOCK ; j++)
+		{	sl = nms_adpcm_decode_sample (&nms, buffer [j]) ;
+			if (sl != pcm_data_out32 [i * NMS_SAMPLES_PER_BLOCK + j])
+			{	printf ("\n\nFail at sample %d (block %d, sample %d). Expected %d got %d\n\n",
+					i * NMS_SAMPLES_PER_BLOCK + j, i, j, pcm_data_out32 [i * NMS_SAMPLES_PER_BLOCK + j], sl) ;
+				exit (1) ;
+				}
+			}
+		}
+
+	puts ("ok") ;
+
+	free (buffer) ;
+}
+
+
+static void
+test_nms_adpcm_24 (void)
+{
+	struct nms_adpcm_state nms ;
+	int16_t *buffer ;
+	unsigned char code ;
+	int i, j, sl ;
+
+	buffer = (int16_t *) malloc (sizeof (int16_t) * NMS_SAMPLES_PER_BLOCK) ;
+
+
+	print_test_name ("Testing nms adpcm 24kbs encoder") ;
+
+	nms_adpcm_codec_init (&nms, NMS24) ;
+	for (i = 0 ; i * NMS_BLOCK_SHORTS_24 < ARRAY_LEN (test_codes_24) ; i ++)
+	{	/* Unpack the reference */
+		nms_adpcm_block_unpack_24 (&test_codes_24 [i * NMS_BLOCK_SHORTS_24], buffer, NULL) ;
+		for (j = 0 ; j < NMS_SAMPLES_PER_BLOCK ; j++)
+		{	sl = pcm_data_src [i * NMS_SAMPLES_PER_BLOCK + j] ;
+			code = nms_adpcm_encode_sample (&nms, sl) ;
+			if (code != buffer [j])
+			{	printf ("\n\nFail at sample %d (block %d, sample %d). Expected 0x%x got 0x%x\n\n",
+					i * NMS_SAMPLES_PER_BLOCK + j, i, j, buffer [j], code) ;
+				exit (1) ;
+				}
+			}
+		}
+
+	puts ("ok") ;
+
+
+	print_test_name ("Testing nms adpcm 24kbs decoder") ;
+
+	nms_adpcm_codec_init (&nms, NMS24) ;
+	for (i = 0 ; i * NMS_BLOCK_SHORTS_24 < ARRAY_LEN (test_codes_24) ; i ++)
+	{	/* Unpack the code */
+		nms_adpcm_block_unpack_24 (&test_codes_24 [i * NMS_BLOCK_SHORTS_24], buffer, NULL) ;
+		for (j = 0 ; j < NMS_SAMPLES_PER_BLOCK ; j++)
+		{	sl = nms_adpcm_decode_sample (&nms, buffer [j]) ;
+			if (sl != pcm_data_out24 [i * NMS_SAMPLES_PER_BLOCK + j])
+			{	printf ("\n\nFail at sample %d (block %d, sample %d). Expected %d got %d\n\n",
+					i * NMS_SAMPLES_PER_BLOCK + j, i, j, pcm_data_out24 [i * NMS_SAMPLES_PER_BLOCK + j], sl) ;
+				exit (1) ;
+				}
+			}
+		}
+
+	puts ("ok") ;
+
+	free (buffer) ;
+} /* test_nms_adpcm_24 */
+
+static void
+test_nms_adpcm_16 (void)
+{	struct nms_adpcm_state nms ;
+	int16_t *buffer ;
+	unsigned char code ;
+	int i, j, sl ;
+
+	buffer = (int16_t *) malloc (sizeof (int16_t) * NMS_SAMPLES_PER_BLOCK) ;
+
+	print_test_name ("Testing nms adpcm 16kbs encoder") ;
+
+	nms_adpcm_codec_init (&nms, NMS16) ;
+	for (i = 0 ; i * NMS_BLOCK_SHORTS_16 < ARRAY_LEN (test_codes_16) ; i ++)
+	{	/* Unpack the reference */
+		nms_adpcm_block_unpack_16 (&test_codes_16 [i * NMS_BLOCK_SHORTS_16], buffer, NULL) ;
+		for (j = 0 ; j < NMS_SAMPLES_PER_BLOCK ; j++)
+		{	sl = pcm_data_src [i * NMS_SAMPLES_PER_BLOCK + j] ;
+			code = nms_adpcm_encode_sample (&nms, sl) ;
+			if (code != buffer [j])
+			{	printf ("\n\nFail at sample %d (block %d, sample %d). Expected 0x%x got 0x%x\n\n",
+					i * NMS_SAMPLES_PER_BLOCK + j, i, j, buffer [j], code) ;
+				exit (1) ;
+				}
+			}
+		}
+
+	puts ("ok") ;
+
+	print_test_name ("Testing nms adpcm 16kbs decoder") ;
+
+	nms_adpcm_codec_init (&nms, NMS16) ;
+	for (i = 0 ; i * NMS_BLOCK_SHORTS_16 < ARRAY_LEN (test_codes_16) ; i ++)
+	{	/* Unpack the code */
+		nms_adpcm_block_unpack_16 (&test_codes_16 [i * NMS_BLOCK_SHORTS_16], buffer, NULL) ;
+		for (j = 0 ; j < NMS_SAMPLES_PER_BLOCK ; j++)
+		{	sl = nms_adpcm_decode_sample (&nms, buffer [j]) ;
+			if (sl != pcm_data_out16 [i * NMS_SAMPLES_PER_BLOCK + j])
+			{	printf ("\n\nFail at sample %d (block %d, sample %d). Expected %d got %d\n\n",
+					i * NMS_SAMPLES_PER_BLOCK + j, i, j, pcm_data_out16 [i * NMS_SAMPLES_PER_BLOCK + j], sl) ;
+				exit (1) ;
+				}
+			}
+		}
+
+	puts ("ok") ;
+
+	free (buffer) ;
+} /* test_nms_adpcm_16 */
+
+void
+test_nms_adpcm (void)
+{	test_nms_adpcm_32 () ;
+	test_nms_adpcm_24 () ;
+	test_nms_adpcm_16 () ;
+} /* main */
+
diff --git a/src/test_strncpy_crlf.c b/src/test_strncpy_crlf.c
new file mode 100644
index 0000000..f21addc
--- /dev/null
+++ b/src/test_strncpy_crlf.c
@@ -0,0 +1,59 @@
+/*
+** Copyright (C) 2010-2012 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "common.h"
+
+#include "test_main.h"
+
+void
+test_psf_strlcpy_crlf (void)
+{	const char *src = "a\nb\nc\n" ;
+	char *dest ;
+	int dest_len ;
+
+	print_test_name ("Testing psf_strlcpy_crlf") ;
+
+	for (dest_len = 3 ; dest_len < 30 ; dest_len++)
+	{	dest = calloc (1, dest_len + 1) ;
+		if (dest == NULL)
+		{	printf ("\n\nLine %d: calloc failed!\n\n", __LINE__) ;
+			exit (1) ;
+			} ;
+
+		/* This value needs to be a in the [0, 127] range to avoid tripping up
+		** compiles like Sun Studio 12.*
+		*/
+		dest [dest_len] = '\x5a' ;
+
+		psf_strlcpy_crlf (dest, src, dest_len, sizeof (*src)) ;
+
+		if (dest [dest_len] != '\x5a')
+		{	printf ("\n\nLine %d: buffer overrun for dest_len == %d\n\n", __LINE__, dest_len) ;
+			exit (1) ;
+			} ;
+
+		free (dest) ;
+		} ;
+
+	puts ("ok") ;
+} /* test_psf_strlcpy_crlf */
diff --git a/src/txw.c b/src/txw.c
new file mode 100644
index 0000000..16525df
--- /dev/null
+++ b/src/txw.c
@@ -0,0 +1,377 @@
+/*
+** Copyright (C) 2002-2012 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*===========================================================================
+** Yamaha TX16 Sampler Files.
+**
+** This header parser was written using information from the SoX source code
+** and trial and error experimentation. The code here however is all original.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<fcntl.h>
+#include	<string.h>
+#include	<ctype.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+#if (ENABLE_EXPERIMENTAL_CODE == 0)
+
+int
+txw_open	(SF_PRIVATE *psf)
+{	if (psf)
+		return SFE_UNIMPLEMENTED ;
+	return 0 ;
+} /* txw_open */
+
+#else
+
+/*------------------------------------------------------------------------------
+** Markers.
+*/
+
+#define TXW_DATA_OFFSET		32
+
+#define	TXW_LOOPED			0x49
+#define	TXW_NO_LOOP			0xC9
+
+/*------------------------------------------------------------------------------
+** Private static functions.
+*/
+
+static int txw_read_header (SF_PRIVATE *psf) ;
+
+static sf_count_t txw_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
+static sf_count_t txw_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
+static sf_count_t txw_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
+static sf_count_t txw_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
+
+static sf_count_t txw_seek (SF_PRIVATE *psf, int mode, sf_count_t offset) ;
+
+/*------------------------------------------------------------------------------
+** Public functions.
+*/
+
+/*
+ * ftp://ftp.t0.or.at/pub/sound/tx16w/samples.yamaha
+ * ftp://ftp.t0.or.at/pub/sound/tx16w/faq/tx16w.tec
+ * http://www.t0.or.at/~mpakesch/tx16w/
+ *
+ * from tx16w.c sox 12.15: (7-Oct-98) (Mark Lakata and Leigh Smith)
+ *  char filetype[6] "LM8953"
+ *  nulls[10],
+ *  dummy_aeg[6]
+ *  format 0x49 = looped, 0xC9 = non-looped
+ *  sample_rate 1 = 33 kHz, 2 = 50 kHz, 3 = 16 kHz
+ *  atc_length[3] if sample rate 0, [2]&0xfe = 6: 33kHz, 0x10:50, 0xf6: 16,
+ *					depending on [5] but to heck with it
+ *  rpt_length[3] (these are for looped samples, attack and loop lengths)
+ *  unused[2]
+ */
+
+typedef struct
+{	unsigned char	format, srate, sr2, sr3 ;
+	unsigned short	srhash ;
+	unsigned int	attacklen, repeatlen ;
+} TXW_HEADER ;
+
+#define	ERROR_666	666
+
+int
+txw_open	(SF_PRIVATE *psf)
+{	int error ;
+
+	if (psf->file.mode != SFM_READ)
+		return SFE_UNIMPLEMENTED ;
+
+	if ((error = txw_read_header (psf)))
+			return error ;
+
+ 	if (psf_fseek (psf, psf->dataoffset, SEEK_SET) != psf->dataoffset)
+		return SFE_BAD_SEEK ;
+
+	psf->read_short		= txw_read_s ;
+	psf->read_int		= txw_read_i ;
+	psf->read_float		= txw_read_f ;
+	psf->read_double	= txw_read_d ;
+
+	psf->seek = txw_seek ;
+
+	return 0 ;
+} /* txw_open */
+
+/*------------------------------------------------------------------------------
+*/
+
+static int
+txw_read_header	(SF_PRIVATE *psf)
+{	BUF_UNION	ubuf ;
+	TXW_HEADER txwh ;
+	const char	*strptr ;
+
+	memset (&txwh, 0, sizeof (txwh)) ;
+	memset (ubuf.cbuf, 0, sizeof (ubuf.cbuf)) ;
+	psf_binheader_readf (psf, "pb", 0, ubuf.cbuf, 16) ;
+
+	if (memcmp (ubuf.cbuf, "LM8953\0\0\0\0\0\0\0\0\0\0", 16) != 0)
+		return ERROR_666 ;
+
+	psf_log_printf (psf, "Read only : Yamaha TX-16 Sampler (.txw)\nLM8953\n") ;
+
+	/* Jump 6 bytes (dummp_aeg), read format, read sample rate. */
+	psf_binheader_readf (psf, "j11", 6, &txwh.format, &txwh.srate) ;
+
+	/* 8 bytes (atc_length[3], rpt_length[3], unused[2]). */
+	psf_binheader_readf (psf, "e33j", &txwh.attacklen, &txwh.repeatlen, 2) ;
+	txwh.sr2 = (txwh.attacklen >> 16) & 0xFE ;
+	txwh.sr3 = (txwh.repeatlen >> 16) & 0xFE ;
+	txwh.attacklen &= 0x1FFFF ;
+	txwh.repeatlen &= 0x1FFFF ;
+
+	switch (txwh.format)
+	{	case TXW_LOOPED :
+				strptr = "looped" ;
+				break ;
+
+		case TXW_NO_LOOP :
+				strptr = "non-looped" ;
+				break ;
+
+		default :
+				psf_log_printf (psf, " Format      : 0x%02x => ?????\n", txwh.format) ;
+				return ERROR_666 ;
+		} ;
+
+	psf_log_printf (psf, " Format      : 0x%02X => %s\n", txwh.format, strptr) ;
+
+	strptr = NULL ;
+
+	switch (txwh.srate)
+	{	case 1 :
+				psf->sf.samplerate = 33333 ;
+				break ;
+
+		case 2 :
+				psf->sf.samplerate = 50000 ;
+				break ;
+
+		case 3 :
+				psf->sf.samplerate = 16667 ;
+				break ;
+
+		default :
+			/* This is ugly and braindead. */
+			txwh.srhash = ((txwh.sr2 & 0xFE) << 8) | (txwh.sr3 & 0xFE) ;
+			switch (txwh.srhash)
+			{	case ((0x6 << 8) | 0x52) :
+						psf->sf.samplerate = 33333 ;
+						break ;
+
+				case ((0x10 << 8) | 0x52) :
+						psf->sf.samplerate = 50000 ;
+						break ;
+
+				case ((0xF6 << 8) | 0x52) :
+						psf->sf.samplerate = 166667 ;
+						break ;
+
+				default :
+						strptr = " Sample Rate : Unknown : forcing to 33333\n" ;
+						psf->sf.samplerate = 33333 ;
+						break ;
+				} ;
+		} ;
+
+
+	if (strptr)
+		psf_log_printf (psf, strptr) ;
+	else if (txwh.srhash)
+		psf_log_printf (psf, " Sample Rate : %d (0x%X) => %d\n", txwh.srate, txwh.srhash, psf->sf.samplerate) ;
+	else
+		psf_log_printf (psf, " Sample Rate : %d => %d\n", txwh.srate, psf->sf.samplerate) ;
+
+	if (txwh.format == TXW_LOOPED)
+	{	psf_log_printf (psf, " Attack Len  : %d\n", txwh.attacklen) ;
+		psf_log_printf (psf, " Repeat Len  : %d\n", txwh.repeatlen) ;
+		} ;
+
+	psf->dataoffset = TXW_DATA_OFFSET ;
+	psf->datalength = psf->filelength - TXW_DATA_OFFSET ;
+	psf->sf.frames	= 2 * psf->datalength / 3 ;
+
+
+	if (psf->datalength % 3 == 1)
+		psf_log_printf (psf, "*** File seems to be truncated, %d extra bytes.\n",
+			(int) (psf->datalength % 3)) ;
+
+	if (txwh.attacklen + txwh.repeatlen > psf->sf.frames)
+		psf_log_printf (psf, "*** File has been truncated.\n") ;
+
+	psf->sf.format = SF_FORMAT_TXW | SF_FORMAT_PCM_16 ;
+	psf->sf.channels = 1 ;
+	psf->sf.sections = 1 ;
+	psf->sf.seekable = SF_TRUE ;
+
+	return 0 ;
+} /* txw_read_header */
+
+static sf_count_t
+txw_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len)
+{	BUF_UNION		ubuf ;
+	unsigned char	*ucptr ;
+	short			sample ;
+	int				k, bufferlen, readcount, count ;
+	sf_count_t		total = 0 ;
+
+	bufferlen = sizeof (ubuf.cbuf) / 3 ;
+	bufferlen -= (bufferlen & 1) ;
+	while (len > 0)
+	{	readcount = (len >= bufferlen) ? bufferlen : len ;
+		count = psf_fread (ubuf.cbuf, 3, readcount, psf) ;
+
+		ucptr = ubuf.ucbuf ;
+		for (k = 0 ; k < readcount ; k += 2)
+		{	sample = (ucptr [0] << 8) | (ucptr [1] & 0xF0) ;
+			ptr [total + k] = sample ;
+			sample = (ucptr [2] << 8) | ((ucptr [1] & 0xF) << 4) ;
+			ptr [total + k + 1] = sample ;
+			ucptr += 3 ;
+			} ;
+
+		total += count ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* txw_read_s */
+
+static sf_count_t
+txw_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len)
+{	BUF_UNION		ubuf ;
+	unsigned char	*ucptr ;
+	short			sample ;
+	int				k, bufferlen, readcount, count ;
+	sf_count_t		total = 0 ;
+
+	bufferlen = sizeof (ubuf.cbuf) / 3 ;
+	bufferlen -= (bufferlen & 1) ;
+	while (len > 0)
+	{	readcount = (len >= bufferlen) ? bufferlen : len ;
+		count = psf_fread (ubuf.cbuf, 3, readcount, psf) ;
+
+		ucptr = ubuf.ucbuf ;
+		for (k = 0 ; k < readcount ; k += 2)
+		{	sample = (ucptr [0] << 8) | (ucptr [1] & 0xF0) ;
+			ptr [total + k] = sample << 16 ;
+			sample = (ucptr [2] << 8) | ((ucptr [1] & 0xF) << 4) ;
+			ptr [total + k + 1] = sample << 16 ;
+			ucptr += 3 ;
+			} ;
+
+		total += count ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* txw_read_i */
+
+static sf_count_t
+txw_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len)
+{	BUF_UNION		ubuf ;
+	unsigned char	*ucptr ;
+	short			sample ;
+	int				k, bufferlen, readcount, count ;
+	sf_count_t		total = 0 ;
+	float			normfact ;
+
+	if (psf->norm_float == SF_TRUE)
+		normfact = 1.0 / 0x8000 ;
+	else
+		normfact = 1.0 / 0x10 ;
+
+	bufferlen = sizeof (ubuf.cbuf) / 3 ;
+	bufferlen -= (bufferlen & 1) ;
+	while (len > 0)
+	{	readcount = (len >= bufferlen) ? bufferlen : len ;
+		count = psf_fread (ubuf.cbuf, 3, readcount, psf) ;
+
+		ucptr = ubuf.ucbuf ;
+		for (k = 0 ; k < readcount ; k += 2)
+		{	sample = (ucptr [0] << 8) | (ucptr [1] & 0xF0) ;
+			ptr [total + k] = normfact * sample ;
+			sample = (ucptr [2] << 8) | ((ucptr [1] & 0xF) << 4) ;
+			ptr [total + k + 1] = normfact * sample ;
+			ucptr += 3 ;
+			} ;
+
+		total += count ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* txw_read_f */
+
+static sf_count_t
+txw_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len)
+{	BUF_UNION		ubuf ;
+	unsigned char	*ucptr ;
+	short			sample ;
+	int				k, bufferlen, readcount, count ;
+	sf_count_t		total = 0 ;
+	double			normfact ;
+
+	if (psf->norm_double == SF_TRUE)
+		normfact = 1.0 / 0x8000 ;
+	else
+		normfact = 1.0 / 0x10 ;
+
+	bufferlen = sizeof (ubuf.cbuf) / 3 ;
+	bufferlen -= (bufferlen & 1) ;
+	while (len > 0)
+	{	readcount = (len >= bufferlen) ? bufferlen : len ;
+		count = psf_fread (ubuf.cbuf, 3, readcount, psf) ;
+
+		ucptr = ubuf.ucbuf ;
+		for (k = 0 ; k < readcount ; k += 2)
+		{	sample = (ucptr [0] << 8) | (ucptr [1] & 0xF0) ;
+			ptr [total + k] = normfact * sample ;
+			sample = (ucptr [2] << 8) | ((ucptr [1] & 0xF) << 4) ;
+			ptr [total + k + 1] = normfact * sample ;
+			ucptr += 3 ;
+			} ;
+
+		total += count ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* txw_read_d */
+
+static sf_count_t
+txw_seek (SF_PRIVATE *psf, int mode, sf_count_t offset)
+{	if (psf && mode)
+		return offset ;
+
+	return 0 ;
+} /* txw_seek */
+
+#endif
diff --git a/src/ulaw.c b/src/ulaw.c
new file mode 100644
index 0000000..ed2f8ff
--- /dev/null
+++ b/src/ulaw.c
@@ -0,0 +1,1056 @@
+/*
+** Copyright (C) 1999-2013 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<math.h>
+#include	<limits.h>
+
+#include	"sndfile.h"
+#include	"common.h"
+
+static sf_count_t ulaw_read_ulaw2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
+static sf_count_t ulaw_read_ulaw2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
+static sf_count_t ulaw_read_ulaw2f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
+static sf_count_t ulaw_read_ulaw2d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
+
+static sf_count_t ulaw_write_s2ulaw (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
+static sf_count_t ulaw_write_i2ulaw (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
+static sf_count_t ulaw_write_f2ulaw (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
+static sf_count_t ulaw_write_d2ulaw (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
+
+int
+ulaw_init (SF_PRIVATE *psf)
+{
+	if (psf->file.mode == SFM_READ || psf->file.mode == SFM_RDWR)
+	{	psf->read_short		= ulaw_read_ulaw2s ;
+		psf->read_int		= ulaw_read_ulaw2i ;
+		psf->read_float		= ulaw_read_ulaw2f ;
+		psf->read_double	= ulaw_read_ulaw2d ;
+		} ;
+
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	psf->write_short	= ulaw_write_s2ulaw ;
+		psf->write_int		= ulaw_write_i2ulaw ;
+		psf->write_float	= ulaw_write_f2ulaw ;
+		psf->write_double	= ulaw_write_d2ulaw ;
+		} ;
+
+	psf->bytewidth = 1 ;
+	psf->blockwidth = psf->sf.channels ;
+
+	if (psf->filelength > psf->dataoffset)
+		psf->datalength = (psf->dataend) ? psf->dataend - psf->dataoffset :
+							psf->filelength - psf->dataoffset ;
+	else
+		psf->datalength = 0 ;
+
+	psf->sf.frames = psf->blockwidth > 0 ? psf->datalength / psf->blockwidth : 0 ;
+
+	return 0 ;
+} /* ulaw_init */
+
+/*==============================================================================
+*/
+
+static short ulaw_decode [256] =
+{	-32124,	-31100,	-30076,	-29052,	-28028,	-27004,	-25980,	-24956,
+	-23932,	-22908,	-21884,	-20860,	-19836,	-18812,	-17788,	-16764,
+	-15996,	-15484,	-14972,	-14460,	-13948,	-13436,	-12924,	-12412,
+	-11900,	-11388,	-10876,	-10364,	-9852,	-9340,	-8828,	-8316,
+	-7932,	-7676,	-7420,	-7164,	-6908,	-6652,	-6396,	-6140,
+	-5884,	-5628,	-5372,	-5116,	-4860,	-4604,	-4348,	-4092,
+	-3900,	-3772,	-3644,	-3516,	-3388,	-3260,	-3132,	-3004,
+	-2876,	-2748,	-2620,	-2492,	-2364,	-2236,	-2108,	-1980,
+	-1884,	-1820,	-1756,	-1692,	-1628,	-1564,	-1500,	-1436,
+	-1372,	-1308,	-1244,	-1180,	-1116,	-1052,	-988,	-924,
+	-876,	-844,	-812,	-780,	-748,	-716,	-684,	-652,
+	-620,	-588,	-556,	-524,	-492,	-460,	-428,	-396,
+	-372,	-356,	-340,	-324,	-308,	-292,	-276,	-260,
+	-244,	-228,	-212,	-196,	-180,	-164,	-148,	-132,
+	-120,	-112,	-104,	-96,	-88,	-80,	-72,	-64,
+	-56,	-48,	-40,	-32,	-24,	-16,	-8,		0,
+
+	32124,	31100,	30076,	29052,	28028,	27004,	25980,	24956,
+	23932,	22908,	21884,	20860,	19836,	18812,	17788,	16764,
+	15996,	15484,	14972,	14460,	13948,	13436,	12924,	12412,
+	11900,	11388,	10876,	10364,	9852,	9340,	8828,	8316,
+	7932,	7676,	7420,	7164,	6908,	6652,	6396,	6140,
+	5884,	5628,	5372,	5116,	4860,	4604,	4348,	4092,
+	3900,	3772,	3644,	3516,	3388,	3260,	3132,	3004,
+	2876,	2748,	2620,	2492,	2364,	2236,	2108,	1980,
+	1884,	1820,	1756,	1692,	1628,	1564,	1500,	1436,
+	1372,	1308,	1244,	1180,	1116,	1052,	988,	924,
+	876,	844,	812,	780,	748,	716,	684,	652,
+	620,	588,	556,	524,	492,	460,	428,	396,
+	372,	356,	340,	324,	308,	292,	276,	260,
+	244,	228,	212,	196,	180,	164,	148,	132,
+	120,	112,	104,	96,		88,		80,		72,		64,
+	56,		48,		40,		32,		24,		16,		8,		0
+} ;
+
+static
+unsigned char ulaw_encode [8193] =
+{	0xff, 0xfe, 0xfe, 0xfd, 0xfd, 0xfc, 0xfc, 0xfb, 0xfb, 0xfa, 0xfa, 0xf9,
+	0xf9, 0xf8, 0xf8, 0xf7, 0xf7, 0xf6, 0xf6, 0xf5, 0xf5, 0xf4, 0xf4, 0xf3,
+	0xf3, 0xf2, 0xf2, 0xf1, 0xf1, 0xf0, 0xf0, 0xef, 0xef, 0xef, 0xef, 0xee,
+	0xee, 0xee, 0xee, 0xed, 0xed, 0xed, 0xed, 0xec, 0xec, 0xec, 0xec, 0xeb,
+	0xeb, 0xeb, 0xeb, 0xea, 0xea, 0xea, 0xea, 0xe9, 0xe9, 0xe9, 0xe9, 0xe8,
+	0xe8, 0xe8, 0xe8, 0xe7, 0xe7, 0xe7, 0xe7, 0xe6, 0xe6, 0xe6, 0xe6, 0xe5,
+	0xe5, 0xe5, 0xe5, 0xe4, 0xe4, 0xe4, 0xe4, 0xe3, 0xe3, 0xe3, 0xe3, 0xe2,
+	0xe2, 0xe2, 0xe2, 0xe1, 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, 0xdf,
+	0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xde, 0xde, 0xde, 0xde, 0xde,
+	0xde, 0xde, 0xde, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdc,
+	0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb,
+	0xdb, 0xdb, 0xdb, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xd9,
+	0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8,
+	0xd8, 0xd8, 0xd8, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd6,
+	0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5,
+	0xd5, 0xd5, 0xd5, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd3,
+	0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2,
+	0xd2, 0xd2, 0xd2, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd0,
+	0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf,
+	0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xce,
+	0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce,
+	0xce, 0xce, 0xce, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+	0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+	0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcb,
+	0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb,
+	0xcb, 0xcb, 0xcb, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca,
+	0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9,
+	0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc8,
+	0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8,
+	0xc8, 0xc8, 0xc8, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7,
+	0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+	0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc5,
+	0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5,
+	0xc5, 0xc5, 0xc5, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4,
+	0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3,
+	0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc2,
+	0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2,
+	0xc2, 0xc2, 0xc2, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1,
+	0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+	0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xbf,
+	0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+	0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+	0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+	0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+	0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+	0xbe, 0xbe, 0xbe, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+	0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+	0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbc,
+	0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+	0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+	0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+	0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+	0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+	0xbb, 0xbb, 0xbb, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+	0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+	0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb9,
+	0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+	0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+	0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+	0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+	0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+	0xb8, 0xb8, 0xb8, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+	0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+	0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb6,
+	0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+	0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+	0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+	0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+	0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+	0xb5, 0xb5, 0xb5, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+	0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+	0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb3,
+	0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+	0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+	0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+	0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+	0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+	0xb2, 0xb2, 0xb2, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+	0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+	0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb0,
+	0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+	0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+	0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+	0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+	0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+	0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+	0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+	0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xae,
+	0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+	0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+	0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+	0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+	0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+	0xae, 0xae, 0xae, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+	0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+	0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+	0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+	0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+	0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xac, 0xac, 0xac, 0xac, 0xac,
+	0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+	0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+	0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+	0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+	0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xab,
+	0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+	0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+	0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+	0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+	0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+	0xab, 0xab, 0xab, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+	0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+	0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+	0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+	0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+	0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+	0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+	0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+	0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+	0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+	0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa8,
+	0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+	0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+	0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+	0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+	0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+	0xa8, 0xa8, 0xa8, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+	0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+	0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+	0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+	0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+	0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+	0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+	0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+	0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+	0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+	0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa5,
+	0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+	0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+	0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+	0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+	0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+	0xa5, 0xa5, 0xa5, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+	0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+	0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+	0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+	0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+	0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+	0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+	0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+	0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+	0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+	0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2,
+	0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+	0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+	0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+	0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+	0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+	0xa2, 0xa2, 0xa2, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+	0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+	0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+	0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+	0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+	0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+	0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+	0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+	0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+	0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+	0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0x9f,
+	0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+	0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+	0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+	0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+	0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+	0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+	0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+	0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+	0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+	0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+	0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+	0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+	0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+	0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+	0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+	0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+	0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+	0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+	0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+	0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+	0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+	0x9e, 0x9e, 0x9e, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+	0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+	0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+	0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+	0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+	0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+	0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+	0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+	0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+	0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+	0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9c,
+	0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+	0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+	0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+	0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+	0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+	0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+	0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+	0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+	0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+	0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+	0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+	0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+	0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+	0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+	0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+	0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+	0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+	0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+	0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+	0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+	0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+	0x9b, 0x9b, 0x9b, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+	0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+	0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+	0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+	0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+	0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+	0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+	0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+	0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+	0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+	0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x99,
+	0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+	0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+	0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+	0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+	0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+	0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+	0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+	0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+	0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+	0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+	0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x98, 0x98, 0x98, 0x98, 0x98,
+	0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+	0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+	0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+	0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+	0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+	0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+	0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+	0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+	0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+	0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+	0x98, 0x98, 0x98, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+	0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+	0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+	0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+	0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+	0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+	0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+	0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+	0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+	0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+	0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x96,
+	0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+	0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+	0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+	0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+	0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+	0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+	0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+	0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+	0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+	0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+	0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x95, 0x95, 0x95, 0x95, 0x95,
+	0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+	0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+	0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+	0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+	0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+	0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+	0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+	0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+	0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+	0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+	0x95, 0x95, 0x95, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+	0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+	0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+	0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+	0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+	0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+	0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+	0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+	0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+	0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+	0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x93,
+	0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+	0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+	0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+	0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+	0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+	0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+	0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+	0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+	0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+	0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+	0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x92, 0x92, 0x92, 0x92, 0x92,
+	0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+	0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+	0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+	0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+	0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+	0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+	0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+	0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+	0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+	0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+	0x92, 0x92, 0x92, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+	0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+	0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+	0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+	0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+	0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+	0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+	0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+	0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+	0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+	0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x90,
+	0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+	0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+	0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+	0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+	0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+	0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+	0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+	0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+	0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+	0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+	0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+	0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+	0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+	0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+	0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+	0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+	0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+	0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+	0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+	0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+	0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+	0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+	0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+	0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+	0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+	0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+	0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+	0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+	0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+	0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+	0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+	0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8e,
+	0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+	0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+	0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+	0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+	0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+	0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+	0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+	0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+	0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+	0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+	0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+	0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+	0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+	0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+	0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+	0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+	0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+	0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+	0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+	0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+	0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+	0x8e, 0x8e, 0x8e, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+	0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+	0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+	0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+	0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+	0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+	0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+	0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+	0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+	0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+	0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+	0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+	0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+	0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+	0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+	0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+	0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+	0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+	0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+	0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+	0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+	0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+	0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+	0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+	0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+	0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+	0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+	0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+	0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+	0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+	0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+	0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+	0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+	0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+	0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+	0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+	0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+	0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+	0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+	0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+	0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+	0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+	0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8b,
+	0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+	0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+	0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+	0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+	0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+	0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+	0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+	0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+	0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+	0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+	0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+	0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+	0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+	0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+	0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+	0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+	0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+	0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+	0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+	0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+	0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+	0x8b, 0x8b, 0x8b, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+	0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+	0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+	0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+	0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+	0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+	0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+	0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+	0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+	0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+	0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+	0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+	0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+	0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+	0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+	0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+	0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+	0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+	0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+	0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+	0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+	0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x89, 0x89, 0x89, 0x89, 0x89,
+	0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+	0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+	0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+	0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+	0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+	0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+	0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+	0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+	0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+	0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+	0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+	0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+	0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+	0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+	0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+	0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+	0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+	0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+	0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+	0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+	0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x88,
+	0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+	0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+	0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+	0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+	0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+	0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+	0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+	0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+	0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+	0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+	0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+	0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+	0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+	0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+	0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+	0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+	0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+	0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+	0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+	0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+	0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+	0x88, 0x88, 0x88, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+	0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+	0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+	0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+	0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+	0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+	0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+	0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+	0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+	0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+	0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+	0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+	0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+	0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+	0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+	0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+	0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+	0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+	0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+	0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+	0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+	0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x86, 0x86, 0x86, 0x86, 0x86,
+	0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+	0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+	0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+	0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+	0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+	0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+	0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+	0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+	0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+	0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+	0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+	0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+	0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+	0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+	0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+	0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+	0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+	0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+	0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+	0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+	0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x85,
+	0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+	0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+	0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+	0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+	0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+	0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+	0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+	0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+	0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+	0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+	0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+	0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+	0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+	0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+	0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+	0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+	0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+	0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+	0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+	0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+	0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+	0x85, 0x85, 0x85, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+	0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+	0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+	0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+	0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+	0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+	0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+	0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+	0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+	0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+	0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+	0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+	0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+	0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+	0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+	0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+	0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+	0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+	0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+	0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+	0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+	0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x83, 0x83, 0x83, 0x83, 0x83,
+	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x82,
+	0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+	0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+	0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+	0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+	0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+	0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+	0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+	0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+	0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+	0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+	0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+	0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+	0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+	0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+	0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+	0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+	0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+	0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+	0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+	0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+	0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+	0x82, 0x82, 0x82, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+	0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+	0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+	0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+	0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+	0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+	0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+	0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+	0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+	0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+	0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+	0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+	0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+	0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+	0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+	0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+	0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+	0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+	0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+	0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+	0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+	0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80,
+	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80
+} ;
+
+static inline void
+ulaw2s_array (unsigned char *buffer, int count, short *ptr)
+{	for (int i = 0 ; i < count ; i++)
+		ptr [i] = ulaw_decode [(int) buffer [i]] ;
+} /* ulaw2s_array */
+
+static inline void
+ulaw2i_array (unsigned char *buffer, int count, int *ptr)
+{	for (int i = 0 ; i < count ; i++)
+		ptr [i] = ((uint32_t) ulaw_decode [buffer [i]]) << 16 ;
+} /* ulaw2i_array */
+
+static inline void
+ulaw2f_array (unsigned char *buffer, int count, float *ptr, float normfact)
+{	for (int i = 0 ; i < count ; i++)
+		ptr [i] = normfact * ulaw_decode [(int) buffer [i]] ;
+} /* ulaw2f_array */
+
+static inline void
+ulaw2d_array (const unsigned char *buffer, int count, double *ptr, double normfact)
+{	for (int i = 0 ; i < count ; i++)
+		ptr [i] = normfact * ulaw_decode [(int) buffer [i]] ;
+} /* ulaw2d_array */
+
+static inline void
+s2ulaw_array (const short *ptr, int count, unsigned char *buffer)
+{	for (int i = 0 ; i < count ; i++)
+	{	if (ptr [i] >= 0)
+			buffer [i] = ulaw_encode [ptr [i] / 4] ;
+		else
+			buffer [i] = 0x7F & ulaw_encode [ptr [i] / -4] ;
+		} ;
+} /* s2ulaw_array */
+
+static inline void
+i2ulaw_array (const int *ptr, int count, unsigned char *buffer)
+{	for (int i = 0 ; i < count ; i++)
+	{	if (ptr [i] == INT_MIN)
+			buffer [i] = ulaw_encode [INT_MAX >> (16 + 2)] ;
+		else if (ptr [i] >= 0)
+			buffer [i] = ulaw_encode [ptr [i] >> (16 + 2)] ;
+		else
+			buffer [i] = 0x7F & ulaw_encode [-ptr [i] >> (16 + 2)] ;
+		} ;
+} /* i2ulaw_array */
+
+static inline void
+f2ulaw_array (const float *ptr, int count, unsigned char *buffer, float normfact)
+{	for (int i = 0 ; i < count ; i++)
+	{	if (ptr [i] >= 0)
+			buffer [i] = ulaw_encode [psf_lrintf (normfact * ptr [i])] ;
+		else
+			buffer [i] = 0x7F & ulaw_encode [- psf_lrintf (normfact * ptr [i])] ;
+		} ;
+} /* f2ulaw_array */
+
+static inline void
+d2ulaw_array (const double *ptr, int count, unsigned char *buffer, double normfact)
+{	for (int i = 0 ; i < count ; i++)
+	{	if (!isfinite (ptr [i]))
+			buffer [i] = 0 ;
+		else if (ptr [i] >= 0)
+			buffer [i] = ulaw_encode [psf_lrint (normfact * ptr [i])] ;
+		else
+			buffer [i] = 0x7F & ulaw_encode [- psf_lrint (normfact * ptr [i])] ;
+		} ;
+} /* d2ulaw_array */
+
+/*==============================================================================
+*/
+
+static sf_count_t
+ulaw_read_ulaw2s (SF_PRIVATE *psf, short *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ucbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.ucbuf, 1, bufferlen, psf) ;
+		ulaw2s_array (ubuf.ucbuf, readcount, ptr + total) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* ulaw_read_ulaw2s */
+
+static sf_count_t
+ulaw_read_ulaw2i (SF_PRIVATE *psf, int *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ucbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.ucbuf, 1, bufferlen, psf) ;
+		ulaw2i_array (ubuf.ucbuf, readcount, ptr + total) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* ulaw_read_ulaw2i */
+
+static sf_count_t
+ulaw_read_ulaw2f (SF_PRIVATE *psf, float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+	float	normfact ;
+
+	normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x8000) : 1.0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ucbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.ucbuf, 1, bufferlen, psf) ;
+		ulaw2f_array (ubuf.ucbuf, readcount, ptr + total, normfact) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* ulaw_read_ulaw2f */
+
+static sf_count_t
+ulaw_read_ulaw2d (SF_PRIVATE *psf, double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+	double	normfact ;
+
+	normfact = (psf->norm_double) ? 1.0 / ((double) 0x8000) : 1.0 ;
+	bufferlen = ARRAY_LEN (ubuf.ucbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.ucbuf, 1, bufferlen, psf) ;
+		ulaw2d_array (ubuf.ucbuf, readcount, ptr + total, normfact) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* ulaw_read_ulaw2d */
+
+/*=============================================================================================
+*/
+
+static sf_count_t
+ulaw_write_s2ulaw	(SF_PRIVATE *psf, const short *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ucbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		s2ulaw_array (ptr + total, bufferlen, ubuf.ucbuf) ;
+		writecount = (int) psf_fwrite (ubuf.ucbuf, 1, bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* ulaw_write_s2ulaw */
+
+static sf_count_t
+ulaw_write_i2ulaw	(SF_PRIVATE *psf, const int *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ucbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		i2ulaw_array (ptr + total, bufferlen, ubuf.ucbuf) ;
+		writecount = (int) psf_fwrite (ubuf.ucbuf, 1, bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* ulaw_write_i2ulaw */
+
+static sf_count_t
+ulaw_write_f2ulaw	(SF_PRIVATE *psf, const float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+	float	normfact ;
+
+	/* Factor in a divide by 4. */
+	normfact = (psf->norm_float == SF_TRUE) ? (0.25 * 0x7FFF) : 0.25 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ucbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		f2ulaw_array (ptr + total, bufferlen, ubuf.ucbuf, normfact) ;
+		writecount = (int) psf_fwrite (ubuf.ucbuf, 1, bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* ulaw_write_f2ulaw */
+
+static sf_count_t
+ulaw_write_d2ulaw	(SF_PRIVATE *psf, const double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+	double	normfact ;
+
+	/* Factor in a divide by 4. */
+	normfact = (psf->norm_double) ? (0.25 * 0x7FFF) : 0.25 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ucbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		d2ulaw_array (ptr + total, bufferlen, ubuf.ucbuf, normfact) ;
+		writecount = (int) psf_fwrite (ubuf.ucbuf, 1, bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* ulaw_write_d2ulaw */
+
diff --git a/src/version-metadata.rc.in b/src/version-metadata.rc.in
new file mode 100644
index 0000000..a672e4b
--- /dev/null
+++ b/src/version-metadata.rc.in
@@ -0,0 +1,31 @@
+#include <windows.h>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS
+1 VERSIONINFO
+	FILEVERSION		@WIN_RC_VERSION@,0
+	PRODUCTVERSION	@WIN_RC_VERSION@,0
+	FILEOS			VOS__WINDOWS32
+	FILETYPE		VFT_DLL
+	FILESUBTYPE		VFT2_UNKNOWN
+	FILEFLAGSMASK	0x00000000
+	FILEFLAGS		0x00000000
+{
+	BLOCK "StringFileInfo"
+	{
+		BLOCK "040904e4"
+		{
+			VALUE "FileDescription", "A library for reading and writing audio files."
+			VALUE "FileVersion", "@CLEAN_VERSION@.0\0"
+			VALUE "Full Version", "@PACKAGE_VERSION@"
+			VALUE "InternalName", "libsndfile"
+			VALUE "LegalCopyright", "Copyright (C) 1999-2012, Licensed LGPL"
+			VALUE "ProductName", "libsndfile-1 DLL"
+			VALUE "ProductVersion", "@CLEAN_VERSION@.0\0"
+			VALUE "Language", "Language Neutral"
+		}
+	}
+	BLOCK "VarFileInfo"
+	{
+		VALUE "Translation", 0x0409, 0x04E4
+	}
+}
diff --git a/src/voc.c b/src/voc.c
new file mode 100644
index 0000000..380d3a3
--- /dev/null
+++ b/src/voc.c
@@ -0,0 +1,883 @@
+/*
+** Copyright (C) 2001-2018 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*	RANT:
+**	The VOC file format is the most brain damaged format I have yet had to deal
+**	with. No one programmer could have bee stupid enough to put this together.
+**	Instead it looks like a series of manic, dyslexic assembly language programmers
+**	hacked it to fit their needs.
+**	Utterly woeful.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "sndfile.h"
+#include "sfendian.h"
+#include "common.h"
+
+
+/*------------------------------------------------------------------------------
+ * Typedefs for file chunks.
+*/
+
+#define	VOC_MAX_SECTIONS	200
+
+enum
+{	VOC_TERMINATOR		= 0,
+	VOC_SOUND_DATA		= 1,
+	VOC_SOUND_CONTINUE	= 2,
+	VOC_SILENCE			= 3,
+	VOC_MARKER			= 4,
+	VOC_ASCII			= 5,
+	VOC_REPEAT			= 6,
+	VOC_END_REPEAT		= 7,
+	VOC_EXTENDED		= 8,
+	VOC_EXTENDED_II		= 9
+} ;
+
+typedef struct
+{	int 	samples ;
+	int		offset ;	/* Offset of zero => silence. */
+} SND_DATA_BLOCKS ;
+
+typedef struct
+{	unsigned int 	sections, section_types ;
+	int				samplerate, channels, bitwidth ;
+	SND_DATA_BLOCKS	blocks [VOC_MAX_SECTIONS] ;
+} VOC_DATA ;
+
+/*------------------------------------------------------------------------------
+ * Private static functions.
+*/
+
+static	int	voc_close	(SF_PRIVATE *psf) ;
+static	int voc_write_header (SF_PRIVATE *psf, int calc_length) ;
+static	int voc_read_header	(SF_PRIVATE *psf) ;
+
+static const char* voc_encoding2str (int encoding) ;
+
+#if 0
+
+/*	These functions would be required for files with more than one VOC_SOUND_DATA
+**	segment. Not sure whether to bother implementing this.
+*/
+
+static int	voc_multi_init (SF_PRIVATE *psf, VOC_DATA *pvoc) ;
+
+static int	voc_multi_read_uc2s		(SF_PRIVATE *psf, short *ptr, int len) ;
+static int	voc_multi_read_les2s	(SF_PRIVATE *psf, short *ptr, int len) ;
+
+static int	voc_multi_read_uc2i		(SF_PRIVATE *psf, int *ptr, int len) ;
+static int	voc_multi_read_les2i	(SF_PRIVATE *psf, int *ptr, int len) ;
+
+static int	voc_multi_read_uc2f		(SF_PRIVATE *psf, float *ptr, int len) ;
+static int	voc_multi_read_les2f	(SF_PRIVATE *psf, float *ptr, int len) ;
+
+static int	voc_multi_read_uc2d		(SF_PRIVATE *psf, double *ptr, int len) ;
+static int	voc_multi_read_les2d	(SF_PRIVATE *psf, double *ptr, int len) ;
+#endif
+
+/*------------------------------------------------------------------------------
+** Public function.
+*/
+
+int
+voc_open	(SF_PRIVATE *psf)
+{	int subformat, error = 0 ;
+
+	if (psf->is_pipe)
+		return SFE_VOC_NO_PIPE ;
+
+	if (psf->file.mode == SFM_READ || (psf->file.mode == SFM_RDWR && psf->filelength > 0))
+	{	if ((error = voc_read_header (psf)))
+			return error ;
+		} ;
+
+	subformat = SF_CODEC (psf->sf.format) ;
+
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	if ((SF_CONTAINER (psf->sf.format)) != SF_FORMAT_VOC)
+			return	SFE_BAD_OPEN_FORMAT ;
+
+		psf->endian = SF_ENDIAN_LITTLE ;
+
+		if ((error = voc_write_header (psf, SF_FALSE)))
+			return error ;
+
+		psf->write_header = voc_write_header ;
+		} ;
+
+	psf->blockwidth = psf->bytewidth * psf->sf.channels ;
+
+	psf->container_close = voc_close ;
+
+	switch (subformat)
+	{	case SF_FORMAT_PCM_U8 :
+		case SF_FORMAT_PCM_16 :
+				error = pcm_init (psf) ;
+				break ;
+
+		case SF_FORMAT_ALAW :
+				error = alaw_init (psf) ;
+				break ;
+
+		case SF_FORMAT_ULAW :
+				error = ulaw_init (psf) ;
+				break ;
+
+		default : return SFE_UNIMPLEMENTED ;
+		} ;
+
+	return error ;
+} /* voc_open */
+
+/*------------------------------------------------------------------------------
+*/
+
+static int
+voc_read_header	(SF_PRIVATE *psf)
+{	VOC_DATA	*pvoc ;
+	char	creative [20] ;
+	unsigned char block_type, rate_byte ;
+	short	version, checksum, encoding, dataoffset ;
+	int		offset ;
+
+	/* Set position to start of file to begin reading header. */
+	offset = psf_binheader_readf (psf, "pb", 0, creative, SIGNED_SIZEOF (creative)) ;
+
+	if (creative [sizeof (creative) - 1] != 0x1A)
+		return SFE_VOC_NO_CREATIVE ;
+
+	/* Terminate the string. */
+	creative [sizeof (creative) - 1] = 0 ;
+
+	if (strcmp ("Creative Voice File", creative))
+		return SFE_VOC_NO_CREATIVE ;
+
+	psf_log_printf (psf, "%s\n", creative) ;
+
+	offset += psf_binheader_readf (psf, "e222", &dataoffset, &version, &checksum) ;
+
+	psf->dataoffset = dataoffset ;
+
+	psf_log_printf (psf, 	"dataoffset : %d\n"
+							"version    : 0x%X\n"
+							"checksum   : 0x%X\n", psf->dataoffset, version, checksum) ;
+
+	if (version != 0x010A && version != 0x0114)
+		return SFE_VOC_BAD_VERSION ;
+
+	if (! (psf->codec_data = malloc (sizeof (VOC_DATA))))
+		return SFE_MALLOC_FAILED ;
+
+	pvoc = (VOC_DATA*) psf->codec_data ;
+
+	memset (pvoc, 0, sizeof (VOC_DATA)) ;
+
+	/* Set the default encoding now. */
+	psf->sf.format = SF_FORMAT_VOC ; /* Major format */
+	encoding = SF_FORMAT_PCM_U8 ; /* Minor format */
+	psf->endian = SF_ENDIAN_LITTLE ;
+
+	while (1)
+	{	char header [256] ;
+		unsigned size ;
+		short count ;
+
+		block_type = 0 ;
+		offset += psf_binheader_readf (psf, "1", &block_type) ;
+
+		switch (block_type)
+		{	case VOC_ASCII :
+					offset += psf_binheader_readf (psf, "e3", &size) ;
+
+					psf_log_printf (psf, " ASCII : %d\n", size) ;
+
+					if (size < sizeof (header) - 1)
+					{	offset += psf_binheader_readf (psf, "b", header, size) ;
+						header [size] = 0 ;
+						psf_log_printf (psf, "  text : %s\n", header) ;
+						continue ;
+						}
+
+					offset += psf_binheader_readf (psf, "j", size) ;
+					continue ;
+
+			case VOC_REPEAT :
+					offset += psf_binheader_readf (psf, "e32", &size, &count) ;
+					psf_log_printf (psf, " Repeat : %d\n", count) ;
+					continue ;
+
+			case VOC_SOUND_DATA :
+			case VOC_EXTENDED :
+			case VOC_EXTENDED_II :
+					break ;
+
+			default : psf_log_printf (psf, "*** Weird block marker (%d)\n", block_type) ;
+			} ;
+
+		break ;
+		} ;
+
+	if (block_type == VOC_SOUND_DATA)
+	{	unsigned char compression ;
+		int 	size ;
+
+		offset += psf_binheader_readf (psf, "e311", &size, &rate_byte, &compression) ;
+
+		psf->sf.samplerate = 1000000 / (256 - (rate_byte & 0xFF)) ;
+
+		psf_log_printf (psf, " Sound Data : %d\n  sr   : %d => %dHz\n  comp : %d\n",
+								size, rate_byte, psf->sf.samplerate, compression) ;
+
+		if (offset + size - 1 > psf->filelength)
+		{	psf_log_printf (psf, "Seems to be a truncated file.\n") ;
+			psf_log_printf (psf, "offset: %d    size: %d    sum: %d    filelength: %D\n", offset, size, offset + size, psf->filelength) ;
+			return SFE_VOC_BAD_SECTIONS ;
+			}
+		else if (psf->filelength - offset - size > 4)
+		{	psf_log_printf (psf, "Seems to be a multi-segment file (#1).\n") ;
+			psf_log_printf (psf, "offset: %d    size: %d    sum: %d    filelength: %D\n", offset, size, offset + size, psf->filelength) ;
+			return SFE_VOC_BAD_SECTIONS ;
+			} ;
+
+		psf->dataoffset = offset ;
+		psf->dataend	= psf->filelength - 1 ;
+
+		psf->sf.channels = 1 ;
+		psf->bytewidth = 1 ;
+
+		psf->sf.format = SF_FORMAT_VOC | SF_FORMAT_PCM_U8 ;
+
+		return 0 ;
+		} ;
+
+	if (block_type == VOC_EXTENDED)
+	{	unsigned char pack, stereo, compression ;
+		unsigned short rate_short ;
+		int		size ;
+
+		offset += psf_binheader_readf (psf, "e3211", &size, &rate_short, &pack, &stereo) ;
+
+		psf_log_printf (psf, " Extended : %d\n", size) ;
+		if (size == 4)
+			psf_log_printf (psf, "  size   : 4\n") ;
+		else
+			psf_log_printf (psf, "  size   : %d (should be 4)\n", size) ;
+
+		psf_log_printf (psf,	"  pack   : %d\n"
+								"  stereo : %s\n", pack, (stereo ? "yes" : "no")) ;
+
+		if (stereo)
+		{	psf->sf.channels = 2 ;
+			psf->sf.samplerate = 128000000 / (65536 - rate_short) ;
+			}
+		else
+		{	psf->sf.channels = 1 ;
+			psf->sf.samplerate = 256000000 / (65536 - rate_short) ;
+			} ;
+
+		psf_log_printf (psf, "  sr     : %d => %dHz\n", (rate_short & 0xFFFF), psf->sf.samplerate) ;
+
+		offset += psf_binheader_readf (psf, "1", &block_type) ;
+
+		if (block_type != VOC_SOUND_DATA)
+		{	psf_log_printf (psf, "*** Expecting VOC_SOUND_DATA section.\n") ;
+			return SFE_VOC_BAD_FORMAT ;
+			} ;
+
+		offset += psf_binheader_readf (psf, "e311", &size, &rate_byte, &compression) ;
+
+		psf_log_printf (psf,	" Sound Data : %d\n"
+								"  sr     : %d\n"
+								"  comp   : %d\n", size, rate_byte, compression) ;
+
+
+		if (offset + size - 1 > psf->filelength)
+		{	psf_log_printf (psf, "Seems to be a truncated file.\n") ;
+			psf_log_printf (psf, "offset: %d    size: %d    sum: %d    filelength: %D\n", offset, size, offset + size, psf->filelength) ;
+			return SFE_VOC_BAD_SECTIONS ;
+			}
+		else if (offset + size - 1 < psf->filelength)
+		{	psf_log_printf (psf, "Seems to be a multi-segment file (#2).\n") ;
+			psf_log_printf (psf, "offset: %d    size: %d    sum: %d    filelength: %D\n", offset, size, offset + size, psf->filelength) ;
+			return SFE_VOC_BAD_SECTIONS ;
+			} ;
+
+		psf->dataoffset = offset ;
+		psf->dataend = psf->filelength - 1 ;
+
+		psf->bytewidth = 1 ;
+
+		psf->sf.format = SF_FORMAT_VOC | SF_FORMAT_PCM_U8 ;
+
+		return 0 ;
+		}
+
+	if (block_type == VOC_EXTENDED_II)
+	{	unsigned char bitwidth, channels ;
+		int size, fourbytes ;
+
+		offset += psf_binheader_readf (psf, "e341124", &size, &psf->sf.samplerate,
+								&bitwidth, &channels, &encoding, &fourbytes) ;
+
+		if (size * 2 == psf->filelength - 39)
+		{	int temp_size = psf->filelength - 31 ;
+
+			psf_log_printf (psf, " Extended II : %d (SoX bug: should be %d)\n", size, temp_size) ;
+			size = temp_size ;
+			}
+		else
+			psf_log_printf (psf, " Extended II : %d\n", size) ;
+
+		psf_log_printf (psf,	"  sample rate : %d\n"
+								"  bit width   : %d\n"
+								"  channels    : %d\n", psf->sf.samplerate, bitwidth, channels) ;
+
+		if (bitwidth == 16 && encoding == 0)
+		{	encoding = 4 ;
+			psf_log_printf (psf, "  encoding    : 0 (SoX bug: should be 4 for 16 bit signed PCM)\n") ;
+			}
+		else
+			psf_log_printf (psf, "  encoding    : %d => %s\n", encoding, voc_encoding2str (encoding)) ;
+
+
+		psf_log_printf (psf, "  fourbytes   : %X\n", fourbytes) ;
+
+		psf->sf.channels = channels ;
+
+		psf->dataoffset = offset ;
+		psf->dataend	= psf->filelength - 1 ;
+
+		if (size + 31 == psf->filelength + 1)
+		{	/* Hack for reading files produced using
+			** sf_command (SFC_UPDATE_HEADER_NOW).
+			*/
+			psf_log_printf (psf, "Missing zero byte at end of file.\n") ;
+			size = psf->filelength - 30 ;
+			psf->dataend = 0 ;
+			}
+		else if (size + 31 > psf->filelength)
+		{	psf_log_printf (psf, "Seems to be a truncated file.\n") ;
+			size = psf->filelength - 31 ;
+			}
+		else if (size + 31 < psf->filelength)
+			psf_log_printf (psf, "Seems to be a multi-segment file (#3).\n") ;
+
+		switch (encoding)
+		{	case 0 :
+					psf->sf.format = SF_FORMAT_VOC | SF_FORMAT_PCM_U8 ;
+					psf->bytewidth = 1 ;
+					break ;
+
+			case 4 :
+					psf->sf.format = SF_FORMAT_VOC | SF_FORMAT_PCM_16 ;
+					psf->bytewidth = 2 ;
+					break ;
+
+			case 6 :
+					psf->sf.format = SF_FORMAT_VOC | SF_FORMAT_ALAW ;
+					psf->bytewidth = 1 ;
+					break ;
+
+			case 7 :
+					psf->sf.format = SF_FORMAT_VOC | SF_FORMAT_ULAW ;
+					psf->bytewidth = 1 ;
+					break ;
+
+			default : /* Unknown */
+					return SFE_VOC_BAD_FORMAT ;
+					break ;
+			} ;
+
+		} ;
+
+	return 0 ;
+} /* voc_read_header */
+
+/*====================================================================================
+*/
+
+static int
+voc_write_header (SF_PRIVATE *psf, int calc_length)
+{	sf_count_t	current ;
+	int			rate_const, subformat ;
+
+	current = psf_ftell (psf) ;
+
+	if (calc_length)
+	{	psf->filelength = psf_get_filelen (psf) ;
+
+		psf->datalength = psf->filelength - psf->dataoffset ;
+		if (psf->dataend)
+			psf->datalength -= psf->filelength - psf->dataend ;
+
+		psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ;
+		} ;
+
+	subformat = SF_CODEC (psf->sf.format) ;
+	/* Reset the current header length to zero. */
+	psf->header.ptr [0] = 0 ;
+	psf->header.indx = 0 ;
+	psf_fseek (psf, 0, SEEK_SET) ;
+
+	/* VOC marker and 0x1A byte. */
+	psf_binheader_writef (psf, "eb1", BHWv ("Creative Voice File"), BHWz (19), BHW1 (0x1A)) ;
+
+	/* Data offset, version and other. */
+	psf_binheader_writef (psf, "e222", BHW2 (26), BHW2 (0x0114), BHW2 (0x111F)) ;
+
+	/*	Use same logic as SOX.
+	**	If the file is mono 8 bit data, use VOC_SOUND_DATA.
+	**	If the file is mono 16 bit data, use VOC_EXTENED.
+	**	Otherwise use VOC_EXTENED_2.
+	*/
+
+	if (subformat == SF_FORMAT_PCM_U8 && psf->sf.channels == 1)
+	{	/* samplerate = 1000000 / (256 - rate_const) ; */
+		rate_const = 256 - 1000000 / psf->sf.samplerate ;
+
+		/* First type marker, length, rate_const and compression */
+		psf_binheader_writef (psf, "e1311", BHW1 (VOC_SOUND_DATA), BHW3 ((int) (psf->datalength + 1)), BHW1 (rate_const), BHW1 (0)) ;
+		}
+	else if (subformat == SF_FORMAT_PCM_U8 && psf->sf.channels == 2)
+	{	/* sample_rate = 128000000 / (65536 - rate_short) ; */
+		rate_const = 65536 - 128000000 / psf->sf.samplerate ;
+
+		/* First write the VOC_EXTENDED section
+		** 		marker, length, rate_const and compression
+		*/
+		psf_binheader_writef (psf, "e13211", BHW1 (VOC_EXTENDED), BHW3 (4), BHW2 (rate_const), BHW1 (0), BHW1 (1)) ;
+
+		/* samplerate = 1000000 / (256 - rate_const) ; */
+		rate_const = 256 - 1000000 / psf->sf.samplerate ;
+
+		/*	Now write the VOC_SOUND_DATA section
+		** 		marker, length, rate_const and compression
+		*/
+		psf_binheader_writef (psf, "e1311", BHW1 (VOC_SOUND_DATA), BHW3 ((int) (psf->datalength + 1)), BHW1 (rate_const), BHW1 (0)) ;
+		}
+	else
+	{	int length ;
+
+		if (psf->sf.channels < 1 || psf->sf.channels > 2)
+			return SFE_CHANNEL_COUNT ;
+
+		switch (subformat)
+		{	case SF_FORMAT_PCM_U8 :
+					psf->bytewidth = 1 ;
+					length = psf->sf.frames * psf->sf.channels * psf->bytewidth + 12 ;
+					/* Marker, length, sample rate, bitwidth, stereo flag, encoding and fourt zero bytes. */
+					psf_binheader_writef (psf, "e1341124", BHW1 (VOC_EXTENDED_II), BHW3 (length), BHW4 (psf->sf.samplerate), BHW1 (16), BHW1 (psf->sf.channels), BHW2 (4), BHW4 (0)) ;
+					break ;
+
+			case SF_FORMAT_PCM_16 :
+					psf->bytewidth = 2 ;
+					length = psf->sf.frames * psf->sf.channels * psf->bytewidth + 12 ;
+					/* Marker, length, sample rate, bitwidth, stereo flag, encoding and fourt zero bytes. */
+					psf_binheader_writef (psf, "e1341124", BHW1 (VOC_EXTENDED_II), BHW3 (length), BHW4 (psf->sf.samplerate), BHW1 (16), BHW1 (psf->sf.channels), BHW2 (4), BHW4 (0)) ;
+					break ;
+
+			case SF_FORMAT_ALAW :
+					psf->bytewidth = 1 ;
+					length = psf->sf.frames * psf->sf.channels * psf->bytewidth + 12 ;
+					psf_binheader_writef (psf, "e1341124", BHW1 (VOC_EXTENDED_II), BHW3 (length), BHW4 (psf->sf.samplerate), BHW1 (8), BHW1 (psf->sf.channels), BHW2 (6), BHW4 (0)) ;
+					break ;
+
+			case SF_FORMAT_ULAW :
+					psf->bytewidth = 1 ;
+					length = psf->sf.frames * psf->sf.channels * psf->bytewidth + 12 ;
+					psf_binheader_writef (psf, "e1341124", BHW1 (VOC_EXTENDED_II), BHW3 (length), BHW4 (psf->sf.samplerate), BHW1 (8), BHW1 (psf->sf.channels), BHW2 (7), BHW4 (0)) ;
+					break ;
+
+			default : return SFE_UNIMPLEMENTED ;
+			} ;
+		} ;
+
+	psf_fwrite (psf->header.ptr, psf->header.indx, 1, psf) ;
+
+	if (psf->error)
+		return psf->error ;
+
+	psf->dataoffset = psf->header.indx ;
+
+	if (current > 0)
+		psf_fseek (psf, current, SEEK_SET) ;
+
+	return psf->error ;
+} /* voc_write_header */
+
+static int
+voc_close	(SF_PRIVATE *psf)
+{
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	/*  Now we know for certain the length of the file we can re-write
+		**	correct values for the FORM, 8SVX and BODY chunks.
+		*/
+		unsigned char byte = VOC_TERMINATOR ;
+
+
+		psf_fseek (psf, 0, SEEK_END) ;
+
+		/* Write terminator */
+		psf_fwrite (&byte, 1, 1, psf) ;
+
+		voc_write_header (psf, SF_TRUE) ;
+		} ;
+
+	return 0 ;
+} /* voc_close */
+
+static const 	char*
+voc_encoding2str (int encoding)
+{
+	switch (encoding)
+	{	case 0 :	return "8 bit unsigned PCM" ;
+		case 4 :	return "16 bit signed PCM" ;
+		case 6 :	return "A-law" ;
+		case 7 :	return "u-law" ;
+		default :	break ;
+		}
+	return "*** Unknown ***" ;
+} /* voc_encoding2str */
+
+/*====================================================================================
+*/
+
+#if 0
+static int
+voc_multi_init (SF_PRIVATE *psf, VOC_DATA *pvoc)
+{
+	psf->sf.frames = 0 ;
+
+	if (pvoc->bitwidth == 8)
+	{	psf->read_short		= voc_multi_read_uc2s ;
+		psf->read_int		= voc_multi_read_uc2i ;
+		psf->read_float		= voc_multi_read_uc2f ;
+		psf->read_double	= voc_multi_read_uc2d ;
+		return 0 ;
+		} ;
+
+	if (pvoc->bitwidth == 16)
+	{	psf->read_short		= voc_multi_read_les2s ;
+		psf->read_int		= voc_multi_read_les2i ;
+		psf->read_float		= voc_multi_read_les2f ;
+		psf->read_double	= voc_multi_read_les2d ;
+		return 0 ;
+		} ;
+
+	psf_log_printf (psf, "Error : bitwith != 8 && bitwidth != 16.\n") ;
+
+	return SFE_UNIMPLEMENTED ;
+} /* voc_multi_read_int */
+
+/*------------------------------------------------------------------------------------
+*/
+
+static int
+voc_multi_read_uc2s (SF_PRIVATE *psf, short *ptr, int len)
+{
+
+	return 0 ;
+} /* voc_multi_read_uc2s */
+
+static int
+voc_multi_read_les2s (SF_PRIVATE *psf, short *ptr, int len)
+{
+
+	return 0 ;
+} /* voc_multi_read_les2s */
+
+
+static int
+voc_multi_read_uc2i (SF_PRIVATE *psf, int *ptr, int len)
+{
+
+	return 0 ;
+} /* voc_multi_read_uc2i */
+
+static int
+voc_multi_read_les2i (SF_PRIVATE *psf, int *ptr, int len)
+{
+
+	return 0 ;
+} /* voc_multi_read_les2i */
+
+
+static int
+voc_multi_read_uc2f (SF_PRIVATE *psf, float *ptr, int len)
+{
+
+	return 0 ;
+} /* voc_multi_read_uc2f */
+
+static int
+voc_multi_read_les2f (SF_PRIVATE *psf, float *ptr, int len)
+{
+
+	return 0 ;
+} /* voc_multi_read_les2f */
+
+
+static int
+voc_multi_read_uc2d (SF_PRIVATE *psf, double *ptr, int len)
+{
+
+	return 0 ;
+} /* voc_multi_read_uc2d */
+
+static int
+voc_multi_read_les2d (SF_PRIVATE *psf, double *ptr, int len)
+{
+
+	return 0 ;
+} /* voc_multi_read_les2d */
+
+#endif
+
+/*------------------------------------------------------------------------------------
+
+Creative Voice (VOC) file format
+--------------------------------
+
+~From: galt@dsd.es.com
+
+(byte numbers are hex!)
+
+    HEADER (bytes 00-19)
+    Series of DATA BLOCKS (bytes 1A+) [Must end w/ Terminator Block]
+
+- ---------------------------------------------------------------
+
+HEADER:
+=======
+     byte #     Description
+     ------     ------------------------------------------
+     00-12      "Creative Voice File"
+     13         1A (eof to abort printing of file)
+     14-15      Offset of first datablock in .voc file (std 1A 00
+                in Intel Notation)
+     16-17      Version number (minor,major) (VOC-HDR puts 0A 01)
+     18-19      1's Comp of Ver. # + 1234h (VOC-HDR puts 29 11)
+
+- ---------------------------------------------------------------
+
+DATA BLOCK:
+===========
+
+   Data Block:  TYPE(1-byte), SIZE(3-bytes), INFO(0+ bytes)
+   NOTE: Terminator Block is an exception -- it has only the TYPE byte.
+
+      TYPE   Description     Size (3-byte int)   Info
+      ----   -----------     -----------------   -----------------------
+      00     Terminator      (NONE)              (NONE)
+      01     Sound data      2+length of data    *
+      02     Sound continue  length of data      Voice Data
+      03     Silence         3                   **
+      04     Marker          2                   Marker# (2 bytes)
+      05     ASCII           length of string    null terminated string
+      06     Repeat          2                   Count# (2 bytes)
+      07     End repeat      0                   (NONE)
+      08     Extended        4                   ***
+
+      *Sound Info Format:
+       ---------------------
+       00   Sample Rate
+       01   Compression Type
+       02+  Voice Data
+
+      **Silence Info Format:
+      ----------------------------
+      00-01  Length of silence - 1
+      02     Sample Rate
+
+
+    ***Extended Info Format:
+       ---------------------
+       00-01  Time Constant: Mono: 65536 - (256000000/sample_rate)
+                             Stereo: 65536 - (25600000/(2*sample_rate))
+       02     Pack
+       03     Mode: 0 = mono
+                    1 = stereo
+
+
+  Marker#           -- Driver keeps the most recent marker in a status byte
+  Count#            -- Number of repetitions + 1
+                         Count# may be 1 to FFFE for 0 - FFFD repetitions
+                         or FFFF for endless repetitions
+  Sample Rate       -- SR byte = 256-(1000000/sample_rate)
+  Length of silence -- in units of sampling cycle
+  Compression Type  -- of voice data
+                         8-bits    = 0
+                         4-bits    = 1
+                         2.6-bits  = 2
+                         2-bits    = 3
+                         Multi DAC = 3+(# of channels) [interesting--
+                                       this isn't in the developer's manual]
+
+
+---------------------------------------------------------------------------------
+Addendum submitted by Votis Kokavessis:
+
+After some experimenting with .VOC files I found out that there is a Data Block
+Type 9, which is not covered in the VOC.TXT file. Here is what I was able to discover
+about this block type:
+
+
+TYPE: 09
+SIZE: 12 + length of data
+INFO: 12 (twelve) bytes
+
+INFO STRUCTURE:
+
+Bytes 0-1: (Word) Sample Rate (e.g. 44100)
+Bytes 2-3: zero (could be that bytes 0-3 are a DWord for Sample Rate)
+Byte 4: Sample Size in bits (e.g. 16)
+Byte 5: Number of channels (e.g. 1 for mono, 2 for stereo)
+Byte 6: Unknown (equal to 4 in all files I examined)
+Bytes 7-11: zero
+
+
+-------------------------------------------------------------------------------------*/
+
+/*=====================================================================================
+**=====================================================================================
+**=====================================================================================
+**=====================================================================================
+*/
+
+/*------------------------------------------------------------------------
+The following is taken from the Audio File Formats FAQ dated 2-Jan-1995
+and submitted by Guido van Rossum <guido@cwi.nl>.
+--------------------------------------------------------------------------
+Creative Voice (VOC) file format
+--------------------------------
+
+From: galt@dsd.es.com
+
+(byte numbers are hex!)
+
+    HEADER (bytes 00-19)
+    Series of DATA BLOCKS (bytes 1A+) [Must end w/ Terminator Block]
+
+- ---------------------------------------------------------------
+
+HEADER:
+-------
+     byte #     Description
+     ------     ------------------------------------------
+     00-12      "Creative Voice File"
+     13         1A (eof to abort printing of file)
+     14-15      Offset of first datablock in .voc file (std 1A 00
+                in Intel Notation)
+     16-17      Version number (minor,major) (VOC-HDR puts 0A 01)
+     18-19      2's Comp of Ver. # + 1234h (VOC-HDR puts 29 11)
+
+- ---------------------------------------------------------------
+
+DATA BLOCK:
+-----------
+
+   Data Block:  TYPE(1-byte), SIZE(3-bytes), INFO(0+ bytes)
+   NOTE: Terminator Block is an exception -- it has only the TYPE byte.
+
+      TYPE   Description     Size (3-byte int)   Info
+      ----   -----------     -----------------   -----------------------
+      00     Terminator      (NONE)              (NONE)
+      01     Sound data      2+length of data    *
+      02     Sound continue  length of data      Voice Data
+      03     Silence         3                   **
+      04     Marker          2                   Marker# (2 bytes)
+      05     ASCII           length of string    null terminated string
+      06     Repeat          2                   Count# (2 bytes)
+      07     End repeat      0                   (NONE)
+      08     Extended        4                   ***
+
+      *Sound Info Format:       **Silence Info Format:
+       ---------------------      ----------------------------
+       00   Sample Rate           00-01  Length of silence - 1
+       01   Compression Type      02     Sample Rate
+       02+  Voice Data
+
+    ***Extended Info Format:
+       ---------------------
+       00-01  Time Constant: Mono: 65536 - (256000000/sample_rate)
+                             Stereo: 65536 - (25600000/(2*sample_rate))
+       02     Pack
+       03     Mode: 0 = mono
+                    1 = stereo
+
+
+  Marker#           -- Driver keeps the most recent marker in a status byte
+  Count#            -- Number of repetitions + 1
+                         Count# may be 1 to FFFE for 0 - FFFD repetitions
+                         or FFFF for endless repetitions
+  Sample Rate       -- SR byte = 256-(1000000/sample_rate)
+  Length of silence -- in units of sampling cycle
+  Compression Type  -- of voice data
+                         8-bits    = 0
+                         4-bits    = 1
+                         2.6-bits  = 2
+                         2-bits    = 3
+                         Multi DAC = 3+(# of channels) [interesting--
+                                       this isn't in the developer's manual]
+
+Detailed description of new data blocks (VOC files version 1.20 and above):
+
+        (Source is fax from Barry Boone at Creative Labs, 405/742-6622)
+
+BLOCK 8 - digitized sound attribute extension, must preceed block 1.
+          Used to define stereo, 8 bit audio
+        BYTE bBlockID;       // = 8
+        BYTE nBlockLen[3];   // 3 byte length
+        WORD wTimeConstant;  // time constant = same as block 1
+        BYTE bPackMethod;    // same as in block 1
+        BYTE bVoiceMode;     // 0-mono, 1-stereo
+
+        Data is stored left, right
+
+BLOCK 9 - data block that supersedes blocks 1 and 8.
+          Used for stereo, 16 bit.
+
+        BYTE bBlockID;          // = 9
+        BYTE nBlockLen[3];      // length 12 plus length of sound
+        DWORD dwSamplesPerSec;  // samples per second, not time const.
+        BYTE bBitsPerSample;    // e.g., 8 or 16
+        BYTE bChannels;         // 1 for mono, 2 for stereo
+        WORD wFormat;           // see below
+        BYTE reserved[4];       // pad to make block w/o data
+                                // have a size of 16 bytes
+
+        Valid values of wFormat are:
+
+                0x0000  8-bit unsigned PCM
+                0x0001  Creative 8-bit to 4-bit ADPCM
+                0x0002  Creative 8-bit to 3-bit ADPCM
+                0x0003  Creative 8-bit to 2-bit ADPCM
+                0x0004  16-bit signed PCM
+                0x0006  CCITT a-Law
+                0x0007  CCITT u-Law
+                0x02000 Creative 16-bit to 4-bit ADPCM
+
+        Data is stored left, right
+
+------------------------------------------------------------------------*/
diff --git a/src/vox_adpcm.c b/src/vox_adpcm.c
new file mode 100644
index 0000000..e206675
--- /dev/null
+++ b/src/vox_adpcm.c
@@ -0,0 +1,400 @@
+/*
+** Copyright (C) 2002-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*
+**	This is the OKI / Dialogic ADPCM encoder/decoder. It converts from
+**	12 bit linear sample data to a 4 bit ADPCM.
+*/
+
+/*
+ * Note: some early Dialogic hardware does not always reset the ADPCM encoder
+ * at the start of each vox file. This can result in clipping and/or DC offset
+ * problems when it comes to decoding the audio. Whilst little can be done
+ * about the clipping, a DC offset can be removed by passing the decoded audio
+ * through a high-pass filter at e.g. 10Hz.
+ */
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<stdlib.h>
+#include	<string.h>
+#include	<math.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+#include	"ima_oki_adpcm.h"
+
+
+static sf_count_t vox_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
+static sf_count_t vox_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
+static sf_count_t vox_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
+static sf_count_t vox_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
+
+static sf_count_t vox_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
+static sf_count_t vox_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
+static sf_count_t vox_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
+static sf_count_t vox_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
+
+static int vox_read_block (SF_PRIVATE *psf, IMA_OKI_ADPCM *pvox, short *ptr, int len) ;
+
+/*------------------------------------------------------------------------------
+*/
+
+static int
+codec_close (SF_PRIVATE * psf)
+{
+	IMA_OKI_ADPCM * p = (IMA_OKI_ADPCM *) psf->codec_data ;
+
+	if (p->errors)
+		psf_log_printf (psf, "*** Warning : ADPCM state errors: %d\n", p->errors) ;
+	return p->errors ;
+} /* code_close */
+
+int
+vox_adpcm_init (SF_PRIVATE *psf)
+{	IMA_OKI_ADPCM *pvox = NULL ;
+
+	if (psf->file.mode == SFM_RDWR)
+		return SFE_BAD_MODE_RW ;
+
+	if (psf->file.mode == SFM_WRITE && psf->sf.channels != 1)
+		return SFE_CHANNEL_COUNT ;
+
+	if ((pvox = malloc (sizeof (IMA_OKI_ADPCM))) == NULL)
+		return SFE_MALLOC_FAILED ;
+
+	psf->codec_data = (void*) pvox ;
+	memset (pvox, 0, sizeof (IMA_OKI_ADPCM)) ;
+
+	if (psf->file.mode == SFM_WRITE)
+	{	psf->write_short	= vox_write_s ;
+		psf->write_int		= vox_write_i ;
+		psf->write_float	= vox_write_f ;
+		psf->write_double	= vox_write_d ;
+		}
+	else
+	{	psf_log_printf (psf, "Header-less OKI Dialogic ADPCM encoded file.\n") ;
+		psf_log_printf (psf, "Setting up for 8kHz, mono, Vox ADPCM.\n") ;
+
+		psf->read_short		= vox_read_s ;
+		psf->read_int		= vox_read_i ;
+		psf->read_float		= vox_read_f ;
+		psf->read_double	= vox_read_d ;
+		} ;
+
+	/* Standard sample rate chennels etc. */
+	if (psf->sf.samplerate < 1)
+		psf->sf.samplerate	= 8000 ;
+	psf->sf.channels	= 1 ;
+
+	psf->sf.frames = psf->filelength * 2 ;
+
+	psf->sf.seekable = SF_FALSE ;
+	psf->codec_close = codec_close ;
+
+	/* Seek back to start of data. */
+	if (psf_fseek (psf, 0 , SEEK_SET) == -1)
+		return SFE_BAD_SEEK ;
+
+	ima_oki_adpcm_init (pvox, IMA_OKI_ADPCM_TYPE_OKI) ;
+
+	return 0 ;
+} /* vox_adpcm_init */
+
+/*==============================================================================
+*/
+
+static int
+vox_read_block (SF_PRIVATE *psf, IMA_OKI_ADPCM *pvox, short *ptr, int len)
+{	int	indx = 0, k ;
+
+	while (indx < len)
+	{	pvox->code_count = (len - indx > IMA_OKI_ADPCM_PCM_LEN) ? IMA_OKI_ADPCM_CODE_LEN : (len - indx + 1) / 2 ;
+
+		if ((k = (int) psf_fread (pvox->codes, 1, pvox->code_count, psf)) != pvox->code_count)
+		{	if (psf_ftell (psf) != psf->filelength)
+				psf_log_printf (psf, "*** Warning : short read (%d != %d).\n", k, pvox->code_count) ;
+			if (k == 0)
+				break ;
+			} ;
+
+		pvox->code_count = k ;
+
+		ima_oki_adpcm_decode_block (pvox) ;
+
+		memcpy (&(ptr [indx]), pvox->pcm, pvox->pcm_count * sizeof (short)) ;
+		indx += pvox->pcm_count ;
+		} ;
+
+	return indx ;
+} /* vox_read_block */
+
+
+static sf_count_t
+vox_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len)
+{	IMA_OKI_ADPCM 	*pvox ;
+	int			readcount, count ;
+	sf_count_t	total = 0 ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	pvox = (IMA_OKI_ADPCM*) psf->codec_data ;
+
+	while (len > 0)
+	{	readcount = (len > 0x10000000) ? 0x10000000 : (int) len ;
+
+		count = vox_read_block (psf, pvox, ptr, readcount) ;
+
+		total += count ;
+		len -= count ;
+		if (count != readcount)
+			break ;
+		} ;
+
+	return total ;
+} /* vox_read_s */
+
+static sf_count_t
+vox_read_i	(SF_PRIVATE *psf, int *ptr, sf_count_t len)
+{	IMA_OKI_ADPCM *pvox ;
+	BUF_UNION	ubuf ;
+	short		*sptr ;
+	int			k, bufferlen, readcount, count ;
+	sf_count_t	total = 0 ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	pvox = (IMA_OKI_ADPCM*) psf->codec_data ;
+
+	sptr = ubuf.sbuf ;
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+	while (len > 0)
+	{	readcount = (len >= bufferlen) ? bufferlen : (int) len ;
+		count = vox_read_block (psf, pvox, sptr, readcount) ;
+		for (k = 0 ; k < readcount ; k++)
+			ptr [total + k] = arith_shift_left (sptr [k], 16) ;
+		total += count ;
+		len -= readcount ;
+		if (count != readcount)
+			break ;
+		} ;
+
+	return total ;
+} /* vox_read_i */
+
+static sf_count_t
+vox_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len)
+{	IMA_OKI_ADPCM *pvox ;
+	BUF_UNION	ubuf ;
+	short		*sptr ;
+	int			k, bufferlen, readcount, count ;
+	sf_count_t	total = 0 ;
+	float		normfact ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	pvox = (IMA_OKI_ADPCM*) psf->codec_data ;
+
+	normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x8000) : 1.0 ;
+
+	sptr = ubuf.sbuf ;
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+	while (len > 0)
+	{	readcount = (len >= bufferlen) ? bufferlen : (int) len ;
+		count = vox_read_block (psf, pvox, sptr, readcount) ;
+		for (k = 0 ; k < readcount ; k++)
+			ptr [total + k] = normfact * (float) (sptr [k]) ;
+		total += count ;
+		len -= readcount ;
+		if (count != readcount)
+			break ;
+		} ;
+
+	return total ;
+} /* vox_read_f */
+
+static sf_count_t
+vox_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len)
+{	IMA_OKI_ADPCM *pvox ;
+	BUF_UNION	ubuf ;
+	short		*sptr ;
+	int			k, bufferlen, readcount, count ;
+	sf_count_t	total = 0 ;
+	double 		normfact ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	pvox = (IMA_OKI_ADPCM*) psf->codec_data ;
+
+	normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((double) 0x8000) : 1.0 ;
+
+	sptr = ubuf.sbuf ;
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+	while (len > 0)
+	{	readcount = (len >= bufferlen) ? bufferlen : (int) len ;
+		count = vox_read_block (psf, pvox, sptr, readcount) ;
+		for (k = 0 ; k < readcount ; k++)
+			ptr [total + k] = normfact * (double) (sptr [k]) ;
+		total += count ;
+		len -= readcount ;
+		if (count != readcount)
+			break ;
+		} ;
+
+	return total ;
+} /* vox_read_d */
+
+/*------------------------------------------------------------------------------
+*/
+
+static int
+vox_write_block (SF_PRIVATE *psf, IMA_OKI_ADPCM *pvox, const short *ptr, int len)
+{	int	indx = 0, k ;
+
+	while (indx < len)
+	{	pvox->pcm_count = (len - indx > IMA_OKI_ADPCM_PCM_LEN) ? IMA_OKI_ADPCM_PCM_LEN : len - indx ;
+
+		memcpy (pvox->pcm, &(ptr [indx]), pvox->pcm_count * sizeof (short)) ;
+
+		ima_oki_adpcm_encode_block (pvox) ;
+
+		if ((k = (int) psf_fwrite (pvox->codes, 1, pvox->code_count, psf)) != pvox->code_count)
+			psf_log_printf (psf, "*** Warning : short write (%d != %d).\n", k, pvox->code_count) ;
+
+		indx += pvox->pcm_count ;
+		} ;
+
+	return indx ;
+} /* vox_write_block */
+
+static sf_count_t
+vox_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len)
+{	IMA_OKI_ADPCM 	*pvox ;
+	int			writecount, count ;
+	sf_count_t	total = 0 ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	pvox = (IMA_OKI_ADPCM*) psf->codec_data ;
+
+	while (len)
+	{	writecount = (len > 0x10000000) ? 0x10000000 : (int) len ;
+
+		count = vox_write_block (psf, pvox, ptr, writecount) ;
+
+		total += count ;
+		len -= count ;
+		if (count != writecount)
+			break ;
+		} ;
+
+	return total ;
+} /* vox_write_s */
+
+static sf_count_t
+vox_write_i	(SF_PRIVATE *psf, const int *ptr, sf_count_t len)
+{	IMA_OKI_ADPCM *pvox ;
+	BUF_UNION	ubuf ;
+	short		*sptr ;
+	int			k, bufferlen, writecount, count ;
+	sf_count_t	total = 0 ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	pvox = (IMA_OKI_ADPCM*) psf->codec_data ;
+
+	sptr = ubuf.sbuf ;
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+	while (len > 0)
+	{	writecount = (len >= bufferlen) ? bufferlen : (int) len ;
+		for (k = 0 ; k < writecount ; k++)
+			sptr [k] = ptr [total + k] >> 16 ;
+		count = vox_write_block (psf, pvox, sptr, writecount) ;
+		total += count ;
+		len -= writecount ;
+		if (count != writecount)
+			break ;
+		} ;
+
+	return total ;
+} /* vox_write_i */
+
+static sf_count_t
+vox_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len)
+{	IMA_OKI_ADPCM *pvox ;
+	BUF_UNION	ubuf ;
+	short		*sptr ;
+	int			k, bufferlen, writecount, count ;
+	sf_count_t	total = 0 ;
+	float		normfact ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	pvox = (IMA_OKI_ADPCM*) psf->codec_data ;
+
+	normfact = (psf->norm_float == SF_TRUE) ? (1.0 * 0x7FFF) : 1.0 ;
+
+	sptr = ubuf.sbuf ;
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+	while (len > 0)
+	{	writecount = (len >= bufferlen) ? bufferlen : (int) len ;
+		for (k = 0 ; k < writecount ; k++)
+			sptr [k] = psf_lrintf (normfact * ptr [total + k]) ;
+		count = vox_write_block (psf, pvox, sptr, writecount) ;
+		total += count ;
+		len -= writecount ;
+		if (count != writecount)
+			break ;
+		} ;
+
+	return total ;
+} /* vox_write_f */
+
+static sf_count_t
+vox_write_d	(SF_PRIVATE *psf, const double *ptr, sf_count_t len)
+{	IMA_OKI_ADPCM *pvox ;
+	BUF_UNION	ubuf ;
+	short		*sptr ;
+	int			k, bufferlen, writecount, count ;
+	sf_count_t	total = 0 ;
+	double 		normfact ;
+
+	if (! psf->codec_data)
+		return 0 ;
+	pvox = (IMA_OKI_ADPCM*) psf->codec_data ;
+
+	normfact = (psf->norm_double == SF_TRUE) ? (1.0 * 0x7FFF) : 1.0 ;
+
+	sptr = ubuf.sbuf ;
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+	while (len > 0)
+	{	writecount = (len >= bufferlen) ? bufferlen : (int) len ;
+		for (k = 0 ; k < writecount ; k++)
+			sptr [k] = psf_lrint (normfact * ptr [total + k]) ;
+		count = vox_write_block (psf, pvox, sptr, writecount) ;
+		total += count ;
+		len -= writecount ;
+		if (count != writecount)
+			break ;
+		} ;
+
+	return total ;
+} /* vox_write_d */
+
diff --git a/src/w64.c b/src/w64.c
new file mode 100644
index 0000000..14346cd
--- /dev/null
+++ b/src/w64.c
@@ -0,0 +1,640 @@
+/*
+** Copyright (C) 1999-2018 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<string.h>
+#include	<ctype.h>
+#include	<time.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+#include	"wavlike.h"
+
+/*------------------------------------------------------------------------------
+** W64 files use 16 byte markers as opposed to the four byte marker of
+** WAV files.
+** For comparison purposes, an integer is required, so make an integer
+** hash for the 16 bytes using MAKE_HASH16 macro, but also create a 16
+** byte array containing the complete 16 bytes required when writing the
+** header.
+*/
+
+#define MAKE_HASH16(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, xa, xb, xc, xd, xe, xf)	\
+			(	(x0)			^ ((x1) << 1)	^ ((x2) << 2)	^ ((x3) << 3) ^	\
+				((x4) << 4) 	^ ((x5) << 5)	^ ((x6) << 6)	^ ((x7) << 7) ^	\
+				((x8) << 8) 	^ ((x9) << 9)	^ ((xa) << 10)	^ ((xb) << 11) ^ \
+				((xc) << 12) 	^ ((xd) << 13)	^ ((xe) << 14)	^ ((xf) << 15)	)
+
+#define MAKE_MARKER16(name, x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, xa, xb, xc, xd, xe, xf)	\
+			static unsigned char name [16] = { (x0), (x1), (x2), (x3), (x4), (x5), \
+				(x6), (x7), (x8), (x9), (xa), (xb), (xc), (xd), (xe), (xf) }
+
+#define	riff_HASH16 MAKE_HASH16 ('r', 'i', 'f', 'f', 0x2E, 0x91, 0xCF, 0x11, \
+								0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00)
+
+#define	wave_HASH16 	MAKE_HASH16 ('w', 'a', 'v', 'e', 0xF3, 0xAC, 0xD3, 0x11, \
+								0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A)
+
+#define	fmt_HASH16 		MAKE_HASH16 ('f', 'm', 't', ' ', 0xF3, 0xAC, 0xD3, 0x11, \
+								0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A)
+
+#define	fact_HASH16 	MAKE_HASH16 ('f', 'a', 'c', 't', 0xF3, 0xAC, 0xD3, 0x11, \
+								0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A)
+
+#define	data_HASH16 	MAKE_HASH16 ('d', 'a', 't', 'a', 0xF3, 0xAC, 0xD3, 0x11, \
+								0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A)
+
+#define	ACID_HASH16 	MAKE_HASH16 (0x6D, 0x07, 0x1C, 0xEA, 0xA3, 0xEF, 0x78, 0x4C, \
+								0x90, 0x57, 0x7F, 0x79, 0xEE, 0x25, 0x2A, 0xAE)
+
+#define	levl_HASH16		MAKE_HASH16 (0x6c, 0x65, 0x76, 0x6c, 0xf3, 0xac, 0xd3, 0x11, \
+								0xd1, 0x8c, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A)
+
+#define list_HASH16		MAKE_HASH16 (0x6C, 0x69, 0x73, 0x74, 0x2F, 0x91, 0xCF, 0x11, \
+								0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00)
+
+#define junk_HASH16		MAKE_HASH16 (0x6A, 0x75, 0x6E, 0x6b, 0xF3, 0xAC, 0xD3, 0x11, \
+								0x8C, 0xD1, 0x00, 0xC0, 0x4f, 0x8E, 0xDB, 0x8A)
+
+#define bext_HASH16		MAKE_HASH16 (0x62, 0x65, 0x78, 0x74, 0xf3, 0xac, 0xd3, 0xaa, \
+								0xd1, 0x8c, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A)
+
+#define MARKER_HASH16	MAKE_HASH16 (0x56, 0x62, 0xf7, 0xab, 0x2d, 0x39, 0xd2, 0x11, \
+								0x86, 0xc7, 0x00, 0xc0, 0x4f, 0x8e, 0xdb, 0x8a)
+
+#define	SUMLIST_HASH16	MAKE_HASH16 (0xBC, 0x94, 0x5F, 0x92, 0x5A, 0x52, 0xD2, 0x11, \
+								0x86, 0xDC, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A)
+
+
+MAKE_MARKER16 (riff_MARKER16, 'r', 'i', 'f', 'f', 0x2E, 0x91, 0xCF, 0x11,
+								0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00) ;
+
+
+MAKE_MARKER16 (wave_MARKER16, 'w', 'a', 'v', 'e', 0xF3, 0xAC, 0xD3, 0x11,
+								0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A) ;
+
+MAKE_MARKER16 (fmt_MARKER16, 'f', 'm', 't', ' ', 0xF3, 0xAC, 0xD3, 0x11,
+								0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A) ;
+
+MAKE_MARKER16 (fact_MARKER16, 'f', 'a', 'c', 't', 0xF3, 0xAC, 0xD3, 0x11,
+								0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A) ;
+
+MAKE_MARKER16 (data_MARKER16, 'd', 'a', 't', 'a', 0xF3, 0xAC, 0xD3, 0x11,
+								0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A) ;
+
+enum
+{	HAVE_riff	= 0x01,
+	HAVE_wave	= 0x02,
+	HAVE_fmt	= 0x04,
+	HAVE_fact	= 0x08,
+	HAVE_data	= 0x20
+} ;
+
+/*------------------------------------------------------------------------------
+ * Private static functions.
+ */
+
+static int	w64_read_header	(SF_PRIVATE *psf, int *blockalign, int *framesperblock) ;
+static int	w64_write_header (SF_PRIVATE *psf, int calc_length) ;
+static int	w64_close (SF_PRIVATE *psf) ;
+
+/*------------------------------------------------------------------------------
+** Public function.
+*/
+
+int
+w64_open	(SF_PRIVATE *psf)
+{	WAVLIKE_PRIVATE * wpriv ;
+	int	subformat, error, blockalign = 0, framesperblock = 0 ;
+
+	if ((wpriv = calloc (1, sizeof (WAVLIKE_PRIVATE))) == NULL)
+		return SFE_MALLOC_FAILED ;
+	psf->container_data = wpriv ;
+
+	if (psf->file.mode == SFM_READ || (psf->file.mode == SFM_RDWR &&psf->filelength > 0))
+	{	if ((error = w64_read_header (psf, &blockalign, &framesperblock)))
+			return error ;
+		} ;
+
+	if ((SF_CONTAINER (psf->sf.format)) != SF_FORMAT_W64)
+		return	SFE_BAD_OPEN_FORMAT ;
+
+	subformat = SF_CODEC (psf->sf.format) ;
+
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	if (psf->is_pipe)
+			return SFE_NO_PIPE_WRITE ;
+
+		psf->endian = SF_ENDIAN_LITTLE ;		/* All W64 files are little endian. */
+
+		psf->blockwidth = psf->bytewidth * psf->sf.channels ;
+
+		if (subformat == SF_FORMAT_IMA_ADPCM || subformat == SF_FORMAT_MS_ADPCM)
+		{	blockalign = wavlike_srate2blocksize (psf->sf.samplerate * psf->sf.channels) ;
+			framesperblock = -1 ;
+
+			/*
+			** At this point we don't know the file length so set it stupidly high, but not
+			** so high that it triggers undefined behaviour whan something is added to it.
+			*/
+			psf->filelength = SF_COUNT_MAX - 10000 ;
+			psf->datalength = psf->filelength ;
+			if (psf->sf.frames <= 0)
+				psf->sf.frames = (psf->blockwidth) ? psf->filelength / psf->blockwidth : psf->filelength ;
+			} ;
+
+		if ((error = w64_write_header (psf, SF_FALSE)))
+			return error ;
+
+		psf->write_header = w64_write_header ;
+		} ;
+
+	psf->container_close = w64_close ;
+
+	switch (subformat)
+	{	case SF_FORMAT_PCM_U8 :
+					error = pcm_init (psf) ;
+					break ;
+
+		case SF_FORMAT_PCM_16 :
+		case SF_FORMAT_PCM_24 :
+		case SF_FORMAT_PCM_32 :
+					error = pcm_init (psf) ;
+					break ;
+
+		case SF_FORMAT_ULAW :
+					error = ulaw_init (psf) ;
+					break ;
+
+		case SF_FORMAT_ALAW :
+					error = alaw_init (psf) ;
+					break ;
+
+		/* Lite remove start */
+		case SF_FORMAT_FLOAT :
+					error = float32_init (psf) ;
+					break ;
+
+		case SF_FORMAT_DOUBLE :
+					error = double64_init (psf) ;
+					break ;
+
+		case SF_FORMAT_IMA_ADPCM :
+					error = wavlike_ima_init (psf, blockalign, framesperblock) ;
+					break ;
+
+		case SF_FORMAT_MS_ADPCM :
+					error = wavlike_msadpcm_init (psf, blockalign, framesperblock) ;
+					break ;
+		/* Lite remove end */
+
+		case SF_FORMAT_GSM610 :
+					error = gsm610_init (psf) ;
+					break ;
+
+		default : 	return SFE_UNIMPLEMENTED ;
+		} ;
+
+	return error ;
+} /* w64_open */
+
+/*=========================================================================
+** Private functions.
+*/
+
+static int
+w64_read_header	(SF_PRIVATE *psf, int *blockalign, int *framesperblock)
+{	WAVLIKE_PRIVATE *wpriv ;
+	WAV_FMT 	*wav_fmt ;
+	int			dword = 0, marker, format = 0 ;
+	sf_count_t	chunk_size, bytesread = 0 ;
+	int			parsestage = 0, error, done = 0 ;
+
+	if ((wpriv = psf->container_data) == NULL)
+		return SFE_INTERNAL ;
+	wav_fmt = &wpriv->wav_fmt ;
+
+	/* Set position to start of file to begin reading header. */
+	psf_binheader_readf (psf, "p", 0) ;
+
+	while (! done)
+	{	/* Each new chunk must start on an 8 byte boundary, so jump if needed. */
+		if (psf->header.indx & 0x7)
+			psf_binheader_readf (psf, "j", 8 - (psf->header.indx & 0x7)) ;
+
+		/* Generate hash of 16 byte marker. */
+		marker = 0 ;
+		chunk_size = 0 ;
+
+		bytesread = psf_binheader_readf (psf, "eh8", &marker, &chunk_size) ;
+		if (bytesread == 0)
+			break ;
+		switch (marker)
+		{	case riff_HASH16 :
+					if (parsestage)
+						return SFE_W64_NO_RIFF ;
+
+					if (psf->filelength != chunk_size)
+						psf_log_printf (psf, "riff : %D (should be %D)\n", chunk_size, psf->filelength) ;
+					else
+						psf_log_printf (psf, "riff : %D\n", chunk_size) ;
+
+					parsestage |= HAVE_riff ;
+
+					bytesread += psf_binheader_readf (psf, "h", &marker) ;
+					if (marker == wave_HASH16)
+					{ 	if ((parsestage & HAVE_riff) != HAVE_riff)
+							return SFE_W64_NO_WAVE ;
+						psf_log_printf (psf, "wave\n") ;
+						parsestage |= HAVE_wave ;
+					} ;
+					chunk_size = 0 ;
+					break ;
+
+			case ACID_HASH16:
+					psf_log_printf (psf, "Looks like an ACID file. Exiting.\n") ;
+					return SFE_UNIMPLEMENTED ;
+
+			case fmt_HASH16 :
+					if ((parsestage & (HAVE_riff | HAVE_wave)) != (HAVE_riff | HAVE_wave))
+						return SFE_WAV_NO_FMT ;
+
+					psf_log_printf (psf, " fmt : %D\n", chunk_size) ;
+
+					/* size of 16 byte marker and 8 byte chunk_size value. */
+					chunk_size -= 24 ;
+
+					if ((error = wavlike_read_fmt_chunk (psf, (int) chunk_size)))
+						return error ;
+
+					if (chunk_size % 8)
+						psf_binheader_readf (psf, "j", 8 - (chunk_size % 8)) ;
+
+					format		= wav_fmt->format ;
+					parsestage |= HAVE_fmt ;
+					chunk_size = 0 ;
+					break ;
+
+			case fact_HASH16:
+					{	sf_count_t frames ;
+
+						psf_binheader_readf (psf, "e8", &frames) ;
+						psf_log_printf (psf, "fact : %D\n  frames : %D\n",
+										chunk_size, frames) ;
+						} ;
+					chunk_size = 0 ;
+					break ;
+
+
+			case data_HASH16 :
+					if ((parsestage & (HAVE_riff | HAVE_wave | HAVE_fmt)) != (HAVE_riff | HAVE_wave | HAVE_fmt))
+						return SFE_W64_NO_DATA ;
+
+					psf->dataoffset = psf_ftell (psf) ;
+					psf->datalength = SF_MIN (chunk_size - 24, psf->filelength - psf->dataoffset) ;
+
+					if (chunk_size % 8)
+						chunk_size += 8 - (chunk_size % 8) ;
+
+					psf_log_printf (psf, "data : %D\n", chunk_size) ;
+
+					parsestage |= HAVE_data ;
+
+					if (! psf->sf.seekable)
+						break ;
+
+					/* Seek past data and continue reading header. */
+					psf_fseek (psf, chunk_size, SEEK_CUR) ;
+					chunk_size = 0 ;
+					break ;
+
+			case levl_HASH16 :
+					psf_log_printf (psf, "levl : %D\n", chunk_size) ;
+					break ;
+
+			case list_HASH16 :
+					psf_log_printf (psf, "list : %D\n", chunk_size) ;
+					break ;
+
+			case junk_HASH16 :
+					psf_log_printf (psf, "junk : %D\n", chunk_size) ;
+					break ;
+
+			case bext_HASH16 :
+					psf_log_printf (psf, "bext : %D\n", chunk_size) ;
+					break ;
+
+			case MARKER_HASH16 :
+					psf_log_printf (psf, "marker : %D\n", chunk_size) ;
+					break ;
+
+			case SUMLIST_HASH16 :
+					psf_log_printf (psf, "summary list : %D\n", chunk_size) ;
+					break ;
+
+			default :
+					psf_log_printf (psf, "*** Unknown chunk marker (%X) at position %D with length %D. Skipping and continuing.\n", marker, psf_ftell (psf) - 8, chunk_size) ;
+					break ;
+			} ;	/* switch (dword) */
+
+		if (chunk_size >= psf->filelength)
+		{	psf_log_printf (psf, "*** Chunk size %u > file length %D. Exiting parser.\n", chunk_size, psf->filelength) ;
+			break ;
+			} ;
+
+		if (psf->sf.seekable == 0 && (parsestage & HAVE_data))
+			break ;
+
+		if (psf_ftell (psf) >= (psf->filelength - (2 * SIGNED_SIZEOF (dword))))
+			break ;
+
+		if (chunk_size > 0 && chunk_size < 0xffff0000)
+		{	dword = chunk_size ;
+			psf_binheader_readf (psf, "j", dword - 24) ;
+			} ;
+		} ; /* while (1) */
+
+	if (psf->dataoffset <= 0)
+		return SFE_W64_NO_DATA ;
+
+	if (psf->sf.channels < 1)
+		return SFE_CHANNEL_COUNT_ZERO ;
+
+	if (psf->sf.channels > SF_MAX_CHANNELS)
+		return SFE_CHANNEL_COUNT ;
+
+	psf->endian = SF_ENDIAN_LITTLE ;		/* All W64 files are little endian. */
+
+	if (psf_ftell (psf) != psf->dataoffset)
+		psf_fseek (psf, psf->dataoffset, SEEK_SET) ;
+
+	if (psf->blockwidth)
+	{	if (psf->filelength - psf->dataoffset < psf->datalength)
+			psf->sf.frames = (psf->filelength - psf->dataoffset) / psf->blockwidth ;
+		else
+			psf->sf.frames = psf->datalength / psf->blockwidth ;
+		} ;
+
+	switch (format)
+	{	case WAVE_FORMAT_PCM :
+		case WAVE_FORMAT_EXTENSIBLE :
+					/* extensible might be FLOAT, MULAW, etc as well! */
+					psf->sf.format = SF_FORMAT_W64 | u_bitwidth_to_subformat (psf->bytewidth * 8) ;
+					break ;
+
+		case WAVE_FORMAT_MULAW :
+					psf->sf.format = (SF_FORMAT_W64 | SF_FORMAT_ULAW) ;
+					break ;
+
+		case WAVE_FORMAT_ALAW :
+					psf->sf.format = (SF_FORMAT_W64 | SF_FORMAT_ALAW) ;
+					break ;
+
+		case WAVE_FORMAT_MS_ADPCM :
+					psf->sf.format = (SF_FORMAT_W64 | SF_FORMAT_MS_ADPCM) ;
+					*blockalign = wav_fmt->msadpcm.blockalign ;
+					*framesperblock = wav_fmt->msadpcm.samplesperblock ;
+					break ;
+
+		case WAVE_FORMAT_IMA_ADPCM :
+					psf->sf.format = (SF_FORMAT_W64 | SF_FORMAT_IMA_ADPCM) ;
+					*blockalign = wav_fmt->ima.blockalign ;
+					*framesperblock = wav_fmt->ima.samplesperblock ;
+					break ;
+
+		case WAVE_FORMAT_GSM610 :
+					psf->sf.format = (SF_FORMAT_W64 | SF_FORMAT_GSM610) ;
+					break ;
+
+		case WAVE_FORMAT_IEEE_FLOAT :
+					psf->sf.format = SF_FORMAT_W64 ;
+					psf->sf.format |= (psf->bytewidth == 8) ? SF_FORMAT_DOUBLE : SF_FORMAT_FLOAT ;
+					break ;
+
+		default : return SFE_UNIMPLEMENTED ;
+		} ;
+
+	return 0 ;
+} /* w64_read_header */
+
+static int
+w64_write_header (SF_PRIVATE *psf, int calc_length)
+{	sf_count_t 	fmt_size, current ;
+	size_t		fmt_pad = 0 ;
+	int 		subformat, add_fact_chunk = SF_FALSE ;
+
+	current = psf_ftell (psf) ;
+
+	if (calc_length)
+	{	psf->filelength = psf_get_filelen (psf) ;
+
+		psf->datalength = psf->filelength - psf->dataoffset ;
+		if (psf->dataend)
+			psf->datalength -= psf->filelength - psf->dataend ;
+
+		if (psf->bytewidth)
+			psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ;
+		} ;
+
+	/* Reset the current header length to zero. */
+	psf->header.ptr [0] = 0 ;
+	psf->header.indx = 0 ;
+	psf_fseek (psf, 0, SEEK_SET) ;
+
+	/* riff marker, length, wave and 'fmt ' markers. */
+	psf_binheader_writef (psf, "eh8hh", BHWh (riff_MARKER16), BHW8 (psf->filelength), BHWh (wave_MARKER16), BHWh (fmt_MARKER16)) ;
+
+	subformat = SF_CODEC (psf->sf.format) ;
+
+	switch (subformat)
+	{	case	SF_FORMAT_PCM_U8 :
+		case	SF_FORMAT_PCM_16 :
+		case	SF_FORMAT_PCM_24 :
+		case	SF_FORMAT_PCM_32 :
+					fmt_size = 24 + 2 + 2 + 4 + 4 + 2 + 2 ;
+					fmt_pad = (size_t) ((fmt_size & 0x7) ? 8 - (fmt_size & 0x7) : 0) ;
+					fmt_size += fmt_pad ;
+
+					/* fmt : format, channels, samplerate */
+					psf_binheader_writef (psf, "e8224", BHW8 (fmt_size), BHW2 (WAVE_FORMAT_PCM), BHW2 (psf->sf.channels), BHW4 (psf->sf.samplerate)) ;
+					/*  fmt : bytespersec */
+					psf_binheader_writef (psf, "e4", BHW4 (psf->sf.samplerate * psf->bytewidth * psf->sf.channels)) ;
+					/*  fmt : blockalign, bitwidth */
+					psf_binheader_writef (psf, "e22", BHW2 (psf->bytewidth * psf->sf.channels), BHW2 (psf->bytewidth * 8)) ;
+					break ;
+
+		case SF_FORMAT_FLOAT :
+		case SF_FORMAT_DOUBLE :
+					fmt_size = 24 + 2 + 2 + 4 + 4 + 2 + 2 ;
+					fmt_pad = (size_t) ((fmt_size & 0x7) ? 8 - (fmt_size & 0x7) : 0) ;
+					fmt_size += fmt_pad ;
+
+					/* fmt : format, channels, samplerate */
+					psf_binheader_writef (psf, "e8224", BHW8 (fmt_size), BHW2 (WAVE_FORMAT_IEEE_FLOAT), BHW2 (psf->sf.channels), BHW4 (psf->sf.samplerate)) ;
+					/*  fmt : bytespersec */
+					psf_binheader_writef (psf, "e4", BHW4 (psf->sf.samplerate * psf->bytewidth * psf->sf.channels)) ;
+					/*  fmt : blockalign, bitwidth */
+					psf_binheader_writef (psf, "e22", BHW2 (psf->bytewidth * psf->sf.channels), BHW2 (psf->bytewidth * 8)) ;
+
+					add_fact_chunk = SF_TRUE ;
+					break ;
+
+		case SF_FORMAT_ULAW :
+					fmt_size = 24 + 2 + 2 + 4 + 4 + 2 + 2 ;
+					fmt_pad = (size_t) ((fmt_size & 0x7) ? 8 - (fmt_size & 0x7) : 0) ;
+					fmt_size += fmt_pad ;
+
+					/* fmt : format, channels, samplerate */
+					psf_binheader_writef (psf, "e8224", BHW8 (fmt_size), BHW2 (WAVE_FORMAT_MULAW), BHW2 (psf->sf.channels), BHW4 (psf->sf.samplerate)) ;
+					/*  fmt : bytespersec */
+					psf_binheader_writef (psf, "e4", BHW4 (psf->sf.samplerate * psf->bytewidth * psf->sf.channels)) ;
+					/*  fmt : blockalign, bitwidth */
+					psf_binheader_writef (psf, "e22", BHW2 (psf->bytewidth * psf->sf.channels), BHW2 (8)) ;
+
+					add_fact_chunk = SF_TRUE ;
+					break ;
+
+		case SF_FORMAT_ALAW :
+					fmt_size = 24 + 2 + 2 + 4 + 4 + 2 + 2 ;
+					fmt_pad = (size_t) ((fmt_size & 0x7) ? 8 - (fmt_size & 0x7) : 0) ;
+					fmt_size += fmt_pad ;
+
+					/* fmt : format, channels, samplerate */
+					psf_binheader_writef (psf, "e8224", BHW8 (fmt_size), BHW2 (WAVE_FORMAT_ALAW), BHW2 (psf->sf.channels), BHW4 (psf->sf.samplerate)) ;
+					/*  fmt : bytespersec */
+					psf_binheader_writef (psf, "e4", BHW4 (psf->sf.samplerate * psf->bytewidth * psf->sf.channels)) ;
+					/*  fmt : blockalign, bitwidth */
+					psf_binheader_writef (psf, "e22", BHW2 (psf->bytewidth * psf->sf.channels), BHW2 (8)) ;
+
+					add_fact_chunk = SF_TRUE ;
+					break ;
+
+		/* Lite remove start */
+		case SF_FORMAT_IMA_ADPCM :
+					{	int		blockalign, framesperblock, bytespersec ;
+
+						blockalign		= wavlike_srate2blocksize (psf->sf.samplerate * psf->sf.channels) ;
+						framesperblock	= 2 * (blockalign - 4 * psf->sf.channels) / psf->sf.channels + 1 ;
+						bytespersec		= (psf->sf.samplerate * blockalign) / framesperblock ;
+
+						/* fmt chunk. */
+						fmt_size = 24 + 2 + 2 + 4 + 4 + 2 + 2 + 2 + 2 ;
+						fmt_pad = (size_t) ((fmt_size & 0x7) ? 8 - (fmt_size & 0x7) : 0) ;
+						fmt_size += fmt_pad ;
+
+						/* fmt : size, WAV format type, channels. */
+						psf_binheader_writef (psf, "e822", BHW8 (fmt_size), BHW2 (WAVE_FORMAT_IMA_ADPCM), BHW2 (psf->sf.channels)) ;
+
+						/* fmt : samplerate, bytespersec. */
+						psf_binheader_writef (psf, "e44", BHW4 (psf->sf.samplerate), BHW4 (bytespersec)) ;
+
+						/* fmt : blockalign, bitwidth, extrabytes, framesperblock. */
+						psf_binheader_writef (psf, "e2222", BHW2 (blockalign), BHW2 (4), BHW2 (2), BHW2 (framesperblock)) ;
+						} ;
+
+					add_fact_chunk = SF_TRUE ;
+					break ;
+
+		case SF_FORMAT_MS_ADPCM :
+					{	int blockalign, framesperblock, bytespersec, extrabytes ;
+
+						blockalign		= wavlike_srate2blocksize (psf->sf.samplerate * psf->sf.channels) ;
+						framesperblock	= 2 + 2 * (blockalign - 7 * psf->sf.channels) / psf->sf.channels ;
+						bytespersec		= (psf->sf.samplerate * blockalign) / framesperblock ;
+
+						/* fmt chunk. */
+						extrabytes	= 2 + 2 + WAVLIKE_MSADPCM_ADAPT_COEFF_COUNT * (2 + 2) ;
+						fmt_size	= 24 + 2 + 2 + 4 + 4 + 2 + 2 + 2 + extrabytes ;
+						fmt_pad = (size_t) ((fmt_size & 0x7) ? 8 - (fmt_size & 0x7) : 0) ;
+						fmt_size += fmt_pad ;
+
+						/* fmt : size, W64 format type, channels. */
+						psf_binheader_writef (psf, "e822", BHW8 (fmt_size), BHW2 (WAVE_FORMAT_MS_ADPCM), BHW2 (psf->sf.channels)) ;
+
+						/* fmt : samplerate, bytespersec. */
+						psf_binheader_writef (psf, "e44", BHW4 (psf->sf.samplerate), BHW4 (bytespersec)) ;
+
+						/* fmt : blockalign, bitwidth, extrabytes, framesperblock. */
+						psf_binheader_writef (psf, "e22222", BHW2 (blockalign), BHW2 (4), BHW2 (extrabytes), BHW2 (framesperblock), BHW2 (7)) ;
+
+						wavlike_msadpcm_write_adapt_coeffs (psf) ;
+						} ;
+
+					add_fact_chunk = SF_TRUE ;
+					break ;
+		/* Lite remove end */
+
+		case SF_FORMAT_GSM610 :
+					{	int bytespersec ;
+
+						bytespersec = (psf->sf.samplerate * WAVLIKE_GSM610_BLOCKSIZE) / WAVLIKE_GSM610_SAMPLES ;
+
+						/* fmt chunk. */
+						fmt_size = 24 + 2 + 2 + 4 + 4 + 2 + 2 + 2 + 2 ;
+						fmt_pad = (size_t) ((fmt_size & 0x7) ? 8 - (fmt_size & 0x7) : 0) ;
+						fmt_size += fmt_pad ;
+
+						/* fmt : size, WAV format type, channels. */
+						psf_binheader_writef (psf, "e822", BHW8 (fmt_size), BHW2 (WAVE_FORMAT_GSM610), BHW2 (psf->sf.channels)) ;
+
+						/* fmt : samplerate, bytespersec. */
+						psf_binheader_writef (psf, "e44", BHW4 (psf->sf.samplerate), BHW4 (bytespersec)) ;
+
+						/* fmt : blockalign, bitwidth, extrabytes, framesperblock. */
+						psf_binheader_writef (psf, "e2222", BHW2 (WAVLIKE_GSM610_BLOCKSIZE), BHW2 (0), BHW2 (2), BHW2 (WAVLIKE_GSM610_SAMPLES)) ;
+						} ;
+
+					add_fact_chunk = SF_TRUE ;
+					break ;
+
+		default : 	return SFE_UNIMPLEMENTED ;
+		} ;
+
+	/* Pad to 8 bytes with zeros. */
+	if (fmt_pad > 0)
+		psf_binheader_writef (psf, "z", BHWz (fmt_pad)) ;
+
+	if (add_fact_chunk)
+		psf_binheader_writef (psf, "eh88", BHWh (fact_MARKER16), BHW8 ((sf_count_t) (16 + 8 + 8)), BHW8 (psf->sf.frames)) ;
+
+	psf_binheader_writef (psf, "eh8", BHWh (data_MARKER16), BHW8 (psf->datalength + 24)) ;
+	psf_fwrite (psf->header.ptr, psf->header.indx, 1, psf) ;
+
+	if (psf->error)
+		return psf->error ;
+
+	psf->dataoffset = psf->header.indx ;
+
+	if (current > 0)
+		psf_fseek (psf, current, SEEK_SET) ;
+
+	return psf->error ;
+} /* w64_write_header */
+
+static int
+w64_close (SF_PRIVATE *psf)
+{
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+		w64_write_header (psf, SF_TRUE) ;
+
+	return 0 ;
+} /* w64_close */
+
diff --git a/src/wav.c b/src/wav.c
new file mode 100644
index 0000000..b870686
--- /dev/null
+++ b/src/wav.c
@@ -0,0 +1,1653 @@
+/*
+** Copyright (C) 1999-2019 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2004-2005 David Viens <davidv@plogue.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<stdlib.h>
+#include	<string.h>
+#include	<ctype.h>
+#include	<time.h>
+#include	<inttypes.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+#include	"wavlike.h"
+
+/*------------------------------------------------------------------------------
+ * Macros to handle big/little endian issues.
+ */
+
+#define RIFF_MARKER		(MAKE_MARKER ('R', 'I', 'F', 'F'))
+#define RIFX_MARKER		(MAKE_MARKER ('R', 'I', 'F', 'X'))
+#define WAVE_MARKER		(MAKE_MARKER ('W', 'A', 'V', 'E'))
+#define fmt_MARKER		(MAKE_MARKER ('f', 'm', 't', ' '))
+#define fact_MARKER		(MAKE_MARKER ('f', 'a', 'c', 't'))
+
+#define cue_MARKER		(MAKE_MARKER ('c', 'u', 'e', ' '))
+#define slnt_MARKER		(MAKE_MARKER ('s', 'l', 'n', 't'))
+#define wavl_MARKER		(MAKE_MARKER ('w', 'a', 'v', 'l'))
+#define plst_MARKER		(MAKE_MARKER ('p', 'l', 's', 't'))
+#define smpl_MARKER		(MAKE_MARKER ('s', 'm', 'p', 'l'))
+#define iXML_MARKER		(MAKE_MARKER ('i', 'X', 'M', 'L'))
+#define levl_MARKER		(MAKE_MARKER ('l', 'e', 'v', 'l'))
+#define MEXT_MARKER		(MAKE_MARKER ('M', 'E', 'X', 'T'))
+#define acid_MARKER		(MAKE_MARKER ('a', 'c', 'i', 'd'))
+#define strc_MARKER		(MAKE_MARKER ('s', 't', 'r', 'c'))
+#define afsp_MARKER		(MAKE_MARKER ('a', 'f', 's', 'p'))
+#define clm_MARKER		(MAKE_MARKER ('c', 'l', 'm', ' '))
+#define elmo_MARKER		(MAKE_MARKER ('e', 'l', 'm', 'o'))
+#define FLLR_MARKER		(MAKE_MARKER ('F', 'L', 'L', 'R'))
+
+#define minf_MARKER		(MAKE_MARKER ('m', 'i', 'n', 'f'))
+#define elm1_MARKER		(MAKE_MARKER ('e', 'l', 'm', '1'))
+#define regn_MARKER		(MAKE_MARKER ('r', 'e', 'g', 'n'))
+#define ovwf_MARKER		(MAKE_MARKER ('o', 'v', 'w', 'f'))
+#define umid_MARKER		(MAKE_MARKER ('u', 'm', 'i', 'd'))
+#define SyLp_MARKER		(MAKE_MARKER ('S', 'y', 'L', 'p'))
+#define Cr8r_MARKER		(MAKE_MARKER ('C', 'r', '8', 'r'))
+#define JUNK_MARKER		(MAKE_MARKER ('J', 'U', 'N', 'K'))
+#define PMX_MARKER		(MAKE_MARKER ('_', 'P', 'M', 'X'))
+#define inst_MARKER		(MAKE_MARKER ('i', 'n', 's', 't'))
+#define AFAn_MARKER		(MAKE_MARKER ('A', 'F', 'A', 'n'))
+
+
+/* Weird WAVPACK marker which can show up at the start of the DATA section. */
+#define wvpk_MARKER (MAKE_MARKER ('w', 'v', 'p', 'k'))
+#define OggS_MARKER (MAKE_MARKER ('O', 'g', 'g', 'S'))
+
+/* ID3v1 trailer which can show up at the end and erronerously look like a chunk. */
+#define TAG__MARKER (MAKE_MARKER ('T', 'A', 'G', 0))
+#define TAG__MARKER_MASK (MAKE_MARKER (0xff, 0xff, 0xff, 0))
+
+#define WAVLIKE_PEAK_CHUNK_SIZE(ch) 	(2 * sizeof (int) + ch * (sizeof (float) + sizeof (int)))
+
+
+enum
+{	HAVE_RIFF	= 1 << 0,
+	HAVE_WAVE	= 1 << 1,
+	HAVE_fmt	= 1 << 2,
+	HAVE_fact	= 1 << 3,
+	HAVE_PEAK	= 1 << 4,
+	HAVE_data	= 1 << 5,
+	HAVE_other	= 1 << 6
+} ;
+
+
+/*  known WAVEFORMATEXTENSIBLE GUIDS  */
+static const EXT_SUBFORMAT MSGUID_SUBTYPE_PCM =
+{	0x00000001, 0x0000, 0x0010, {	0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }
+} ;
+
+#if 0
+static const EXT_SUBFORMAT MSGUID_SUBTYPE_MS_ADPCM =
+{	0x00000002, 0x0000, 0x0010, {	0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }
+} ;
+#endif
+
+static const EXT_SUBFORMAT MSGUID_SUBTYPE_IEEE_FLOAT =
+{	0x00000003, 0x0000, 0x0010, {	0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }
+} ;
+
+static const EXT_SUBFORMAT MSGUID_SUBTYPE_ALAW =
+{	0x00000006, 0x0000, 0x0010, {	0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }
+} ;
+
+static const EXT_SUBFORMAT MSGUID_SUBTYPE_MULAW =
+{	0x00000007, 0x0000, 0x0010, {	0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }
+} ;
+
+/*
+** the next two are from
+** http://dream.cs.bath.ac.uk/researchdev/wave-ex/bformat.html
+*/
+static const EXT_SUBFORMAT MSGUID_SUBTYPE_AMBISONIC_B_FORMAT_PCM =
+{	0x00000001, 0x0721, 0x11d3, {	0x86, 0x44, 0xC8, 0xC1, 0xCA, 0x00, 0x00, 0x00 }
+} ;
+
+static const EXT_SUBFORMAT MSGUID_SUBTYPE_AMBISONIC_B_FORMAT_IEEE_FLOAT =
+{	0x00000003, 0x0721, 0x11d3, {	0x86, 0x44, 0xC8, 0xC1, 0xCA, 0x00, 0x00, 0x00 }
+} ;
+
+
+#if 0
+/* maybe interesting one day to read the following through sf_read_raw */
+/* http://www.bath.ac.uk/~masrwd/pvocex/pvocex.html */
+static const EXT_SUBFORMAT MSGUID_SUBTYPE_PVOCEX =
+{	0x8312B9C2, 0x2E6E, 0x11d4, {	0xA8, 0x24, 0xDE, 0x5B, 0x96, 0xC3, 0xAB, 0x21 }
+} ;
+#endif
+
+/*------------------------------------------------------------------------------
+** Private static functions.
+*/
+
+static int	wav_read_header		(SF_PRIVATE *psf, int *blockalign, int *framesperblock) ;
+static int	wav_write_header	(SF_PRIVATE *psf, int calc_length) ;
+
+static int	wav_write_tailer (SF_PRIVATE *psf) ;
+static int	wav_command (SF_PRIVATE *psf, int command, void *data, int datasize) ;
+static int	wav_close (SF_PRIVATE *psf) ;
+
+static int	wav_read_smpl_chunk (SF_PRIVATE *psf, uint32_t chunklen) ;
+static int	wav_read_acid_chunk (SF_PRIVATE *psf, uint32_t chunklen) ;
+
+static int wav_set_chunk (SF_PRIVATE *psf, const SF_CHUNK_INFO * chunk_info) ;
+static SF_CHUNK_ITERATOR * wav_next_chunk_iterator (SF_PRIVATE *psf, SF_CHUNK_ITERATOR * iterator) ;
+static int wav_get_chunk_size (SF_PRIVATE *psf, const SF_CHUNK_ITERATOR * iterator, SF_CHUNK_INFO * chunk_info) ;
+static int wav_get_chunk_data (SF_PRIVATE *psf, const SF_CHUNK_ITERATOR * iterator, SF_CHUNK_INFO * chunk_info) ;
+
+/*------------------------------------------------------------------------------
+** Public function.
+*/
+
+int
+wav_open	(SF_PRIVATE *psf)
+{	WAVLIKE_PRIVATE * wpriv ;
+	int	format, subformat, error, blockalign = 0, framesperblock = 0 ;
+
+	if ((wpriv = calloc (1, sizeof (WAVLIKE_PRIVATE))) == NULL)
+		return SFE_MALLOC_FAILED ;
+	psf->container_data = wpriv ;
+
+	wpriv->wavex_ambisonic = SF_AMBISONIC_NONE ;
+	psf->strings.flags = SF_STR_ALLOW_START | SF_STR_ALLOW_END ;
+
+	if (psf->file.mode == SFM_READ || (psf->file.mode == SFM_RDWR && psf->filelength > 0))
+	{	if ((error = wav_read_header (psf, &blockalign, &framesperblock)))
+			return error ;
+
+		psf->next_chunk_iterator = wav_next_chunk_iterator ;
+		psf->get_chunk_size = wav_get_chunk_size ;
+		psf->get_chunk_data = wav_get_chunk_data ;
+		} ;
+
+	subformat = SF_CODEC (psf->sf.format) ;
+
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	if (psf->is_pipe)
+			return SFE_NO_PIPE_WRITE ;
+
+		wpriv->wavex_ambisonic = SF_AMBISONIC_NONE ;
+
+		format = SF_CONTAINER (psf->sf.format) ;
+		if (format != SF_FORMAT_WAV && format != SF_FORMAT_WAVEX)
+			return	SFE_BAD_OPEN_FORMAT ;
+
+		psf->blockwidth = psf->bytewidth * psf->sf.channels ;
+
+		/* RIFF WAVs are little-endian, RIFX WAVs are big-endian, default to little */
+		psf->endian = SF_ENDIAN (psf->sf.format) ;
+		if (CPU_IS_BIG_ENDIAN && psf->endian == SF_ENDIAN_CPU)
+			psf->endian = SF_ENDIAN_BIG ;
+		else if (psf->endian != SF_ENDIAN_BIG)
+			psf->endian = SF_ENDIAN_LITTLE ;
+
+		if (psf->file.mode != SFM_RDWR || psf->filelength < 44)
+		{	psf->filelength = 0 ;
+			psf->datalength = 0 ;
+			psf->dataoffset = 0 ;
+			psf->sf.frames = 0 ;
+			} ;
+
+#if (ENABLE_EXPERIMENTAL_CODE == 0)
+		/* For now, don't support writing MPEGLAYER3 WAVs, as we can't guarentee that
+		** such a file written by libsndfile would have the same length when opened again.
+		*/
+		if (subformat == SF_FORMAT_MPEG_LAYER_III)
+			return SFE_UNSUPPORTED_ENCODING ;
+#endif
+
+		if (subformat == SF_FORMAT_IMA_ADPCM || subformat == SF_FORMAT_MS_ADPCM)
+		{	blockalign = wavlike_srate2blocksize (psf->sf.samplerate * psf->sf.channels) ;
+			framesperblock = -1 ; /* Corrected later. */
+			} ;
+
+		/* By default, add the peak chunk to floating point files. Default behaviour
+		** can be switched off using sf_command (SFC_SET_PEAK_CHUNK, SF_FALSE).
+		*/
+		if (psf->file.mode == SFM_WRITE && (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE))
+		{	if ((psf->peak_info = peak_info_calloc (psf->sf.channels)) == NULL)
+				return SFE_MALLOC_FAILED ;
+			psf->peak_info->peak_loc = SF_PEAK_START ;
+			} ;
+
+		psf->write_header	= wav_write_header ;
+		psf->set_chunk		= wav_set_chunk ;
+		} ;
+
+	psf->container_close = wav_close ;
+	psf->command = wav_command ;
+
+	switch (subformat)
+	{	case SF_FORMAT_PCM_U8 :
+		case SF_FORMAT_PCM_16 :
+		case SF_FORMAT_PCM_24 :
+		case SF_FORMAT_PCM_32 :
+					error = pcm_init (psf) ;
+					break ;
+
+		case SF_FORMAT_ULAW :
+					error = ulaw_init (psf) ;
+					break ;
+
+		case SF_FORMAT_ALAW :
+					error = alaw_init (psf) ;
+					break ;
+
+		/* Lite remove start */
+		case SF_FORMAT_FLOAT :
+					error = float32_init (psf) ;
+					break ;
+
+		case SF_FORMAT_DOUBLE :
+					error = double64_init (psf) ;
+					break ;
+
+		case SF_FORMAT_IMA_ADPCM :
+					error = wavlike_ima_init (psf, blockalign, framesperblock) ;
+					break ;
+
+		case SF_FORMAT_MS_ADPCM :
+					error = wavlike_msadpcm_init (psf, blockalign, framesperblock) ;
+					break ;
+
+		case SF_FORMAT_G721_32 :
+					error = g72x_init (psf) ;
+					break ;
+
+		case SF_FORMAT_NMS_ADPCM_16 :
+		case SF_FORMAT_NMS_ADPCM_24 :
+		case SF_FORMAT_NMS_ADPCM_32 :
+					error = nms_adpcm_init (psf) ;
+					break ;
+
+		/* Lite remove end */
+
+		case SF_FORMAT_GSM610 :
+					error = gsm610_init (psf) ;
+					break ;
+
+		case SF_FORMAT_MPEG_LAYER_III :
+					error = mpeg_init (psf, SF_BITRATE_MODE_CONSTANT, SF_FALSE) ;
+					break ;
+
+		default : 	return SFE_UNIMPLEMENTED ;
+		} ;
+
+	if (psf->file.mode == SFM_WRITE || (psf->file.mode == SFM_RDWR && psf->filelength == 0))
+		return psf->write_header (psf, SF_FALSE) ;
+
+	return error ;
+} /* wav_open */
+
+/*=========================================================================
+** Private functions.
+*/
+
+static int
+wav_read_header	(SF_PRIVATE *psf, int *blockalign, int *framesperblock)
+{	WAVLIKE_PRIVATE	*wpriv ;
+	WAV_FMT		*wav_fmt ;
+	FACT_CHUNK	fact_chunk ;
+	uint32_t	marker, chunk_size = 0, RIFFsize = 0, done = 0 ;
+	int			parsestage = 0, error, format = 0 ;
+
+	if (psf->is_pipe == 0 && psf->filelength > 0xFFFFFFFFLL)
+		psf_log_printf (psf, "Warning : filelength > 0xffffffff. This is bad!!!!\n") ;
+
+	if ((wpriv = psf->container_data) == NULL)
+		return SFE_INTERNAL ;
+	wav_fmt = &wpriv->wav_fmt ;
+
+	/* Set position to start of file to begin reading header. */
+	psf_binheader_readf (psf, "pmj", 0, &marker, -4) ;
+	psf->header.indx = 0 ;
+
+	/* RIFX signifies big-endian format for all header and data  to prevent
+	** lots of code copying here, we'll set the psf->rwf_endian flag once here,
+	** and never specify endian-ness for all other header ops/
+	*/
+	psf->rwf_endian = (marker == RIFF_MARKER) ? SF_ENDIAN_LITTLE : SF_ENDIAN_BIG ;
+
+	while (! done)
+	{	size_t jump = chunk_size & 1 ;
+
+		marker = chunk_size = 0 ;
+		psf_binheader_readf (psf, "jm4", jump, &marker, &chunk_size) ;
+		if (marker == 0)
+		{	sf_count_t pos = psf_ftell (psf) ;
+			psf_log_printf (psf, "Have 0 marker at position %D (0x%x).\n", pos, pos) ;
+			break ;
+			} ;
+
+		psf_store_read_chunk_u32 (&psf->rchunks, marker, psf_ftell (psf), chunk_size) ;
+
+		switch (marker)
+		{	case RIFF_MARKER :
+			case RIFX_MARKER :
+					if (parsestage)
+						return SFE_WAV_NO_RIFF ;
+
+					parsestage |= HAVE_RIFF ;
+
+					RIFFsize = chunk_size ;
+
+					if (psf->fileoffset > 0 && psf->filelength > RIFFsize + 8)
+					{	/* Set file length. */
+						psf->filelength = RIFFsize + 8 ;
+						if (marker == RIFF_MARKER)
+							psf_log_printf (psf, "RIFF : %u\n", RIFFsize) ;
+						else
+							psf_log_printf (psf, "RIFX : %u\n", RIFFsize) ;
+						}
+					else if (psf->filelength < RIFFsize + 2 * SIGNED_SIZEOF (marker))
+					{	if (marker == RIFF_MARKER)
+							psf_log_printf (psf, "RIFF : %u (should be %D)\n", RIFFsize, psf->filelength - 2 * SIGNED_SIZEOF (marker)) ;
+						else
+							psf_log_printf (psf, "RIFX : %u (should be %D)\n", RIFFsize, psf->filelength - 2 * SIGNED_SIZEOF (marker)) ;
+
+						RIFFsize = psf->filelength - 2 * SIGNED_SIZEOF (RIFFsize) ;
+						}
+					else
+					{	if (marker == RIFF_MARKER)
+							psf_log_printf (psf, "RIFF : %u\n", RIFFsize) ;
+						else
+							psf_log_printf (psf, "RIFX : %u\n", RIFFsize) ;
+					} ;
+
+					psf_binheader_readf (psf, "m", &marker) ;
+					if (marker != WAVE_MARKER)
+						return SFE_WAV_NO_WAVE ;
+					parsestage |= HAVE_WAVE ;
+					psf_log_printf (psf, "WAVE\n") ;
+					chunk_size = 0 ;
+					break ;
+
+			case fmt_MARKER :
+					if ((parsestage & (HAVE_RIFF | HAVE_WAVE)) != (HAVE_RIFF | HAVE_WAVE))
+						return SFE_WAV_NO_FMT ;
+
+					/* If this file has a SECOND fmt chunk, I don't want to know about it. */
+					if (parsestage & HAVE_fmt)
+						break ;
+
+					parsestage |= HAVE_fmt ;
+
+					psf_log_printf (psf, "fmt  : %d\n", chunk_size) ;
+
+					if ((error = wavlike_read_fmt_chunk (psf, chunk_size)))
+						return error ;
+
+					format = wav_fmt->format ;
+					break ;
+
+			case data_MARKER :
+					if ((parsestage & (HAVE_RIFF | HAVE_WAVE | HAVE_fmt)) != (HAVE_RIFF | HAVE_WAVE | HAVE_fmt))
+						return SFE_WAV_NO_DATA ;
+
+					if (psf->file.mode == SFM_RDWR && (parsestage & HAVE_other) != 0)
+						return SFE_RDWR_BAD_HEADER ;
+
+					parsestage |= HAVE_data ;
+
+					psf->datalength = chunk_size ;
+					if (psf->datalength & 1)
+						psf_log_printf (psf, "*** 'data' chunk should be an even number of bytes in length.\n") ;
+
+					psf->dataoffset = psf_ftell (psf) ;
+
+					if (psf->dataoffset > 0)
+					{	if (chunk_size == 0 && RIFFsize == 8 && psf->filelength > 44)
+						{	psf_log_printf (psf, "*** Looks like a WAV file which wasn't closed properly. Fixing it.\n") ;
+							psf->datalength = psf->filelength - psf->dataoffset ;
+							} ;
+
+						if (psf->datalength > psf->filelength - psf->dataoffset)
+						{	psf_log_printf (psf, "data : %D (should be %D)\n", psf->datalength, psf->filelength - psf->dataoffset) ;
+							psf->datalength = psf->filelength - psf->dataoffset ;
+							}
+						else
+							psf_log_printf (psf, "data : %D\n", psf->datalength) ;
+
+						/* Only set dataend if there really is data at the end. */
+						if (psf->datalength + psf->dataoffset < psf->filelength)
+							psf->dataend = psf->datalength + psf->dataoffset ;
+
+						psf->datalength += chunk_size & 1 ;
+						chunk_size = 0 ;
+						} ;
+
+					if (! psf->sf.seekable || psf->dataoffset < 0)
+						break ;
+
+					/* Seek past data and continue reading header. */
+					psf_fseek (psf, psf->datalength, SEEK_CUR) ;
+
+					if (psf_ftell (psf) != psf->datalength + psf->dataoffset)
+						psf_log_printf (psf, "*** psf_fseek past end error ***\n") ;
+					break ;
+
+			case fact_MARKER :
+					if ((parsestage & (HAVE_RIFF | HAVE_WAVE)) != (HAVE_RIFF | HAVE_WAVE))
+						return SFE_WAV_BAD_FACT ;
+
+					parsestage |= HAVE_fact ;
+
+					if ((parsestage & HAVE_fmt) != HAVE_fmt)
+						psf_log_printf (psf, "*** Should have 'fmt ' chunk before 'fact'\n") ;
+
+					psf_binheader_readf (psf, "4", & (fact_chunk.frames)) ;
+
+					if (chunk_size > SIGNED_SIZEOF (fact_chunk))
+						psf_binheader_readf (psf, "j", (int) (chunk_size - SIGNED_SIZEOF (fact_chunk))) ;
+
+					if (chunk_size)
+						psf_log_printf (psf, "%M : %u\n", marker, chunk_size) ;
+					else
+						psf_log_printf (psf, "%M : %u (should not be zero)\n", marker, chunk_size) ;
+
+					psf_log_printf (psf, "  frames  : %d\n", fact_chunk.frames) ;
+					break ;
+
+			case PEAK_MARKER :
+					if ((parsestage & (HAVE_RIFF | HAVE_WAVE | HAVE_fmt)) != (HAVE_RIFF | HAVE_WAVE | HAVE_fmt))
+						return SFE_WAV_PEAK_B4_FMT ;
+
+					parsestage |= HAVE_PEAK ;
+
+					psf_log_printf (psf, "%M : %u\n", marker, chunk_size) ;
+					if ((error = wavlike_read_peak_chunk (psf, chunk_size)) != 0)
+						return error ;
+					psf->peak_info->peak_loc = ((parsestage & HAVE_data) == 0) ? SF_PEAK_START : SF_PEAK_END ;
+					break ;
+
+			case cue_MARKER :
+					parsestage |= HAVE_other ;
+
+					{	uint32_t thisread, bytesread, cue_count, position, offset ;
+						int id, chunk_id, chunk_start, block_start, cue_index ;
+
+						bytesread = psf_binheader_readf (psf, "4", &cue_count) ;
+						psf_log_printf (psf, "%M : %u\n", marker, chunk_size) ;
+
+						if (cue_count > 2500) /* 2500 is close to the largest number of cues possible because of block sizes */
+						{	psf_log_printf (psf, "  Count : %u (skipping)\n", cue_count) ;
+							psf_binheader_readf (psf, "j", chunk_size - bytesread) ;
+							break ;
+							} ;
+
+						psf_log_printf (psf, "  Count : %d\n", cue_count) ;
+
+						if (psf->cues)
+						{	free (psf->cues) ;
+							psf->cues = NULL ;
+							} ;
+
+						if ((psf->cues = psf_cues_alloc (cue_count)) == NULL)
+							return SFE_MALLOC_FAILED ;
+
+						cue_index = 0 ;
+
+						while (cue_count)
+						{
+							if ((thisread = psf_binheader_readf (psf, "e44m444", &id, &position, &chunk_id, &chunk_start, &block_start, &offset)) == 0)
+								break ;
+							bytesread += thisread ;
+
+							if (cue_index < 10) /* avoid swamping log buffer with cues */
+								psf_log_printf (psf,	"   Cue ID : %2d"
+											"  Pos : %5u  Chunk : %M"
+											"  Chk Start : %d  Blk Start : %d"
+											"  Offset : %5d\n",
+										id, position, chunk_id, chunk_start, block_start, offset) ;
+							else if (cue_index == 10)
+								psf_log_printf (psf,	"   (Skipping)\n") ;
+
+							psf->cues->cue_points [cue_index].indx = id ;
+							psf->cues->cue_points [cue_index].position = position ;
+							psf->cues->cue_points [cue_index].fcc_chunk = chunk_id ;
+							psf->cues->cue_points [cue_index].chunk_start = chunk_start ;
+							psf->cues->cue_points [cue_index].block_start = block_start ;
+							psf->cues->cue_points [cue_index].sample_offset = offset ;
+							psf->cues->cue_points [cue_index].name [0] = '\0' ;
+							cue_count -- ;
+							cue_index ++ ;
+							} ;
+
+						if (bytesread != chunk_size)
+						{	psf_log_printf (psf, "**** Chunk size weirdness (%d != %d)\n", chunk_size, bytesread) ;
+							psf_binheader_readf (psf, "j", chunk_size - bytesread) ;
+							} ;
+						} ;
+					break ;
+
+			case smpl_MARKER :
+					parsestage |= HAVE_other ;
+
+					psf_log_printf (psf, "smpl : %u\n", chunk_size) ;
+
+					if ((error = wav_read_smpl_chunk (psf, chunk_size)))
+						return error ;
+					break ;
+
+			case acid_MARKER :
+					parsestage |= HAVE_other ;
+
+					psf_log_printf (psf, "acid : %u\n", chunk_size) ;
+
+					if ((error = wav_read_acid_chunk (psf, chunk_size)))
+						return error ;
+					break ;
+
+			case INFO_MARKER :
+			case LIST_MARKER :
+					parsestage |= HAVE_other ;
+
+					if ((error = wavlike_subchunk_parse (psf, marker, chunk_size)) != 0)
+						return error ;
+					break ;
+
+			case bext_MARKER :
+					/*
+					The 'bext' chunk can actually be updated, so don't need to set this.
+					parsestage |= HAVE_other ;
+					*/
+					if ((error = wavlike_read_bext_chunk (psf, chunk_size)))
+						return error ;
+					break ;
+
+			case PAD_MARKER :
+					/*
+					We can eat into a 'PAD ' chunk if we need to.
+					parsestage |= HAVE_other ;
+					*/
+					psf_log_printf (psf, "%M : %u\n", marker, chunk_size) ;
+					psf_binheader_readf (psf, "j", chunk_size) ;
+					break ;
+
+			case cart_MARKER:
+					if ((error = wavlike_read_cart_chunk (psf, chunk_size)))
+						return error ;
+					break ;
+
+			case iXML_MARKER : /* See http://en.wikipedia.org/wiki/IXML */
+			case strc_MARKER : /* Multiple of 32 bytes. */
+			case afsp_MARKER :
+			case clm_MARKER :
+			case elmo_MARKER :
+			case levl_MARKER :
+			case plst_MARKER :
+			case minf_MARKER :
+			case elm1_MARKER :
+			case regn_MARKER :
+			case ovwf_MARKER :
+			case inst_MARKER :
+			case AFAn_MARKER :
+			case umid_MARKER :
+			case SyLp_MARKER :
+			case Cr8r_MARKER :
+			case JUNK_MARKER :
+			case PMX_MARKER :
+			case DISP_MARKER :
+			case MEXT_MARKER :
+			case FLLR_MARKER :
+					psf_log_printf (psf, "%M : %u\n", marker, chunk_size) ;
+					psf_binheader_readf (psf, "j", chunk_size) ;
+					break ;
+
+			default :
+					if (chunk_size >= 0xffff0000)
+					{	done = SF_TRUE ;
+						psf_log_printf (psf, "*** Unknown chunk marker (%X) at position %D with length %u. Exiting parser.\n", marker, psf_ftell (psf) - 8, chunk_size) ;
+						break ;
+						} ;
+
+					if ((marker & TAG__MARKER_MASK) == TAG__MARKER &&
+						psf_ftell (psf) - 8 + 128 == psf->filelength)
+					{	psf_log_printf (psf, "*** Hit ID3v1 trailer. Exiting parser.\n") ;
+						chunk_size = 128 ;
+						done = SF_TRUE ;
+						parsestage |= HAVE_other ;
+						break ;
+						} ;
+
+					if (psf_isprint ((marker >> 24) & 0xFF) && psf_isprint ((marker >> 16) & 0xFF)
+						&& psf_isprint ((marker >> 8) & 0xFF) && psf_isprint (marker & 0xFF))
+					{	psf_log_printf (psf, "*** %M : %u (unknown marker)\n", marker, chunk_size) ;
+						psf_binheader_readf (psf, "j", chunk_size) ;
+						break ;
+						} ;
+					if (psf_ftell (psf) & 0x03)
+					{	psf_log_printf (psf, "  Unknown chunk marker at position %D. Resynching.\n", psf_ftell (psf) - 8) ;
+						psf_binheader_readf (psf, "j", -3) ;
+						/* File is too messed up so we prevent editing in RDWR mode here. */
+						parsestage |= HAVE_other ;
+						break ;
+						} ;
+					psf_log_printf (psf, "*** Unknown chunk marker (%X) at position %D. Exiting parser.\n", marker, psf_ftell (psf) - 8) ;
+					done = SF_TRUE ;
+					break ;
+			} ;	/* switch (marker) */
+
+		if (chunk_size >= psf->filelength)
+		{	psf_log_printf (psf, "*** Chunk size %u > file length %D. Exiting parser.\n", chunk_size, psf->filelength) ;
+			break ;
+			} ;
+
+		if (! psf->sf.seekable && (parsestage & HAVE_data))
+			break ;
+
+		if (psf_ftell (psf) >= psf->filelength - SIGNED_SIZEOF (chunk_size))
+		{	psf_log_printf (psf, "End\n") ;
+			break ;
+			} ;
+		} ; /* while (1) */
+
+	if (psf->dataoffset <= 0)
+		return SFE_WAV_NO_DATA ;
+
+	if (psf->sf.channels < 1)
+		return SFE_CHANNEL_COUNT_ZERO ;
+
+	if (psf->sf.channels > SF_MAX_CHANNELS)
+		return SFE_CHANNEL_COUNT ;
+
+	if (format != WAVE_FORMAT_PCM && (parsestage & HAVE_fact) == 0)
+		psf_log_printf (psf, "**** All non-PCM format files should have a 'fact' chunk.\n") ;
+
+	/* WAVs can be little or big endian */
+	psf->endian = psf->rwf_endian ;
+
+	psf_fseek (psf, psf->dataoffset, SEEK_SET) ;
+
+	if (psf->is_pipe == 0)
+	{	/*
+		** Check for 'wvpk' at the start of the DATA section. Not able to
+		** handle this.
+		*/
+		psf_binheader_readf (psf, "4", &marker) ;
+		if (marker == wvpk_MARKER || marker == OggS_MARKER)
+			return SFE_WAV_WVPK_DATA ;
+		} ;
+
+	/* Seek to start of DATA section. */
+	psf_fseek (psf, psf->dataoffset, SEEK_SET) ;
+
+	if (psf->blockwidth)
+	{	if (psf->filelength - psf->dataoffset < psf->datalength)
+			psf->sf.frames = (psf->filelength - psf->dataoffset) / psf->blockwidth ;
+		else
+			psf->sf.frames = psf->datalength / psf->blockwidth ;
+		} ;
+
+	switch (format)
+	{	case WAVE_FORMAT_EXTENSIBLE :
+			if (psf->sf.format == (SF_FORMAT_WAVEX | SF_FORMAT_MS_ADPCM))
+			{	*blockalign = wav_fmt->msadpcm.blockalign ;
+				*framesperblock = wav_fmt->msadpcm.samplesperblock ;
+				} ;
+			break ;
+
+		case WAVE_FORMAT_NMS_VBXADPCM :
+			switch (wav_fmt->min.bitwidth)
+			{	case 2 :
+					psf->sf.format = SF_FORMAT_WAV | SF_FORMAT_NMS_ADPCM_16 ;
+					break ;
+				case 3 :
+					psf->sf.format = SF_FORMAT_WAV | SF_FORMAT_NMS_ADPCM_24 ;
+					break ;
+				case 4 :
+					psf->sf.format = SF_FORMAT_WAV | SF_FORMAT_NMS_ADPCM_32 ;
+					break ;
+
+				default :
+					return SFE_UNIMPLEMENTED ;
+				}
+				break ;
+
+		case WAVE_FORMAT_PCM :
+					psf->sf.format = SF_FORMAT_WAV | u_bitwidth_to_subformat (psf->bytewidth * 8) ;
+					break ;
+
+		case WAVE_FORMAT_MULAW :
+		case IBM_FORMAT_MULAW :
+					psf->sf.format = (SF_FORMAT_WAV | SF_FORMAT_ULAW) ;
+					break ;
+
+		case WAVE_FORMAT_ALAW :
+		case IBM_FORMAT_ALAW :
+					psf->sf.format = (SF_FORMAT_WAV | SF_FORMAT_ALAW) ;
+					break ;
+
+		case WAVE_FORMAT_MS_ADPCM :
+					psf->sf.format = (SF_FORMAT_WAV | SF_FORMAT_MS_ADPCM) ;
+					*blockalign = wav_fmt->msadpcm.blockalign ;
+					*framesperblock = wav_fmt->msadpcm.samplesperblock ;
+					break ;
+
+		case WAVE_FORMAT_IMA_ADPCM :
+					psf->sf.format = (SF_FORMAT_WAV | SF_FORMAT_IMA_ADPCM) ;
+					*blockalign = wav_fmt->ima.blockalign ;
+					*framesperblock = wav_fmt->ima.samplesperblock ;
+					break ;
+
+		case WAVE_FORMAT_GSM610 :
+					psf->sf.format = (SF_FORMAT_WAV | SF_FORMAT_GSM610) ;
+					break ;
+
+		case WAVE_FORMAT_IEEE_FLOAT :
+					psf->sf.format = SF_FORMAT_WAV ;
+					psf->sf.format |= (psf->bytewidth == 8) ? SF_FORMAT_DOUBLE : SF_FORMAT_FLOAT ;
+					break ;
+
+		case WAVE_FORMAT_G721_ADPCM :
+					psf->sf.format = SF_FORMAT_WAV | SF_FORMAT_G721_32 ;
+					break ;
+
+		case WAVE_FORMAT_MPEGLAYER3 :
+					psf->sf.format = SF_FORMAT_WAV | SF_FORMAT_MPEG_LAYER_III ;
+					if (parsestage & HAVE_fact)
+						psf->sf.frames = fact_chunk.frames ;
+					break ;
+
+		default : return SFE_UNIMPLEMENTED ;
+		} ;
+
+	if (wpriv->fmt_is_broken)
+		wavlike_analyze (psf) ;
+
+	/* Only set the format endian-ness if its non-standard big-endian. */
+	if (psf->endian == SF_ENDIAN_BIG)
+		psf->sf.format |= SF_ENDIAN_BIG ;
+
+	return 0 ;
+} /* wav_read_header */
+
+static int
+wav_write_fmt_chunk (SF_PRIVATE *psf)
+{	int subformat, fmt_size, add_fact_chunk = 0 ;
+
+	subformat = SF_CODEC (psf->sf.format) ;
+
+	switch (subformat)
+	{	case SF_FORMAT_PCM_U8 :
+		case SF_FORMAT_PCM_16 :
+		case SF_FORMAT_PCM_24 :
+		case SF_FORMAT_PCM_32 :
+					fmt_size = 2 + 2 + 4 + 4 + 2 + 2 ;
+
+					/* fmt : format, channels, samplerate */
+					psf_binheader_writef (psf, "4224", BHW4 (fmt_size), BHW2 (WAVE_FORMAT_PCM), BHW2 (psf->sf.channels), BHW4 (psf->sf.samplerate)) ;
+					/*  fmt : bytespersec */
+					psf_binheader_writef (psf, "4", BHW4 (psf->sf.samplerate * psf->bytewidth * psf->sf.channels)) ;
+					/*  fmt : blockalign, bitwidth */
+					psf_binheader_writef (psf, "22", BHW2 (psf->bytewidth * psf->sf.channels), BHW2 (psf->bytewidth * 8)) ;
+					break ;
+
+		case SF_FORMAT_FLOAT :
+		case SF_FORMAT_DOUBLE :
+					fmt_size = 2 + 2 + 4 + 4 + 2 + 2 ;
+
+					/* fmt : format, channels, samplerate */
+					psf_binheader_writef (psf, "4224", BHW4 (fmt_size), BHW2 (WAVE_FORMAT_IEEE_FLOAT), BHW2 (psf->sf.channels), BHW4 (psf->sf.samplerate)) ;
+					/*  fmt : bytespersec */
+					psf_binheader_writef (psf, "4", BHW4 (psf->sf.samplerate * psf->bytewidth * psf->sf.channels)) ;
+					/*  fmt : blockalign, bitwidth */
+					psf_binheader_writef (psf, "22", BHW2 (psf->bytewidth * psf->sf.channels), BHW2 (psf->bytewidth * 8)) ;
+
+					add_fact_chunk = SF_TRUE ;
+					break ;
+
+		case SF_FORMAT_ULAW :
+					fmt_size = 2 + 2 + 4 + 4 + 2 + 2 + 2 ;
+
+					/* fmt : format, channels, samplerate */
+					psf_binheader_writef (psf, "4224", BHW4 (fmt_size), BHW2 (WAVE_FORMAT_MULAW), BHW2 (psf->sf.channels), BHW4 (psf->sf.samplerate)) ;
+					/*  fmt : bytespersec */
+					psf_binheader_writef (psf, "4", BHW4 (psf->sf.samplerate * psf->bytewidth * psf->sf.channels)) ;
+					/*  fmt : blockalign, bitwidth, extrabytes */
+					psf_binheader_writef (psf, "222", BHW2 (psf->bytewidth * psf->sf.channels), BHW2 (8), BHW2 (0)) ;
+
+					add_fact_chunk = SF_TRUE ;
+					break ;
+
+		case SF_FORMAT_ALAW :
+					fmt_size = 2 + 2 + 4 + 4 + 2 + 2 + 2 ;
+
+					/* fmt : format, channels, samplerate */
+					psf_binheader_writef (psf, "4224", BHW4 (fmt_size), BHW2 (WAVE_FORMAT_ALAW), BHW2 (psf->sf.channels), BHW4 (psf->sf.samplerate)) ;
+					/*  fmt : bytespersec */
+					psf_binheader_writef (psf, "4", BHW4 (psf->sf.samplerate * psf->bytewidth * psf->sf.channels)) ;
+					/*  fmt : blockalign, bitwidth, extrabytes */
+					psf_binheader_writef (psf, "222", BHW2 (psf->bytewidth * psf->sf.channels), BHW2 (8), BHW2 (0)) ;
+
+					add_fact_chunk = SF_TRUE ;
+					break ;
+
+		/* Lite remove start */
+		case SF_FORMAT_IMA_ADPCM :
+					{	int blockalign, framesperblock, bytespersec ;
+
+						blockalign		= wavlike_srate2blocksize (psf->sf.samplerate * psf->sf.channels) ;
+						framesperblock	= 2 * (blockalign - 4 * psf->sf.channels) / psf->sf.channels + 1 ;
+						bytespersec		= (psf->sf.samplerate * blockalign) / framesperblock ;
+
+						/* fmt chunk. */
+						fmt_size = 2 + 2 + 4 + 4 + 2 + 2 + 2 + 2 ;
+
+						/* fmt : size, WAV format type, channels, samplerate, bytespersec */
+						psf_binheader_writef (psf, "42244", BHW4 (fmt_size), BHW2 (WAVE_FORMAT_IMA_ADPCM),
+									BHW2 (psf->sf.channels), BHW4 (psf->sf.samplerate), BHW4 (bytespersec)) ;
+
+						/* fmt : blockalign, bitwidth, extrabytes, framesperblock. */
+						psf_binheader_writef (psf, "2222", BHW2 (blockalign), BHW2 (4), BHW2 (2), BHW2 (framesperblock)) ;
+						} ;
+
+					add_fact_chunk = SF_TRUE ;
+					break ;
+
+		case SF_FORMAT_MS_ADPCM :
+					{	int	blockalign, framesperblock, bytespersec, extrabytes ;
+
+						blockalign		= wavlike_srate2blocksize (psf->sf.samplerate * psf->sf.channels) ;
+						framesperblock	= 2 + 2 * (blockalign - 7 * psf->sf.channels) / psf->sf.channels ;
+						bytespersec		= (psf->sf.samplerate * blockalign) / framesperblock ;
+
+						/* fmt chunk. */
+						extrabytes	= 2 + 2 + WAVLIKE_MSADPCM_ADAPT_COEFF_COUNT * (2 + 2) ;
+						fmt_size	= 2 + 2 + 4 + 4 + 2 + 2 + 2 + extrabytes ;
+
+						/* fmt : size, WAV format type, channels. */
+						psf_binheader_writef (psf, "422", BHW4 (fmt_size), BHW2 (WAVE_FORMAT_MS_ADPCM), BHW2 (psf->sf.channels)) ;
+
+						/* fmt : samplerate, bytespersec. */
+						psf_binheader_writef (psf, "44", BHW4 (psf->sf.samplerate), BHW4 (bytespersec)) ;
+
+						/* fmt : blockalign, bitwidth, extrabytes, framesperblock. */
+						psf_binheader_writef (psf, "22222", BHW2 (blockalign), BHW2 (4), BHW2 (extrabytes), BHW2 (framesperblock), BHW2 (7)) ;
+
+						wavlike_msadpcm_write_adapt_coeffs (psf) ;
+						} ;
+
+					add_fact_chunk = SF_TRUE ;
+					break ;
+
+
+		case SF_FORMAT_G721_32 :
+					/* fmt chunk. */
+					fmt_size = 2 + 2 + 4 + 4 + 2 + 2 + 2 + 2 ;
+
+					/* fmt : size, WAV format type, channels, samplerate, bytespersec */
+					psf_binheader_writef (psf, "42244", BHW4 (fmt_size), BHW2 (WAVE_FORMAT_G721_ADPCM),
+								BHW2 (psf->sf.channels), BHW4 (psf->sf.samplerate), BHW4 (psf->sf.samplerate * psf->sf.channels / 2)) ;
+
+					/* fmt : blockalign, bitwidth, extrabytes, auxblocksize. */
+					psf_binheader_writef (psf, "2222", BHW2 (64), BHW2 (4), BHW2 (2), BHW2 (0)) ;
+
+					add_fact_chunk = SF_TRUE ;
+					break ;
+
+		case SF_FORMAT_NMS_ADPCM_16 :
+		case SF_FORMAT_NMS_ADPCM_24 :
+		case SF_FORMAT_NMS_ADPCM_32 :
+					{	int bytespersec, blockalign, bitwidth ;
+
+						bitwidth 	= subformat == SF_FORMAT_NMS_ADPCM_16 ? 2 : subformat == SF_FORMAT_NMS_ADPCM_24 ? 3 : 4 ;
+						blockalign 	= 20 * bitwidth + 2 ;
+						bytespersec	= psf->sf.samplerate * blockalign / 160 ;
+
+						/* fmt chunk. */
+						fmt_size = 2 + 2 + 4 + 4 + 2 + 2 ;
+
+						/* fmt : format, channels, samplerate */
+						psf_binheader_writef (psf, "4224", BHW4 (fmt_size), BHW2 (WAVE_FORMAT_NMS_VBXADPCM),
+									BHW2 (psf->sf.channels), BHW4 (psf->sf.samplerate)) ;
+						/*  fmt : bytespersec, blockalign, bitwidth */
+						psf_binheader_writef (psf, "422", BHW4 (bytespersec), BHW2 (blockalign), BHW2 (bitwidth)) ;
+
+						add_fact_chunk = SF_TRUE ;
+						break ;
+						}
+
+		/* Lite remove end */
+
+		case SF_FORMAT_GSM610 :
+					{	int	blockalign, framesperblock, bytespersec ;
+
+						blockalign		= WAVLIKE_GSM610_BLOCKSIZE ;
+						framesperblock	= WAVLIKE_GSM610_SAMPLES ;
+						bytespersec		= (psf->sf.samplerate * blockalign) / framesperblock ;
+
+						/* fmt chunk. */
+						fmt_size = 2 + 2 + 4 + 4 + 2 + 2 + 2 + 2 ;
+
+						/* fmt : size, WAV format type, channels. */
+						psf_binheader_writef (psf, "422", BHW4 (fmt_size), BHW2 (WAVE_FORMAT_GSM610), BHW2 (psf->sf.channels)) ;
+
+						/* fmt : samplerate, bytespersec. */
+						psf_binheader_writef (psf, "44", BHW4 (psf->sf.samplerate), BHW4 (bytespersec)) ;
+
+						/* fmt : blockalign, bitwidth, extrabytes, framesperblock. */
+						psf_binheader_writef (psf, "2222", BHW2 (blockalign), BHW2 (0), BHW2 (2), BHW2 (framesperblock)) ;
+						} ;
+
+					add_fact_chunk = SF_TRUE ;
+					break ;
+
+#if (ENABLE_EXPERIMENTAL_CODE == 0)
+		case SF_FORMAT_MPEG_LAYER_III :
+					{	int bytespersec, blockalign, flags, blocksize, samplesperblock, codecdelay ;
+
+						/* Intended to be set as the average sample rate.
+						** TODO: Maybe re-write this on close with final average
+						** byterate? */
+						bytespersec		= psf->byterate (psf) ;
+
+						/* Average block size. Info only I think. */
+						blocksize		= (1152 * bytespersec) / psf->sf.samplerate ;
+
+						/* Can be set to block size IFF the block size is
+						** constant, set to 1 otherwise. Constant sized
+						** MPEG block streams are uncommon (CBR @ 32kHz and
+						** 48kHz only. Meh. */
+						blockalign		= 1 ;
+
+						/* TODO: Only flags defined are padding-type. I /think/
+						** Lame does ISO style padding by default, which has a
+						** flag value of 0.
+						*/
+						flags			= 0 ;
+
+						/* Should only vary per MPEG 1.0/2.0 vs '2.5'.
+						** TODO: Move this out to MPEG specific place? */
+						samplesperblock	= psf->sf.samplerate >= 32000 ? 1152 : 576 ;
+
+						/* Set as 0 if unknown.
+						** TODO: Plumb this cleanly from Lame.
+						*/
+						codecdelay		= 0 ;
+
+						/* fmt chunk. */
+						fmt_size = 2 + 2 + 4 + 4 + 2 + 2 + 2 + 2 + 4 + 2 + 2 + 2 ;
+
+						/* fmt : size, WAV format type, channels. */
+						psf_binheader_writef (psf, "422", BHW4 (fmt_size), BHW2 (WAVE_FORMAT_MPEGLAYER3), BHW2 (psf->sf.channels)) ;
+
+						/* fmt : samplerate, bytespersec. */
+						psf_binheader_writef (psf, "44", BHW4 (psf->sf.samplerate), BHW4 (bytespersec)) ;
+
+						/* fmt : blockalign, bitwidth, extrabytes, id. */
+						psf_binheader_writef (psf, "2222", BHW2 (blockalign), BHW2 (0), BHW2 (12), BHW2 (1)) ;
+
+						/* fmt : flags, blocksize, samplesperblock, codecdelay */
+						psf_binheader_writef (psf, "4222", BHW4 (flags), BHW2 (blocksize), BHW2 (samplesperblock), BHW2 (codecdelay)) ;
+						} ;
+
+					add_fact_chunk = SF_TRUE ;
+					break ;
+#endif
+
+		default : 	return SFE_UNIMPLEMENTED ;
+		} ;
+
+	if (add_fact_chunk)
+		psf_binheader_writef (psf, "tm48", BHWm (fact_MARKER), BHW4 (4), BHW8 (psf->sf.frames)) ;
+
+	return 0 ;
+} /* wav_write_fmt_chunk */
+
+static int
+wavex_write_fmt_chunk (SF_PRIVATE *psf)
+{	WAVLIKE_PRIVATE	*wpriv ;
+	int subformat, fmt_size ;
+
+	if ((wpriv = psf->container_data) == NULL)
+		return SFE_INTERNAL ;
+
+	subformat = SF_CODEC (psf->sf.format) ;
+
+	/* initial section (same for all, it appears) */
+	switch (subformat)
+	{	case SF_FORMAT_PCM_U8 :
+		case SF_FORMAT_PCM_16 :
+		case SF_FORMAT_PCM_24 :
+		case SF_FORMAT_PCM_32 :
+		case SF_FORMAT_FLOAT :
+		case SF_FORMAT_DOUBLE :
+		case SF_FORMAT_ULAW :
+		case SF_FORMAT_ALAW :
+			fmt_size = 2 + 2 + 4 + 4 + 2 + 2 + 2 + 2 + 4 + 4 + 2 + 2 + 8 ;
+
+			/* fmt : format, channels, samplerate */
+			psf_binheader_writef (psf, "4224", BHW4 (fmt_size), BHW2 (WAVE_FORMAT_EXTENSIBLE), BHW2 (psf->sf.channels), BHW4 (psf->sf.samplerate)) ;
+			/*  fmt : bytespersec */
+			psf_binheader_writef (psf, "4", BHW4 (psf->sf.samplerate * psf->bytewidth * psf->sf.channels)) ;
+			/*  fmt : blockalign, bitwidth */
+			psf_binheader_writef (psf, "22", BHW2 (psf->bytewidth * psf->sf.channels), BHW2 (psf->bytewidth * 8)) ;
+
+			/* cbSize 22 is sizeof (WAVEFORMATEXTENSIBLE) - sizeof (WAVEFORMATEX) */
+			psf_binheader_writef (psf, "2", BHW2 (22)) ;
+
+			/* wValidBitsPerSample, for our use same as bitwidth as we use it fully */
+			psf_binheader_writef (psf, "2", BHW2 (psf->bytewidth * 8)) ;
+
+			/* For an Ambisonic file set the channel mask to zero.
+			** Otherwise use a default based on the channel count.
+			*/
+			if (wpriv->wavex_ambisonic != SF_AMBISONIC_NONE)
+				psf_binheader_writef (psf, "4", BHW4 (0)) ;
+			else if (wpriv->wavex_channelmask != 0)
+				psf_binheader_writef (psf, "4", BHW4 (wpriv->wavex_channelmask)) ;
+			else
+			{	/*
+				** Ok some liberty is taken here to use the most commonly used channel masks
+				** instead of "no mapping". If you really want to use "no mapping" for 8 channels and less
+				** please don't use wavex. (otherwise we'll have to create a new SF_COMMAND)
+				*/
+				switch (psf->sf.channels)
+				{	case 1 :	/* center channel mono */
+						psf_binheader_writef (psf, "4", BHW4 (0x4)) ;
+						break ;
+
+					case 2 :	/* front left and right */
+						psf_binheader_writef (psf, "4", BHW4 (0x1 | 0x2)) ;
+						break ;
+
+					case 4 :	/* Quad */
+						psf_binheader_writef (psf, "4", BHW4 (0x1 | 0x2 | 0x10 | 0x20)) ;
+						break ;
+
+					case 6 :	/* 5.1 */
+						psf_binheader_writef (psf, "4", BHW4 (0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20)) ;
+						break ;
+
+					case 8 :	/* 7.1 */
+						psf_binheader_writef (psf, "4", BHW4 (0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20 | 0x40 | 0x80)) ;
+						break ;
+
+					default :	/* 0 when in doubt , use direct out, ie NO mapping*/
+						psf_binheader_writef (psf, "4", BHW4 (0x0)) ;
+						break ;
+					} ;
+				} ;
+			break ;
+
+		case SF_FORMAT_MS_ADPCM : /* Todo, GUID exists might have different header as per wav_write_header */
+		default :
+			return SFE_UNIMPLEMENTED ;
+		} ;
+
+	/* GUID section, different for each */
+
+	switch (subformat)
+	{	case SF_FORMAT_PCM_U8 :
+		case SF_FORMAT_PCM_16 :
+		case SF_FORMAT_PCM_24 :
+		case SF_FORMAT_PCM_32 :
+			wavlike_write_guid (psf, wpriv->wavex_ambisonic == SF_AMBISONIC_NONE ?
+						&MSGUID_SUBTYPE_PCM : &MSGUID_SUBTYPE_AMBISONIC_B_FORMAT_PCM) ;
+			break ;
+
+		case SF_FORMAT_FLOAT :
+		case SF_FORMAT_DOUBLE :
+			wavlike_write_guid (psf, wpriv->wavex_ambisonic == SF_AMBISONIC_NONE ?
+						&MSGUID_SUBTYPE_IEEE_FLOAT : &MSGUID_SUBTYPE_AMBISONIC_B_FORMAT_IEEE_FLOAT) ;
+			break ;
+
+		case SF_FORMAT_ULAW :
+			wavlike_write_guid (psf, &MSGUID_SUBTYPE_MULAW) ;
+			break ;
+
+		case SF_FORMAT_ALAW :
+			wavlike_write_guid (psf, &MSGUID_SUBTYPE_ALAW) ;
+			break ;
+
+#if 0
+		/* This is dead code due to return in previous switch statement. */
+		case SF_FORMAT_MS_ADPCM : /* todo, GUID exists */
+			wavlike_write_guid (psf, &MSGUID_SUBTYPE_MS_ADPCM) ;
+			break ;
+			return SFE_UNIMPLEMENTED ;
+#endif
+
+		default : return SFE_UNIMPLEMENTED ;
+		} ;
+
+	psf_binheader_writef (psf, "tm48", BHWm (fact_MARKER), BHW4 (4), BHW8 (psf->sf.frames)) ;
+
+	return 0 ;
+} /* wavex_write_fmt_chunk */
+
+
+static int
+wav_write_header (SF_PRIVATE *psf, int calc_length)
+{	sf_count_t	current ;
+	int 		error, has_data = SF_FALSE ;
+
+	current = psf_ftell (psf) ;
+
+	if (current > psf->dataoffset)
+		has_data = SF_TRUE ;
+
+	if (calc_length)
+	{	psf->filelength = psf_get_filelen (psf) ;
+
+		psf->datalength = psf->filelength - psf->dataoffset ;
+
+		if (psf->dataend)
+			psf->datalength -= psf->filelength - psf->dataend ;
+		else if (psf->bytewidth > 0 && psf->sf.seekable == SF_TRUE)
+			psf->datalength = psf->sf.frames * psf->bytewidth * psf->sf.channels ;
+		} ;
+
+	/* Reset the current header length to zero. */
+	psf->header.ptr [0] = 0 ;
+	psf->header.indx = 0 ;
+	psf_fseek (psf, 0, SEEK_SET) ;
+
+	/*
+	** RIFX signifies big-endian format for all header and data.
+	** To prevent lots of code copying here, we'll set the psf->rwf_endian flag
+	** once here, and never specify endian-ness for all other header operations.
+	*/
+
+	/* RIFF/RIFX marker, length, WAVE and 'fmt ' markers. */
+
+	if (psf->endian == SF_ENDIAN_LITTLE)
+        psf_binheader_writef (psf, "etm8", BHWm (RIFF_MARKER), BHW8 ((psf->filelength < 8) ? 8 : SF_MIN(psf->filelength - 8, UINT32_MAX))) ;
+	else
+        psf_binheader_writef (psf, "Etm8", BHWm (RIFX_MARKER), BHW8 ((psf->filelength < 8) ? 8 : SF_MIN(psf->filelength - 8, UINT32_MAX))) ;
+
+	/* WAVE and 'fmt ' markers. */
+	psf_binheader_writef (psf, "mm", BHWm (WAVE_MARKER), BHWm (fmt_MARKER)) ;
+
+	/* Write the 'fmt ' chunk. */
+	switch (SF_CONTAINER (psf->sf.format))
+	{	case SF_FORMAT_WAV :
+				if ((error = wav_write_fmt_chunk (psf)) != 0)
+					return error ;
+				break ;
+
+		case SF_FORMAT_WAVEX :
+				if ((error = wavex_write_fmt_chunk (psf)) != 0)
+					return error ;
+				break ;
+
+		default :
+				return SFE_UNIMPLEMENTED ;
+		} ;
+
+	/* The LIST/INFO chunk. */
+	if (psf->strings.flags & SF_STR_LOCATE_START)
+		wavlike_write_strings (psf, SF_STR_LOCATE_START) ;
+
+	if (psf->peak_info != NULL && psf->peak_info->peak_loc == SF_PEAK_START)
+		wavlike_write_peak_chunk (psf) ;
+
+	if (psf->broadcast_16k != NULL)
+		wavlike_write_bext_chunk (psf) ;
+
+	if (psf->cart_16k != NULL)
+		wavlike_write_cart_chunk (psf) ;
+
+	if (psf->cues != NULL)
+	{	uint32_t k ;
+
+		psf_binheader_writef (psf, "em44", BHWm (cue_MARKER), BHW4 (4 + psf->cues->cue_count * 6 * 4), BHW4 (psf->cues->cue_count)) ;
+
+		for (k = 0 ; k < psf->cues->cue_count ; k++)
+			psf_binheader_writef (psf, "e44m444", BHW4 (psf->cues->cue_points [k].indx), BHW4 (psf->cues->cue_points [k].position),
+						BHWm (psf->cues->cue_points [k].fcc_chunk), BHW4 (psf->cues->cue_points [k].chunk_start),
+						BHW4 (psf->cues->cue_points [k].block_start), BHW4 (psf->cues->cue_points [k].sample_offset)) ;
+		} ;
+
+	if (psf->instrument != NULL)
+	{	int		tmp ;
+		double	dtune = (double) (0x40000000) / 25.0 ;
+
+		psf_binheader_writef (psf, "m4", BHWm (smpl_MARKER), BHW4 (9 * 4 + psf->instrument->loop_count * 6 * 4)) ;
+		psf_binheader_writef (psf, "44", BHW4 (0), BHW4 (0)) ; /* Manufacturer zero is everyone */
+		tmp = (int) (1.0e9 / psf->sf.samplerate) ; /* Sample period in nano seconds */
+		psf_binheader_writef (psf, "44", BHW4 (tmp), BHW4 (psf->instrument->basenote)) ;
+		tmp = (uint32_t) (psf->instrument->detune * dtune + 0.5) ;
+		psf_binheader_writef (psf, "4", BHW4 (tmp)) ;
+		psf_binheader_writef (psf, "44", BHW4 (0), BHW4 (0)) ; /* SMTPE format */
+		psf_binheader_writef (psf, "44", BHW4 (psf->instrument->loop_count), BHW4 (0)) ;
+
+		/* Make sure we don't read past the loops array end. */
+		if (psf->instrument->loop_count > ARRAY_LEN (psf->instrument->loops))
+			psf->instrument->loop_count = ARRAY_LEN (psf->instrument->loops) ;
+
+		for (tmp = 0 ; tmp < psf->instrument->loop_count ; tmp++)
+		{	int type ;
+
+			type = psf->instrument->loops [tmp].mode ;
+			type = (type == SF_LOOP_FORWARD ? 0 : type == SF_LOOP_BACKWARD ? 2 : type == SF_LOOP_ALTERNATING ? 1 : 32) ;
+
+			psf_binheader_writef (psf, "44", BHW4 (tmp), BHW4 (type)) ;
+			psf_binheader_writef (psf, "44", BHW4 (psf->instrument->loops [tmp].start), BHW4 (psf->instrument->loops [tmp].end - 1)) ;
+			psf_binheader_writef (psf, "44", BHW4 (0), BHW4 (psf->instrument->loops [tmp].count)) ;
+			} ;
+		} ;
+
+	/* Write custom headers. */
+	if (psf->wchunks.used > 0)
+		wavlike_write_custom_chunks (psf) ;
+
+	if (psf->header.indx + 16 < psf->dataoffset)
+	{	/* Add PAD data if necessary. */
+		size_t k = psf->dataoffset - (psf->header.indx + 16) ;
+		psf_binheader_writef (psf, "m4z", BHWm (PAD_MARKER), BHW4 (k), BHWz (k)) ;
+		} ;
+
+    psf_binheader_writef (psf, "tm8", BHWm (data_MARKER), BHW8 (SF_MIN(psf->datalength, UINT32_MAX))) ;
+	psf_fwrite (psf->header.ptr, psf->header.indx, 1, psf) ;
+	if (psf->error)
+		return psf->error ;
+
+	if (has_data && psf->dataoffset != psf->header.indx)
+	{	psf_log_printf (psf, "Oooops : has_data && psf->dataoffset != psf->header.indx\n") ;
+		return psf->error = SFE_INTERNAL ;
+		} ;
+
+	psf->dataoffset = psf->header.indx ;
+
+	if (! has_data)
+		psf_fseek (psf, psf->dataoffset, SEEK_SET) ;
+	else if (current > 0)
+		psf_fseek (psf, current, SEEK_SET) ;
+
+	return psf->error ;
+} /* wav_write_header */
+
+
+static int
+wav_write_tailer (SF_PRIVATE *psf)
+{
+	/* Reset the current header buffer length to zero. */
+	psf->header.ptr [0] = 0 ;
+	psf->header.indx = 0 ;
+
+	if (psf->bytewidth > 0 && psf->sf.seekable == SF_TRUE)
+	{	psf->datalength = psf->sf.frames * psf->bytewidth * psf->sf.channels ;
+		psf->dataend = psf->dataoffset + psf->datalength ;
+		} ;
+
+	if (psf->dataend > 0)
+		psf_fseek (psf, psf->dataend, SEEK_SET) ;
+	else
+		psf->dataend = psf_fseek (psf, 0, SEEK_END) ;
+
+	if (psf->dataend & 1)
+		psf_binheader_writef (psf, "z", BHWz (1)) ;
+
+	/* Add a PEAK chunk if requested. */
+	if (psf->peak_info != NULL && psf->peak_info->peak_loc == SF_PEAK_END)
+		wavlike_write_peak_chunk (psf) ;
+
+	if (psf->strings.flags & SF_STR_LOCATE_END)
+		wavlike_write_strings (psf, SF_STR_LOCATE_END) ;
+
+	/* Write the tailer. */
+	if (psf->header.indx > 0)
+		psf_fwrite (psf->header.ptr, psf->header.indx, 1, psf) ;
+
+	return 0 ;
+} /* wav_write_tailer */
+
+static int
+wav_close (SF_PRIVATE *psf)
+{
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	wav_write_tailer (psf) ;
+
+		if (psf->file.mode == SFM_RDWR)
+		{	sf_count_t current = psf_ftell (psf) ;
+
+			/*
+			**	If the mode is RDWR and the current position is less than the
+			**	filelength, truncate the file.
+			*/
+
+			if (current < psf->filelength)
+			{	psf_ftruncate (psf, current) ;
+				psf->filelength = current ;
+				} ;
+			} ;
+
+		psf->write_header (psf, SF_TRUE) ;
+		} ;
+
+	return 0 ;
+} /* wav_close */
+
+static int
+wav_command (SF_PRIVATE *psf, int command, void * UNUSED (data), int datasize)
+{	WAVLIKE_PRIVATE	*wpriv ;
+
+	if ((wpriv = psf->container_data) == NULL)
+		return SFE_INTERNAL ;
+
+	switch (command)
+	{	case SFC_WAVEX_SET_AMBISONIC :
+			if ((SF_CONTAINER (psf->sf.format)) == SF_FORMAT_WAVEX)
+			{	if (datasize == SF_AMBISONIC_NONE)
+					wpriv->wavex_ambisonic = SF_AMBISONIC_NONE ;
+				else if (datasize == SF_AMBISONIC_B_FORMAT)
+					wpriv->wavex_ambisonic = SF_AMBISONIC_B_FORMAT ;
+				else
+					return 0 ;
+				} ;
+			return wpriv->wavex_ambisonic ;
+
+		case SFC_WAVEX_GET_AMBISONIC :
+			return wpriv->wavex_ambisonic ;
+
+		case SFC_SET_CHANNEL_MAP_INFO :
+			wpriv->wavex_channelmask = wavlike_gen_channel_mask (psf->channel_map, psf->sf.channels) ;
+			return (wpriv->wavex_channelmask != 0) ;
+
+		default :
+			break ;
+	} ;
+
+	return 0 ;
+} /* wav_command */
+
+static int
+wav_read_smpl_chunk (SF_PRIVATE *psf, uint32_t chunklen)
+{	char buffer [512] ;
+	uint32_t thisread, bytesread = 0, dword, sampler_data, loop_count, actually_loop_count = 0 ;
+	uint32_t note, pitch, start, end, type = -1, count ;
+	int j, k ;
+
+	chunklen += (chunklen & 1) ;
+
+	bytesread += psf_binheader_readf (psf, "4", &dword) ;
+	psf_log_printf (psf, "  Manufacturer : %X\n", dword) ;
+
+	bytesread += psf_binheader_readf (psf, "4", &dword) ;
+	psf_log_printf (psf, "  Product      : %u\n", dword) ;
+
+	bytesread += psf_binheader_readf (psf, "4", &dword) ;
+	psf_log_printf (psf, "  Period       : %u nsec\n", dword) ;
+
+	bytesread += psf_binheader_readf (psf, "4", &note) ;
+	psf_log_printf (psf, "  Midi Note    : %u\n", note) ;
+
+	bytesread += psf_binheader_readf (psf, "4", &pitch) ;
+	if (pitch != 0)
+	{	snprintf (buffer, sizeof (buffer), "%f",
+					(1.0 * 0x80000000) / ((uint32_t) pitch)) ;
+		psf_log_printf (psf, "  Pitch Fract. : %s\n", buffer) ;
+		}
+	else
+		psf_log_printf (psf, "  Pitch Fract. : 0\n") ;
+
+	bytesread += psf_binheader_readf (psf, "4", &dword) ;
+	psf_log_printf (psf, "  SMPTE Format : %u\n", dword) ;
+
+	bytesread += psf_binheader_readf (psf, "4", &dword) ;
+	snprintf (buffer, sizeof (buffer), "%02"PRIu32 ":%02"PRIu32 ":%02"PRIu32
+				" %02"PRIu32 "", (dword >> 24) & 0x7F, (dword >> 16) & 0x7F,
+				(dword >> 8) & 0x7F, dword & 0x7F) ;
+	psf_log_printf (psf, "  SMPTE Offset : %s\n", buffer) ;
+
+	bytesread += psf_binheader_readf (psf, "4", &loop_count) ;
+	psf_log_printf (psf, "  Loop Count   : %u\n", loop_count) ;
+
+	if (loop_count == 0 && chunklen == bytesread)
+		return 0 ;
+
+	/* Sampler Data holds the number of data bytes after the CUE chunks which
+	** is not actually CUE data. Display value after CUE data.
+	*/
+	bytesread += psf_binheader_readf (psf, "4", &sampler_data) ;
+
+	if (psf->instrument)
+	{	psf_log_printf (psf, "  Found more than one SMPL chunk, using last one.\n") ;
+		free (psf->instrument) ;
+		psf->instrument = NULL ;
+		} ;
+	if ((psf->instrument = psf_instrument_alloc ()) == NULL)
+		return SFE_MALLOC_FAILED ;
+
+	psf->instrument->loop_count = loop_count ;
+
+	for (j = 0 ; loop_count > 0 && chunklen - bytesread >= 24 ; j ++)
+	{	if ((thisread = psf_binheader_readf (psf, "4", &dword)) == 0)
+			break ;
+		bytesread += thisread ;
+		psf_log_printf (psf, "    Cue ID : %2u", dword) ;
+
+		bytesread += psf_binheader_readf (psf, "4", &type) ;
+		psf_log_printf (psf, "  Type : %2u", type) ;
+
+		bytesread += psf_binheader_readf (psf, "4", &start) ;
+		psf_log_printf (psf, "  Start : %5u", start) ;
+
+		bytesread += psf_binheader_readf (psf, "4", &end) ;
+		psf_log_printf (psf, "  End : %5u", end) ;
+
+		bytesread += psf_binheader_readf (psf, "4", &dword) ;
+		psf_log_printf (psf, "  Fraction : %5u", dword) ;
+
+		bytesread += psf_binheader_readf (psf, "4", &count) ;
+		psf_log_printf (psf, "  Count : %5u\n", count) ;
+
+		if (j < ARRAY_LEN (psf->instrument->loops))
+		{	psf->instrument->loops [j].start = start ;
+			psf->instrument->loops [j].end = end + 1 ;
+			psf->instrument->loops [j].count = count ;
+
+			switch (type)
+			{	case 0 :
+					psf->instrument->loops [j].mode = SF_LOOP_FORWARD ;
+					break ;
+				case 1 :
+					psf->instrument->loops [j].mode = SF_LOOP_ALTERNATING ;
+					break ;
+				case 2 :
+					psf->instrument->loops [j].mode = SF_LOOP_BACKWARD ;
+					break ;
+				default:
+					psf->instrument->loops [j].mode = SF_LOOP_NONE ;
+					break ;
+				} ;
+			} ;
+		actually_loop_count ++ ;
+		} ;
+
+	if (actually_loop_count > ARRAY_LEN (psf->instrument->loops))
+	{
+		psf_log_printf (psf, "*** Warning, actual Loop Points count exceeds %u, changing Loop Count from %u to %u\n", ARRAY_LEN (psf->instrument->loops), loop_count, ARRAY_LEN (psf->instrument->loops)) ;
+		psf->instrument->loop_count = ARRAY_LEN (psf->instrument->loops) ;
+		}
+	else if (loop_count != actually_loop_count)
+	{	psf_log_printf (psf, "*** Warning, actual Loop Points count != Loop Count, changing Loop Count from %u to %u\n", loop_count, actually_loop_count) ;
+		psf->instrument->loop_count = actually_loop_count ;
+		} ;
+
+	if (chunklen - bytesread == 0)
+	{	if (sampler_data != 0)
+			psf_log_printf (psf, "  Sampler Data : %u (should be 0)\n", sampler_data) ;
+		else
+			psf_log_printf (psf, "  Sampler Data : %u\n", sampler_data) ;
+		}
+	else
+	{	if (sampler_data != chunklen - bytesread)
+		{	psf_log_printf (psf, "  Sampler Data : %u (should have been %u)\n", sampler_data, chunklen - bytesread) ;
+			sampler_data = chunklen - bytesread ;
+			}
+		else
+			psf_log_printf (psf, "  Sampler Data : %u\n", sampler_data) ;
+
+		psf_log_printf (psf, "      ") ;
+		for (k = 0 ; k < (int) sampler_data ; k++)
+		{	char ch ;
+
+			if (k > 0 && (k % 20) == 0)
+				psf_log_printf (psf, "\n      ") ;
+
+			if ((thisread = psf_binheader_readf (psf, "1", &ch)) == 0)
+				break ;
+			bytesread += thisread ;
+			psf_log_printf (psf, "%02X ", ch & 0xFF) ;
+			} ;
+
+		psf_log_printf (psf, "\n") ;
+		} ;
+
+	psf->instrument->basenote = note ;
+	psf->instrument->detune = (int8_t) (pitch / (0x40000000 / 25.0) + 0.5) ;
+	psf->instrument->gain = 1 ;
+	psf->instrument->velocity_lo = psf->instrument->key_lo = 0 ;
+	psf->instrument->velocity_hi = psf->instrument->key_hi = 127 ;
+
+	return 0 ;
+} /* wav_read_smpl_chunk */
+
+/*
+** The acid chunk goes a little something like this:
+**
+** 4 bytes          'acid'
+** 4 bytes (int)     length of chunk starting at next byte
+**
+** 4 bytes (int)     type of file:
+**        this appears to be a bit mask,however some combinations
+**        are probably impossible and/or qualified as "errors"
+**
+**        0x01 On: One Shot         Off: Loop
+**        0x02 On: Root note is Set Off: No root
+**        0x04 On: Stretch is On,   Off: Strech is OFF
+**        0x08 On: Disk Based       Off: Ram based
+**        0x10 On: ??????????       Off: ????????? (Acidizer puts that ON)
+**
+** 2 bytes (short)      root note
+**        if type 0x10 is OFF : [C,C#,(...),B] -> [0x30 to 0x3B]
+**        if type 0x10 is ON  : [C,C#,(...),B] -> [0x3C to 0x47]
+**         (both types fit on same MIDI pitch albeit different octaves, so who cares)
+**
+** 2 bytes (short)      ??? always set to 0x8000
+** 4 bytes (float)      ??? seems to be always 0
+** 4 bytes (int)        number of beats
+** 2 bytes (short)      meter denominator   //always 4 in SF/ACID
+** 2 bytes (short)      meter numerator     //always 4 in SF/ACID
+**                      //are we sure about the order?? usually its num/denom
+** 4 bytes (float)      tempo
+**
+*/
+
+static int
+wav_read_acid_chunk (SF_PRIVATE *psf, uint32_t chunklen)
+{	char buffer [512] ;
+	uint32_t bytesread = 0 ;
+	int	beats, flags ;
+	short rootnote, q1, meter_denom, meter_numer ;
+	float q2, tempo ;
+
+	chunklen += (chunklen & 1) ;
+
+	bytesread += psf_binheader_readf (psf, "422f", &flags, &rootnote, &q1, &q2) ;
+
+	snprintf (buffer, sizeof (buffer), "%f", q2) ;
+
+	psf_log_printf (psf, "  Flags     : 0x%04x (%s,%s,%s,%s,%s)\n", flags,
+			(flags & 0x01) ? "OneShot" : "Loop",
+			(flags & 0x02) ? "RootNoteValid" : "RootNoteInvalid",
+			(flags & 0x04) ? "StretchOn" : "StretchOff",
+			(flags & 0x08) ? "DiskBased" : "RAMBased",
+			(flags & 0x10) ? "??On" : "??Off") ;
+
+	psf_log_printf (psf, "  Root note : 0x%x\n  ????      : 0x%04x\n  ????      : %s\n",
+				rootnote, q1, buffer) ;
+
+	bytesread += psf_binheader_readf (psf, "422f", &beats, &meter_denom, &meter_numer, &tempo) ;
+	snprintf (buffer, sizeof (buffer), "%f", tempo) ;
+	psf_log_printf (psf, "  Beats     : %d\n  Meter     : %d/%d\n  Tempo     : %s\n",
+				beats, meter_numer, meter_denom, buffer) ;
+
+	psf_binheader_readf (psf, "j", chunklen - bytesread) ;
+
+	if (psf->loop_info)
+	{	psf_log_printf (psf, "  Found existing loop info, using last one.\n") ;
+		free (psf->loop_info) ;
+		psf->loop_info = NULL ;
+		} ;
+	if ((psf->loop_info = calloc (1, sizeof (SF_LOOP_INFO))) == NULL)
+		return SFE_MALLOC_FAILED ;
+
+	psf->loop_info->time_sig_num	= meter_numer ;
+	psf->loop_info->time_sig_den	= meter_denom ;
+	psf->loop_info->loop_mode		= (flags & 0x01) ? SF_LOOP_NONE : SF_LOOP_FORWARD ;
+	psf->loop_info->num_beats		= beats ;
+	psf->loop_info->bpm				= tempo ;
+	psf->loop_info->root_key		= (flags & 0x02) ? rootnote : -1 ;
+
+	return 0 ;
+} /* wav_read_acid_chunk */
+
+/*==============================================================================
+*/
+
+static int
+wav_set_chunk (SF_PRIVATE *psf, const SF_CHUNK_INFO * chunk_info)
+{	return psf_save_write_chunk (&psf->wchunks, chunk_info) ;
+} /* wav_set_chunk */
+
+static SF_CHUNK_ITERATOR *
+wav_next_chunk_iterator (SF_PRIVATE *psf, SF_CHUNK_ITERATOR * iterator)
+{	return psf_next_chunk_iterator (&psf->rchunks, iterator) ;
+} /* wav_next_chunk_iterator */
+
+static int
+wav_get_chunk_size (SF_PRIVATE *psf, const SF_CHUNK_ITERATOR * iterator, SF_CHUNK_INFO * chunk_info)
+{	int indx ;
+
+	if ((indx = psf_find_read_chunk_iterator (&psf->rchunks, iterator)) < 0)
+		return SFE_UNKNOWN_CHUNK ;
+
+	chunk_info->datalen = psf->rchunks.chunks [indx].len ;
+
+	return SFE_NO_ERROR ;
+} /* wav_get_chunk_size */
+
+static int
+wav_get_chunk_data (SF_PRIVATE *psf, const SF_CHUNK_ITERATOR * iterator, SF_CHUNK_INFO * chunk_info)
+{	int indx ;
+	sf_count_t pos ;
+
+	if ((indx = psf_find_read_chunk_iterator (&psf->rchunks, iterator)) < 0)
+		return SFE_UNKNOWN_CHUNK ;
+
+	if (chunk_info->data == NULL)
+		return SFE_BAD_CHUNK_DATA_PTR ;
+
+	chunk_info->id_size = psf->rchunks.chunks [indx].id_size ;
+	memcpy (chunk_info->id, psf->rchunks.chunks [indx].id, sizeof (chunk_info->id) / sizeof (*chunk_info->id)) ;
+
+	pos = psf_ftell (psf) ;
+	psf_fseek (psf, psf->rchunks.chunks [indx].offset, SEEK_SET) ;
+	psf_fread (chunk_info->data, SF_MIN (chunk_info->datalen, psf->rchunks.chunks [indx].len), 1, psf) ;
+	psf_fseek (psf, pos, SEEK_SET) ;
+
+	return SFE_NO_ERROR ;
+} /* wav_get_chunk_data */
diff --git a/src/wavlike.c b/src/wavlike.c
new file mode 100644
index 0000000..7acdc80
--- /dev/null
+++ b/src/wavlike.c
@@ -0,0 +1,1371 @@
+/*
+** Copyright (C) 1999-2020 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2004-2005 David Viens <davidv@plogue.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<string.h>
+#include	<ctype.h>
+#include	<time.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+#include	"wavlike.h"
+
+
+#define WAV_BEXT_MIN_CHUNK_SIZE		602
+#define WAV_BEXT_MAX_CHUNK_SIZE		(10 * 1024)
+
+#define WAV_CART_MIN_CHUNK_SIZE		2048
+#define WAV_CART_MAX_CHUNK_SIZE		0xffffffff
+
+
+static int 	exif_subchunk_parse	(SF_PRIVATE *psf, uint32_t length) ;
+
+
+/*  Known WAVEFORMATEXTENSIBLE GUIDS.  */
+static const EXT_SUBFORMAT MSGUID_SUBTYPE_PCM =
+{	0x00000001, 0x0000, 0x0010, {	0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }
+} ;
+
+static const EXT_SUBFORMAT MSGUID_SUBTYPE_MS_ADPCM =
+{	0x00000002, 0x0000, 0x0010, {	0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }
+} ;
+
+static const EXT_SUBFORMAT MSGUID_SUBTYPE_IEEE_FLOAT =
+{	0x00000003, 0x0000, 0x0010, {	0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }
+} ;
+
+static const EXT_SUBFORMAT MSGUID_SUBTYPE_ALAW =
+{	0x00000006, 0x0000, 0x0010, {	0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }
+} ;
+
+static const EXT_SUBFORMAT MSGUID_SUBTYPE_MULAW =
+{	0x00000007, 0x0000, 0x0010, {	0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }
+} ;
+
+/*
+** the next two are from
+** http://dream.cs.bath.ac.uk/researchdev/wave-ex/bformat.html
+*/
+
+static const EXT_SUBFORMAT MSGUID_SUBTYPE_AMBISONIC_B_FORMAT_PCM =
+{	0x00000001, 0x0721, 0x11d3, {	0x86, 0x44, 0xc8, 0xc1, 0xca, 0x00, 0x00, 0x00 }
+} ;
+
+static const EXT_SUBFORMAT MSGUID_SUBTYPE_AMBISONIC_B_FORMAT_IEEE_FLOAT =
+{	0x00000003, 0x0721, 0x11d3, {	0x86, 0x44, 0xc8, 0xc1, 0xca, 0x00, 0x00, 0x00 }
+} ;
+
+
+#if 0
+/* maybe interesting one day to read the following through sf_read_raw */
+/* http://www.bath.ac.uk/~masrwd/pvocex/pvocex.html */
+static const EXT_SUBFORMAT MSGUID_SUBTYPE_PVOCEX =
+{	0x8312b9c2, 0x2e6e, 0x11d4, {	0xa8, 0x24, 0xde, 0x5b, 0x96, 0xc3, 0xab, 0x21 }
+} ;
+#endif
+
+/* This stores which bit in dwChannelMask maps to which channel */
+static const struct chanmap_s
+{	int id ;
+	const char * name ;
+} channel_mask_bits [] =
+{	/* WAVEFORMATEXTENSIBLE doesn't distuingish FRONT_LEFT from LEFT */
+	{	SF_CHANNEL_MAP_LEFT, "L" },
+	{	SF_CHANNEL_MAP_RIGHT, "R" },
+	{	SF_CHANNEL_MAP_CENTER, "C" },
+	{	SF_CHANNEL_MAP_LFE, "LFE" },
+	{	SF_CHANNEL_MAP_REAR_LEFT, "Ls" },
+	{	SF_CHANNEL_MAP_REAR_RIGHT, "Rs" },
+	{	SF_CHANNEL_MAP_FRONT_LEFT_OF_CENTER, "Lc" },
+	{	SF_CHANNEL_MAP_FRONT_RIGHT_OF_CENTER, "Rc" },
+	{	SF_CHANNEL_MAP_REAR_CENTER, "Cs" },
+	{	SF_CHANNEL_MAP_SIDE_LEFT, "Sl" },
+	{	SF_CHANNEL_MAP_SIDE_RIGHT, "Sr" },
+	{	SF_CHANNEL_MAP_TOP_CENTER, "Tc" },
+	{	SF_CHANNEL_MAP_TOP_FRONT_LEFT, "Tfl" },
+	{	SF_CHANNEL_MAP_TOP_FRONT_CENTER, "Tfc" },
+	{	SF_CHANNEL_MAP_TOP_FRONT_RIGHT, "Tfr" },
+	{	SF_CHANNEL_MAP_TOP_REAR_LEFT, "Trl" },
+	{	SF_CHANNEL_MAP_TOP_REAR_CENTER, "Trc" },
+	{	SF_CHANNEL_MAP_TOP_REAR_RIGHT, "Trr" },
+} ;
+
+/*------------------------------------------------------------------------------
+ * Private static functions.
+ */
+
+static int
+wavex_guid_equal (const EXT_SUBFORMAT * first, const EXT_SUBFORMAT * second)
+{	return !memcmp (first, second, sizeof (EXT_SUBFORMAT)) ;
+} /* wavex_guid_equal */
+
+
+
+int
+wavlike_read_fmt_chunk (SF_PRIVATE *psf, int fmtsize)
+{	WAVLIKE_PRIVATE * wpriv ;
+	WAV_FMT *wav_fmt ;
+	int	bytesread, k, bytespersec = 0 ;
+
+	if ((wpriv = psf->container_data) == NULL)
+		return SFE_INTERNAL ;
+	wav_fmt = &wpriv->wav_fmt ;
+
+	memset (wav_fmt, 0, sizeof (WAV_FMT)) ;
+
+	if (fmtsize < 16)
+		return SFE_WAV_FMT_SHORT ;
+
+	/* assume psf->rwf_endian is already properly set */
+
+	/* Read the minimal WAV file header here. */
+	bytesread = psf_binheader_readf (psf, "224422",
+					&(wav_fmt->format), &(wav_fmt->min.channels),
+					&(wav_fmt->min.samplerate), &(wav_fmt->min.bytespersec),
+					&(wav_fmt->min.blockalign), &(wav_fmt->min.bitwidth)) ;
+
+	psf_log_printf (psf, "  Format        : 0x%X => %s\n", wav_fmt->format, wavlike_format_str (wav_fmt->format)) ;
+	psf_log_printf (psf, "  Channels      : %d\n", wav_fmt->min.channels) ;
+	psf_log_printf (psf, "  Sample Rate   : %d\n", wav_fmt->min.samplerate) ;
+
+	if (wav_fmt->format == WAVE_FORMAT_PCM && wav_fmt->min.blockalign == 0
+		&& wav_fmt->min.bitwidth > 0 && wav_fmt->min.channels > 0)
+	{	wav_fmt->min.blockalign = wav_fmt->min.bitwidth / 8 + (wav_fmt->min.bitwidth % 8 > 0 ? 1 : 0) ;
+		wav_fmt->min.blockalign *= wav_fmt->min.channels ;
+		psf_log_printf (psf, "  Block Align   : 0 (should be %d)\n", wav_fmt->min.blockalign) ;
+		}
+	else
+		psf_log_printf (psf, "  Block Align   : %d\n", wav_fmt->min.blockalign) ;
+
+	if (wav_fmt->format == WAVE_FORMAT_PCM && wav_fmt->min.bitwidth == 24 &&
+			wav_fmt->min.blockalign == 4 * wav_fmt->min.channels)
+	{	psf_log_printf (psf, "  Bit Width     : 24\n") ;
+
+		psf_log_printf (psf, "\n"
+			"  Ambiguous information in 'fmt ' chunk. Possibile file types:\n"
+			"    0) Invalid IEEE float file generated by Syntrillium's Cooledit!\n"
+			"    1) File generated by ALSA's arecord containing 24 bit samples in 32 bit containers.\n"
+			"    2) 24 bit file with incorrect Block Align value.\n"
+			"\n") ;
+
+		wpriv->fmt_is_broken = 1 ;
+		}
+	else if (wav_fmt->min.bitwidth == 0)
+	{	switch (wav_fmt->format)
+		{	case WAVE_FORMAT_GSM610 :
+			case WAVE_FORMAT_IPP_ITU_G_723_1 :
+			case WAVE_FORMAT_MPEGLAYER3 :
+					psf_log_printf (psf, "  Bit Width     : %d\n", wav_fmt->min.bitwidth) ;
+					break ;
+			default :
+					psf_log_printf (psf, "  Bit Width     : %d (should not be 0)\n", wav_fmt->min.bitwidth) ;
+			}
+		}
+	else
+	{	switch (wav_fmt->format)
+		{	case WAVE_FORMAT_GSM610 :
+			case WAVE_FORMAT_IPP_ITU_G_723_1 :
+			case WAVE_FORMAT_MPEGLAYER3 :
+					psf_log_printf (psf, "  Bit Width     : %d (should be 0)\n", wav_fmt->min.bitwidth) ;
+					break ;
+			default :
+					psf_log_printf (psf, "  Bit Width     : %d\n", wav_fmt->min.bitwidth) ;
+			}
+		} ;
+
+	psf->sf.samplerate	= wav_fmt->min.samplerate ;
+	psf->sf.frames 		= 0 ;					/* Correct this when reading data chunk. */
+	psf->sf.channels	= wav_fmt->min.channels ;
+
+	switch (wav_fmt->format)
+	{	case WAVE_FORMAT_PCM :
+		case WAVE_FORMAT_IEEE_FLOAT :
+				bytespersec = wav_fmt->min.samplerate * wav_fmt->min.blockalign ;
+				if (wav_fmt->min.bytespersec != (unsigned) bytespersec)
+					psf_log_printf (psf, "  Bytes/sec     : %d (should be %d)\n", wav_fmt->min.bytespersec, bytespersec) ;
+				else
+					psf_log_printf (psf, "  Bytes/sec     : %d\n", wav_fmt->min.bytespersec) ;
+
+				psf->bytewidth = BITWIDTH2BYTES (wav_fmt->min.bitwidth) ;
+				break ;
+
+		case WAVE_FORMAT_ALAW :
+		case WAVE_FORMAT_MULAW :
+				if (wav_fmt->min.bytespersec != wav_fmt->min.samplerate * wav_fmt->min.blockalign)
+					psf_log_printf (psf, "  Bytes/sec     : %d (should be %d)\n", wav_fmt->min.bytespersec, wav_fmt->min.samplerate * wav_fmt->min.blockalign) ;
+				else
+					psf_log_printf (psf, "  Bytes/sec     : %d\n", wav_fmt->min.bytespersec) ;
+
+				psf->bytewidth = 1 ;
+				if (fmtsize >= 18)
+				{	bytesread += psf_binheader_readf (psf, "2", &(wav_fmt->size20.extrabytes)) ;
+					psf_log_printf (psf, "  Extra Bytes   : %d\n", wav_fmt->size20.extrabytes) ;
+					} ;
+				break ;
+
+		case WAVE_FORMAT_IMA_ADPCM :
+				if (wav_fmt->min.bitwidth != 4)
+					return SFE_WAV_ADPCM_NOT4BIT ;
+				if (wav_fmt->min.channels < 1 || wav_fmt->min.channels > 2)
+					return SFE_WAV_ADPCM_CHANNELS ;
+
+				bytesread += psf_binheader_readf (psf, "22", &(wav_fmt->ima.extrabytes), &(wav_fmt->ima.samplesperblock)) ;
+				psf_log_printf (psf, "  Extra Bytes   : %d\n", wav_fmt->ima.extrabytes) ;
+				if (wav_fmt->ima.samplesperblock < 1)
+				{	psf_log_printf (psf, "  Samples/Block : %d (should be > 0)\n", wav_fmt->ima.samplesperblock) ;
+					return SFE_WAV_ADPCM_SAMPLES ;
+					}
+				else
+					psf_log_printf (psf, "  Samples/Block : %d\n", wav_fmt->ima.samplesperblock) ;
+
+				bytespersec = (wav_fmt->ima.samplerate * wav_fmt->ima.blockalign) / wav_fmt->ima.samplesperblock ;
+				if (wav_fmt->ima.bytespersec != (unsigned) bytespersec)
+					psf_log_printf (psf, "  Bytes/sec     : %d (should be %d)\n", wav_fmt->ima.bytespersec, bytespersec) ;
+				else
+					psf_log_printf (psf, "  Bytes/sec     : %d\n", wav_fmt->ima.bytespersec) ;
+
+				break ;
+
+		case WAVE_FORMAT_MS_ADPCM :
+				if (wav_fmt->msadpcm.bitwidth != 4)
+					return SFE_WAV_ADPCM_NOT4BIT ;
+				if (wav_fmt->msadpcm.channels < 1 || wav_fmt->msadpcm.channels > 2)
+					return SFE_WAV_ADPCM_CHANNELS ;
+
+				bytesread += psf_binheader_readf (psf, "222", &(wav_fmt->msadpcm.extrabytes),
+								&(wav_fmt->msadpcm.samplesperblock), &(wav_fmt->msadpcm.numcoeffs)) ;
+
+				psf_log_printf (psf, "  Extra Bytes   : %d\n", wav_fmt->msadpcm.extrabytes) ;
+				if (wav_fmt->ima.samplesperblock < 1)
+				{	psf_log_printf (psf, "  Samples/Block : %d (should be > 0)\n", wav_fmt->ima.samplesperblock) ;
+					return SFE_WAV_ADPCM_SAMPLES ;
+					}
+				else
+					psf_log_printf (psf, "  Samples/Block : %d\n", wav_fmt->ima.samplesperblock) ;
+
+				bytespersec = (wav_fmt->min.samplerate * wav_fmt->min.blockalign) / wav_fmt->msadpcm.samplesperblock ;
+				if (wav_fmt->min.bytespersec == (unsigned) bytespersec)
+					psf_log_printf (psf, "  Bytes/sec     : %d\n", wav_fmt->min.bytespersec) ;
+				else if (wav_fmt->min.bytespersec == (wav_fmt->min.samplerate / wav_fmt->msadpcm.samplesperblock) * wav_fmt->min.blockalign)
+					psf_log_printf (psf, "  Bytes/sec     : %d (should be %d (MS BUG!))\n", wav_fmt->min.bytespersec, bytespersec) ;
+				else
+					psf_log_printf (psf, "  Bytes/sec     : %d (should be %d)\n", wav_fmt->min.bytespersec, bytespersec) ;
+
+				if (wav_fmt->msadpcm.numcoeffs > ARRAY_LEN (wav_fmt->msadpcm.coeffs))
+				{	psf_log_printf (psf, "  No. of Coeffs : %d (should be <= %d)\n", wav_fmt->msadpcm.numcoeffs, ARRAY_LEN (wav_fmt->msadpcm.coeffs)) ;
+					wav_fmt->msadpcm.numcoeffs = ARRAY_LEN (wav_fmt->msadpcm.coeffs) ;
+					}
+				else
+					psf_log_printf (psf, "  No. of Coeffs : %d\n", wav_fmt->msadpcm.numcoeffs) ;
+
+				psf_log_printf (psf, "    Index   Coeffs1   Coeffs2\n") ;
+				for (k = 0 ; k < wav_fmt->msadpcm.numcoeffs ; k++)
+				{	char buffer [128] ;
+
+					bytesread +=
+						psf_binheader_readf (psf, "22", &(wav_fmt->msadpcm.coeffs [k].coeff1), &(wav_fmt->msadpcm.coeffs [k].coeff2)) ;
+					snprintf (buffer, sizeof (buffer), "     %2d     %7d   %7d\n", k, wav_fmt->msadpcm.coeffs [k].coeff1, wav_fmt->msadpcm.coeffs [k].coeff2) ;
+					psf_log_printf (psf, buffer) ;
+					} ;
+				break ;
+
+		case WAVE_FORMAT_GSM610 :
+				if (wav_fmt->gsm610.channels != 1 || wav_fmt->gsm610.blockalign != 65)
+					return SFE_WAV_GSM610_FORMAT ;
+
+				bytesread +=
+				psf_binheader_readf (psf, "22", &(wav_fmt->gsm610.extrabytes), &(wav_fmt->gsm610.samplesperblock)) ;
+
+				if (wav_fmt->gsm610.samplesperblock != 320)
+					return SFE_WAV_GSM610_FORMAT ;
+
+				bytespersec = (wav_fmt->gsm610.samplerate * wav_fmt->gsm610.blockalign) / wav_fmt->gsm610.samplesperblock ;
+				if (wav_fmt->gsm610.bytespersec != (unsigned) bytespersec)
+					psf_log_printf (psf, "  Bytes/sec     : %d (should be %d)\n", wav_fmt->gsm610.bytespersec, bytespersec) ;
+				else
+					psf_log_printf (psf, "  Bytes/sec     : %d\n", wav_fmt->gsm610.bytespersec) ;
+
+				psf_log_printf (psf, "  Extra Bytes   : %d\n", wav_fmt->gsm610.extrabytes) ;
+				psf_log_printf (psf, "  Samples/Block : %d\n", wav_fmt->gsm610.samplesperblock) ;
+				break ;
+
+		case WAVE_FORMAT_MPEGLAYER3 :
+				bytesread += psf_binheader_readf (psf, "24222", &(wav_fmt->mpeg3.extrabytes),
+					&(wav_fmt->mpeg3.id), &(wav_fmt->mpeg3.flags), &(wav_fmt->mpeg3.blocksize),
+					&(wav_fmt->mpeg3.samplesperblock), &(wav_fmt->mpeg3.codecdelay)) ;
+
+				psf_log_printf (psf, "  Bytes/sec     : %d\n", wav_fmt->mpeg3.bytespersec) ;
+				psf_log_printf (psf, "  Extra Bytes   : %d\n", wav_fmt->mpeg3.extrabytes) ;
+				if (wav_fmt->mpeg3.id != 1)
+					psf_log_printf (psf, "  ID            : %d (unknown, should be 1)\n", wav_fmt->mpeg3.id) ;
+				else
+					psf_log_printf (psf, "  ID            : MPEGLAYER3_ID_MPEG\n") ;
+				psf_log_printf (psf, "  Flags         : 0x%08x\n", wav_fmt->mpeg3.flags) ;
+				psf_log_printf (psf, "  Block Size    : %d\n", wav_fmt->mpeg3.blocksize) ;
+				psf_log_printf (psf, "  Samples/Block : %d\n", wav_fmt->mpeg3.samplesperblock) ;
+				psf_log_printf (psf, "  Codec Delay   : %d samples\n", wav_fmt->mpeg3.codecdelay) ;
+				break ;
+
+		case WAVE_FORMAT_EXTENSIBLE :
+				if (wav_fmt->ext.bytespersec != wav_fmt->ext.samplerate * wav_fmt->ext.blockalign)
+					psf_log_printf (psf, "  Bytes/sec     : %d (should be %d)\n", wav_fmt->ext.bytespersec, wav_fmt->ext.samplerate * wav_fmt->ext.blockalign) ;
+				else
+					psf_log_printf (psf, "  Bytes/sec     : %d\n", wav_fmt->ext.bytespersec) ;
+
+				bytesread +=
+				psf_binheader_readf (psf, "224", &(wav_fmt->ext.extrabytes), &(wav_fmt->ext.validbits),
+						&(wav_fmt->ext.channelmask)) ;
+
+				psf_log_printf (psf, "  Valid Bits    : %d\n", wav_fmt->ext.validbits) ;
+
+				if (wav_fmt->ext.channelmask == 0)
+					psf_log_printf (psf, "  Channel Mask  : 0x0 (should not be zero)\n") ;
+				else
+				{	char buffer [512] ;
+					unsigned bit ;
+
+					wpriv->wavex_channelmask = wav_fmt->ext.channelmask ;
+
+					/* It's probably wise to ignore the channel mask if it is all zero */
+					free (psf->channel_map) ;
+
+					if ((psf->channel_map = calloc (psf->sf.channels, sizeof (psf->channel_map [0]))) == NULL)
+						return SFE_MALLOC_FAILED ;
+
+					/* Terminate the buffer we're going to append_snprintf into. */
+					buffer [0] = 0 ;
+
+					for (bit = k = 0 ; bit < ARRAY_LEN (channel_mask_bits) && k < psf->sf.channels ; bit++)
+					{
+						if (wav_fmt->ext.channelmask & (1 << bit))
+						{	if (k > psf->sf.channels)
+							{	psf_log_printf (psf, "*** More channel map bits than there are channels.\n") ;
+								break ;
+								} ;
+
+							psf->channel_map [k++] = channel_mask_bits [bit].id ;
+							append_snprintf (buffer, sizeof (buffer), "%s, ", channel_mask_bits [bit].name) ;
+							} ;
+						} ;
+
+					/* Remove trailing ", ". */
+					bit = strlen (buffer) ;
+					if (bit >= 2)
+					{	buffer [--bit] = 0 ;
+						buffer [--bit] = 0 ;
+						} ;
+
+					if (k != psf->sf.channels)
+					{	psf_log_printf (psf, "  Channel Mask  : 0x%X\n", wav_fmt->ext.channelmask) ;
+						psf_log_printf (psf, "*** Less channel map bits than there are channels.\n") ;
+						}
+					else
+						psf_log_printf (psf, "  Channel Mask  : 0x%X (%s)\n", wav_fmt->ext.channelmask, buffer) ;
+					} ;
+
+				bytesread += psf_binheader_readf (psf, "422", &(wav_fmt->ext.esf.esf_field1), &(wav_fmt->ext.esf.esf_field2), &(wav_fmt->ext.esf.esf_field3)) ;
+
+				/* compare the esf_fields with each known GUID? and print? */
+				psf_log_printf (psf, "  Subformat\n") ;
+				psf_log_printf (psf, "    esf_field1 : 0x%X\n", wav_fmt->ext.esf.esf_field1) ;
+				psf_log_printf (psf, "    esf_field2 : 0x%X\n", wav_fmt->ext.esf.esf_field2) ;
+				psf_log_printf (psf, "    esf_field3 : 0x%X\n", wav_fmt->ext.esf.esf_field3) ;
+				psf_log_printf (psf, "    esf_field4 : ") ;
+				for (k = 0 ; k < 8 ; k++)
+				{	bytesread += psf_binheader_readf (psf, "1", &(wav_fmt->ext.esf.esf_field4 [k])) ;
+					psf_log_printf (psf, "0x%X ", wav_fmt->ext.esf.esf_field4 [k] & 0xFF) ;
+					} ;
+				psf_log_printf (psf, "\n") ;
+				psf->bytewidth = BITWIDTH2BYTES (wav_fmt->ext.bitwidth) ;
+
+				/* Compare GUIDs for known ones. */
+				if (wavex_guid_equal (&wav_fmt->ext.esf, &MSGUID_SUBTYPE_PCM))
+				{	psf->sf.format = SF_FORMAT_WAVEX | u_bitwidth_to_subformat (psf->bytewidth * 8) ;
+					psf_log_printf (psf, "    format : pcm\n") ;
+					}
+				else if (wavex_guid_equal (&wav_fmt->ext.esf, &MSGUID_SUBTYPE_MS_ADPCM))
+				{	psf->sf.format = (SF_FORMAT_WAVEX | SF_FORMAT_MS_ADPCM) ;
+					psf_log_printf (psf, "    format : ms adpcm\n") ;
+					}
+				else if (wavex_guid_equal (&wav_fmt->ext.esf, &MSGUID_SUBTYPE_IEEE_FLOAT))
+				{	psf->sf.format = SF_FORMAT_WAVEX | ((psf->bytewidth == 8) ? SF_FORMAT_DOUBLE : SF_FORMAT_FLOAT) ;
+					psf_log_printf (psf, "    format : IEEE float\n") ;
+					}
+				else if (wavex_guid_equal (&wav_fmt->ext.esf, &MSGUID_SUBTYPE_ALAW))
+				{	psf->sf.format = (SF_FORMAT_WAVEX | SF_FORMAT_ALAW) ;
+					psf_log_printf (psf, "    format : A-law\n") ;
+					}
+				else if (wavex_guid_equal (&wav_fmt->ext.esf, &MSGUID_SUBTYPE_MULAW))
+				{	psf->sf.format = (SF_FORMAT_WAVEX | SF_FORMAT_ULAW) ;
+					psf_log_printf (psf, "    format : u-law\n") ;
+					}
+				else if (wavex_guid_equal (&wav_fmt->ext.esf, &MSGUID_SUBTYPE_AMBISONIC_B_FORMAT_PCM))
+				{	psf->sf.format = SF_FORMAT_WAVEX | u_bitwidth_to_subformat (psf->bytewidth * 8) ;
+					psf_log_printf (psf, "    format : pcm (Ambisonic B)\n") ;
+					wpriv->wavex_ambisonic = SF_AMBISONIC_B_FORMAT ;
+					}
+				else if (wavex_guid_equal (&wav_fmt->ext.esf, &MSGUID_SUBTYPE_AMBISONIC_B_FORMAT_IEEE_FLOAT))
+				{	psf->sf.format = SF_FORMAT_WAVEX | ((psf->bytewidth == 8) ? SF_FORMAT_DOUBLE : SF_FORMAT_FLOAT) ;
+					psf_log_printf (psf, "    format : IEEE float (Ambisonic B)\n") ;
+					wpriv->wavex_ambisonic = SF_AMBISONIC_B_FORMAT ;
+					}
+				else
+					return SFE_UNIMPLEMENTED ;
+
+				break ;
+
+		case WAVE_FORMAT_G721_ADPCM :
+				psf_log_printf (psf, "  Bytes/sec     : %d\n", wav_fmt->g72x.bytespersec) ;
+				if (fmtsize >= 20)
+				{	bytesread += psf_binheader_readf (psf, "22", &(wav_fmt->g72x.extrabytes), &(wav_fmt->g72x.auxblocksize)) ;
+					if (wav_fmt->g72x.extrabytes == 0)
+						psf_log_printf (psf, "  Extra Bytes   : %d (should be 2)\n", wav_fmt->g72x.extrabytes) ;
+					else
+						psf_log_printf (psf, "  Extra Bytes   : %d\n", wav_fmt->g72x.extrabytes) ;
+					psf_log_printf (psf, "  Aux Blk Size  : %d\n", wav_fmt->g72x.auxblocksize) ;
+					}
+				else if (fmtsize == 18)
+				{	bytesread += psf_binheader_readf (psf, "2", &(wav_fmt->g72x.extrabytes)) ;
+					psf_log_printf (psf, "  Extra Bytes   : %d%s\n", wav_fmt->g72x.extrabytes, wav_fmt->g72x.extrabytes != 0 ? " (should be 0)" : "") ;
+					}
+				else
+					psf_log_printf (psf, "*** 'fmt ' chunk should be bigger than this!\n") ;
+				break ;
+
+		case WAVE_FORMAT_NMS_VBXADPCM :
+				if (wav_fmt->min.channels != 1 || wav_fmt->min.bitwidth < 2 || wav_fmt->min.bitwidth * 20 + 2 != wav_fmt->min.blockalign)
+					return SFE_WAV_NMS_FORMAT ;
+
+				bytespersec = (wav_fmt->min.samplerate * wav_fmt->min.blockalign) / 160 ;
+				if (wav_fmt->min.bytespersec == (unsigned) bytespersec)
+					psf_log_printf (psf, "  Bytes/sec     : %d\n", wav_fmt->min.bytespersec) ;
+				else
+					psf_log_printf (psf, "  Bytes/sec     : %d (should be %d)\n", wav_fmt->min.bytespersec, bytespersec) ;
+				if (fmtsize >= 18)
+				{	bytesread += psf_binheader_readf (psf, "2", &(wav_fmt->size20.extrabytes)) ;
+					psf_log_printf (psf, "  Extra Bytes   : %d\n", wav_fmt->size20.extrabytes) ;
+					} ;
+				break ;
+
+		default :
+				psf_log_printf (psf, "*** No 'fmt ' chunk dumper for this format!\n") ;
+				return SFE_WAV_BAD_FMT ;
+		} ;
+
+	if (bytesread > fmtsize)
+	{	psf_log_printf (psf, "*** wavlike_read_fmt_chunk (bytesread > fmtsize)\n") ;
+		return SFE_WAV_BAD_FMT ;
+		}
+	else
+		psf_binheader_readf (psf, "j", fmtsize - bytesread) ;
+
+	psf->blockwidth = wav_fmt->min.channels * psf->bytewidth ;
+
+	return 0 ;
+} /* wavlike_read_fmt_chunk */
+
+void
+wavlike_write_guid (SF_PRIVATE *psf, const EXT_SUBFORMAT * subformat)
+{
+	psf_binheader_writef (psf, "422b", BHW4 (subformat->esf_field1),
+					BHW2 (subformat->esf_field2), BHW2 (subformat->esf_field3),
+					BHWv (subformat->esf_field4), BHWz (8)) ;
+} /* wavlike_write_guid */
+
+
+int
+wavlike_gen_channel_mask (const int *chan_map, int channels)
+{	int chan, mask = 0, bit = -1, last_bit = -1 ;
+
+	if (chan_map == NULL)
+		return 0 ;
+
+	for (chan = 0 ; chan < channels ; chan ++)
+	{	int k ;
+
+		for (k = bit + 1 ; k < ARRAY_LEN (channel_mask_bits) ; k++)
+			if (chan_map [chan] == channel_mask_bits [k].id)
+			{	bit = k ;
+				break ;
+				} ;
+
+		/* Check for bad sequence. */
+		if (bit <= last_bit)
+			return 0 ;
+
+		mask += 1 << bit ;
+		last_bit = bit ;
+		} ;
+
+	return mask ;
+} /* wavlike_gen_channel_mask */
+
+void
+wavlike_analyze (SF_PRIVATE *psf)
+{	unsigned char buffer [4096] ;
+	AUDIO_DETECT ad ;
+	int format = 0 ;
+
+	if (psf->is_pipe)
+	{	psf_log_printf (psf, "*** Error : Reading from a pipe. Can't analyze data section to figure out real data format.\n\n") ;
+		return ;
+		} ;
+
+	psf_log_printf (psf, "---------------------------------------------------\n"
+						"Format is known to be broken. Using detection code.\n") ;
+
+	/* Code goes here. */
+	ad.endianness = SF_ENDIAN_LITTLE ;
+	ad.channels = psf->sf.channels ;
+
+	psf_fseek (psf, 3 * 4 * 50, SEEK_SET) ;
+
+	while (psf_fread (buffer, 1, sizeof (buffer), psf) == sizeof (buffer))
+	{	format = audio_detect (psf, &ad, buffer, sizeof (buffer)) ;
+		if (format != 0)
+			break ;
+		} ;
+
+	/* Seek to start of DATA section. */
+	psf_fseek (psf, psf->dataoffset, SEEK_SET) ;
+
+	if (format == 0)
+	{	psf_log_printf (psf, "wavlike_analyze : detection failed.\n") ;
+		return ;
+		} ;
+
+	switch (format)
+	{	case SF_FORMAT_PCM_32 :
+		case SF_FORMAT_FLOAT :
+			psf_log_printf (psf, "wavlike_analyze : found format : 0x%X\n", format) ;
+			psf->sf.format = (psf->sf.format & ~SF_FORMAT_SUBMASK) + format ;
+			psf->bytewidth = 4 ;
+			psf->blockwidth = psf->sf.channels * psf->bytewidth ;
+			break ;
+
+		case SF_FORMAT_PCM_24 :
+			psf_log_printf (psf, "wavlike_analyze : found format : 0x%X\n", format) ;
+			psf->sf.format = (psf->sf.format & ~SF_FORMAT_SUBMASK) + format ;
+			psf->bytewidth = 3 ;
+			psf->blockwidth = psf->sf.channels * psf->bytewidth ;
+			break ;
+
+		default :
+			psf_log_printf (psf, "wavlike_analyze : unhandled format : 0x%X\n", format) ;
+			break ;
+		} ;
+
+	return ;
+} /* wavlike_analyze */
+
+/*==============================================================================
+*/
+
+typedef struct
+{	int			ID ;
+	const char	*name ;
+} WAV_FORMAT_DESC ;
+
+#define STR(x)			#x
+#define FORMAT_TYPE(x)	{ x, STR (x) }
+
+static WAV_FORMAT_DESC wave_descs [] =
+{	FORMAT_TYPE	(WAVE_FORMAT_PCM),
+	FORMAT_TYPE (WAVE_FORMAT_MS_ADPCM),
+	FORMAT_TYPE (WAVE_FORMAT_IEEE_FLOAT),
+	FORMAT_TYPE (WAVE_FORMAT_VSELP),
+	FORMAT_TYPE (WAVE_FORMAT_IBM_CVSD),
+	FORMAT_TYPE (WAVE_FORMAT_ALAW),
+	FORMAT_TYPE (WAVE_FORMAT_MULAW),
+	FORMAT_TYPE (WAVE_FORMAT_OKI_ADPCM),
+	FORMAT_TYPE (WAVE_FORMAT_IMA_ADPCM),
+	FORMAT_TYPE (WAVE_FORMAT_MEDIASPACE_ADPCM),
+	FORMAT_TYPE (WAVE_FORMAT_SIERRA_ADPCM),
+	FORMAT_TYPE (WAVE_FORMAT_G723_ADPCM),
+	FORMAT_TYPE (WAVE_FORMAT_DIGISTD),
+	FORMAT_TYPE (WAVE_FORMAT_DIGIFIX),
+	FORMAT_TYPE (WAVE_FORMAT_DIALOGIC_OKI_ADPCM),
+	FORMAT_TYPE (WAVE_FORMAT_MEDIAVISION_ADPCM),
+	FORMAT_TYPE (WAVE_FORMAT_CU_CODEC),
+	FORMAT_TYPE (WAVE_FORMAT_YAMAHA_ADPCM),
+	FORMAT_TYPE (WAVE_FORMAT_SONARC),
+	FORMAT_TYPE (WAVE_FORMAT_DSPGROUP_TRUESPEECH),
+	FORMAT_TYPE (WAVE_FORMAT_ECHOSC1),
+	FORMAT_TYPE (WAVE_FORMAT_AUDIOFILE_AF36),
+	FORMAT_TYPE (WAVE_FORMAT_APTX),
+	FORMAT_TYPE (WAVE_FORMAT_AUDIOFILE_AF10),
+	FORMAT_TYPE (WAVE_FORMAT_PROSODY_1612),
+	FORMAT_TYPE (WAVE_FORMAT_LRC),
+	FORMAT_TYPE (WAVE_FORMAT_DOLBY_AC2),
+	FORMAT_TYPE (WAVE_FORMAT_GSM610),
+	FORMAT_TYPE (WAVE_FORMAT_MSNAUDIO),
+	FORMAT_TYPE (WAVE_FORMAT_ANTEX_ADPCME),
+	FORMAT_TYPE (WAVE_FORMAT_CONTROL_RES_VQLPC),
+	FORMAT_TYPE (WAVE_FORMAT_DIGIREAL),
+	FORMAT_TYPE (WAVE_FORMAT_DIGIADPCM),
+	FORMAT_TYPE (WAVE_FORMAT_CONTROL_RES_CR10),
+	FORMAT_TYPE (WAVE_FORMAT_NMS_VBXADPCM),
+	FORMAT_TYPE (WAVE_FORMAT_ROLAND_RDAC),
+	FORMAT_TYPE (WAVE_FORMAT_ECHOSC3),
+	FORMAT_TYPE (WAVE_FORMAT_ROCKWELL_ADPCM),
+	FORMAT_TYPE (WAVE_FORMAT_ROCKWELL_DIGITALK),
+	FORMAT_TYPE (WAVE_FORMAT_XEBEC),
+	FORMAT_TYPE (WAVE_FORMAT_G721_ADPCM),
+	FORMAT_TYPE (WAVE_FORMAT_G728_CELP),
+	FORMAT_TYPE (WAVE_FORMAT_MSG723),
+	FORMAT_TYPE (WAVE_FORMAT_MPEG),
+	FORMAT_TYPE (WAVE_FORMAT_RT24),
+	FORMAT_TYPE (WAVE_FORMAT_PAC),
+	FORMAT_TYPE (WAVE_FORMAT_MPEGLAYER3),
+	FORMAT_TYPE (WAVE_FORMAT_LUCENT_G723),
+	FORMAT_TYPE (WAVE_FORMAT_CIRRUS),
+	FORMAT_TYPE (WAVE_FORMAT_ESPCM),
+	FORMAT_TYPE (WAVE_FORMAT_VOXWARE),
+	FORMAT_TYPE (WAVE_FORMAT_CANOPUS_ATRAC),
+	FORMAT_TYPE (WAVE_FORMAT_G726_ADPCM),
+	FORMAT_TYPE (WAVE_FORMAT_G722_ADPCM),
+	FORMAT_TYPE (WAVE_FORMAT_DSAT),
+	FORMAT_TYPE (WAVE_FORMAT_DSAT_DISPLAY),
+	FORMAT_TYPE (WAVE_FORMAT_VOXWARE_BYTE_ALIGNED),
+	FORMAT_TYPE (WAVE_FORMAT_VOXWARE_AC8),
+	FORMAT_TYPE (WAVE_FORMAT_VOXWARE_AC10),
+	FORMAT_TYPE (WAVE_FORMAT_VOXWARE_AC16),
+	FORMAT_TYPE (WAVE_FORMAT_VOXWARE_AC20),
+	FORMAT_TYPE (WAVE_FORMAT_VOXWARE_RT24),
+	FORMAT_TYPE (WAVE_FORMAT_VOXWARE_RT29),
+	FORMAT_TYPE (WAVE_FORMAT_VOXWARE_RT29HW),
+	FORMAT_TYPE (WAVE_FORMAT_VOXWARE_VR12),
+	FORMAT_TYPE (WAVE_FORMAT_VOXWARE_VR18),
+	FORMAT_TYPE (WAVE_FORMAT_VOXWARE_TQ40),
+	FORMAT_TYPE (WAVE_FORMAT_SOFTSOUND),
+	FORMAT_TYPE (WAVE_FORMAT_VOXARE_TQ60),
+	FORMAT_TYPE (WAVE_FORMAT_MSRT24),
+	FORMAT_TYPE (WAVE_FORMAT_G729A),
+	FORMAT_TYPE (WAVE_FORMAT_MVI_MV12),
+	FORMAT_TYPE (WAVE_FORMAT_DF_G726),
+	FORMAT_TYPE (WAVE_FORMAT_DF_GSM610),
+	FORMAT_TYPE (WAVE_FORMAT_ONLIVE),
+	FORMAT_TYPE (WAVE_FORMAT_SBC24),
+	FORMAT_TYPE (WAVE_FORMAT_DOLBY_AC3_SPDIF),
+	FORMAT_TYPE (WAVE_FORMAT_ZYXEL_ADPCM),
+	FORMAT_TYPE (WAVE_FORMAT_PHILIPS_LPCBB),
+	FORMAT_TYPE (WAVE_FORMAT_PACKED),
+	FORMAT_TYPE (WAVE_FORMAT_RHETOREX_ADPCM),
+	FORMAT_TYPE (IBM_FORMAT_MULAW),
+	FORMAT_TYPE (IBM_FORMAT_ALAW),
+	FORMAT_TYPE (IBM_FORMAT_ADPCM),
+	FORMAT_TYPE (WAVE_FORMAT_VIVO_G723),
+	FORMAT_TYPE (WAVE_FORMAT_VIVO_SIREN),
+	FORMAT_TYPE (WAVE_FORMAT_DIGITAL_G723),
+	FORMAT_TYPE (WAVE_FORMAT_CREATIVE_ADPCM),
+	FORMAT_TYPE (WAVE_FORMAT_CREATIVE_FASTSPEECH8),
+	FORMAT_TYPE (WAVE_FORMAT_CREATIVE_FASTSPEECH10),
+	FORMAT_TYPE (WAVE_FORMAT_QUARTERDECK),
+	FORMAT_TYPE (WAVE_FORMAT_FM_TOWNS_SND),
+	FORMAT_TYPE (WAVE_FORMAT_BZV_DIGITAL),
+	FORMAT_TYPE (WAVE_FORMAT_VME_VMPCM),
+	FORMAT_TYPE (WAVE_FORMAT_OLIGSM),
+	FORMAT_TYPE (WAVE_FORMAT_OLIADPCM),
+	FORMAT_TYPE (WAVE_FORMAT_OLICELP),
+	FORMAT_TYPE (WAVE_FORMAT_OLISBC),
+	FORMAT_TYPE (WAVE_FORMAT_OLIOPR),
+	FORMAT_TYPE (WAVE_FORMAT_LH_CODEC),
+	FORMAT_TYPE (WAVE_FORMAT_NORRIS),
+	FORMAT_TYPE (WAVE_FORMAT_SOUNDSPACE_MUSICOMPRESS),
+	FORMAT_TYPE (WAVE_FORMAT_DVM),
+	FORMAT_TYPE (WAVE_FORMAT_INTERWAV_VSC112),
+	FORMAT_TYPE (WAVE_FORMAT_IPP_ITU_G_723_1),
+	FORMAT_TYPE (WAVE_FORMAT_EXTENSIBLE),
+} ;
+
+char const*
+wavlike_format_str (int k)
+{	int lower, upper, mid ;
+
+	lower = -1 ;
+	upper = sizeof (wave_descs) / sizeof (WAV_FORMAT_DESC) ;
+
+	/* binary search */
+	if ((wave_descs [0].ID <= k) && (k <= wave_descs [upper - 1].ID))
+	{
+		while (lower + 1 < upper)
+		{	mid = (upper + lower) / 2 ;
+
+			if (k == wave_descs [mid].ID)
+				return wave_descs [mid].name ;
+			if (k < wave_descs [mid].ID)
+				upper = mid ;
+			else
+				lower = mid ;
+			} ;
+		} ;
+
+	return "Unknown format" ;
+} /* wavlike_format_str */
+
+int
+wavlike_srate2blocksize (int srate_chan_product)
+{	if (srate_chan_product < 12000)
+		return 256 ;
+	if (srate_chan_product < 23000)
+		return 512 ;
+	if (srate_chan_product < 44000)
+		return 1024 ;
+	return 2048 ;
+} /* srate2blocksize */
+
+int
+wavlike_read_bext_chunk (SF_PRIVATE *psf, uint32_t chunksize)
+{
+	SF_BROADCAST_INFO_16K * b ;
+	uint32_t bytes = 0 ;
+
+	if (chunksize < WAV_BEXT_MIN_CHUNK_SIZE)
+	{	psf_log_printf (psf, "bext : %u (should be >= %d)\n", chunksize, WAV_BEXT_MIN_CHUNK_SIZE) ;
+		psf_binheader_readf (psf, "j", chunksize) ;
+		return 0 ;
+		} ;
+
+	if (chunksize > WAV_BEXT_MAX_CHUNK_SIZE)
+	{	psf_log_printf (psf, "bext : %u (should be < %d)\n", chunksize, WAV_BEXT_MAX_CHUNK_SIZE) ;
+		psf_binheader_readf (psf, "j", chunksize) ;
+		return 0 ;
+		} ;
+
+	if (chunksize >= sizeof (SF_BROADCAST_INFO_16K))
+	{	psf_log_printf (psf, "bext : %u too big to be handled\n", chunksize) ;
+		psf_binheader_readf (psf, "j", chunksize) ;
+		return 0 ;
+		} ;
+
+	psf_log_printf (psf, "bext : %u\n", chunksize) ;
+
+	if (!psf->broadcast_16k)
+	{	psf->broadcast_16k = broadcast_var_alloc () ;
+		if (!psf->broadcast_16k)
+		{	psf->error = SFE_MALLOC_FAILED ;
+			return psf->error ;
+			}
+		}
+	else
+	{	psf_log_printf (psf, "bext : found more than one bext chunk, using last one.\n") ;
+		memset (psf->broadcast_16k, 0, sizeof (SF_BROADCAST_INFO_16K)) ;
+		}
+
+	b = psf->broadcast_16k ;
+
+	bytes += psf_binheader_readf (psf, "b", b->description, sizeof (b->description)) ;
+	bytes += psf_binheader_readf (psf, "b", b->originator, sizeof (b->originator)) ;
+	bytes += psf_binheader_readf (psf, "b", b->originator_reference, sizeof (b->originator_reference)) ;
+	bytes += psf_binheader_readf (psf, "b", b->origination_date, sizeof (b->origination_date)) ;
+	bytes += psf_binheader_readf (psf, "b", b->origination_time, sizeof (b->origination_time)) ;
+	bytes += psf_binheader_readf (psf, "442", &b->time_reference_low, &b->time_reference_high, &b->version) ;
+	bytes += psf_binheader_readf (psf, "b", &b->umid, sizeof (b->umid)) ;
+	bytes += psf_binheader_readf (psf, "22", &b->loudness_value, &b->loudness_range) ;
+	bytes += psf_binheader_readf (psf, "222", &b->max_true_peak_level, &b->max_momentary_loudness, &b->max_shortterm_loudness) ;
+	bytes += psf_binheader_readf (psf, "j", 180) ;
+
+	if (chunksize > WAV_BEXT_MIN_CHUNK_SIZE)
+	{	/* File has coding history data. */
+
+		b->coding_history_size = chunksize - WAV_BEXT_MIN_CHUNK_SIZE ;
+
+		/* We do not parse the coding history */
+		bytes += psf_binheader_readf (psf, "b", BHWv (b->coding_history), BHWz (b->coding_history_size)) ;
+		} ;
+
+	if (bytes < chunksize)
+		psf_binheader_readf (psf, "j", BHWj (chunksize - bytes)) ;
+
+	return 0 ;
+} /* wavlike_read_bext_chunk */
+
+int
+wavlike_write_bext_chunk (SF_PRIVATE *psf)
+{	SF_BROADCAST_INFO_16K *b ;
+
+	if (psf->broadcast_16k == NULL)
+		return -1 ;
+
+	b = psf->broadcast_16k ;
+
+	psf_binheader_writef (psf, "m4", BHWm (bext_MARKER), BHW4 (WAV_BEXT_MIN_CHUNK_SIZE + b->coding_history_size)) ;
+
+	/*
+	**	Note that it is very important that the field widths of the SF_BROADCAST_INFO
+	**	struct match those of the bext chunk fields.
+	*/
+
+	psf_binheader_writef (psf, "b", BHWv (b->description), BHWz (sizeof (b->description))) ;
+	psf_binheader_writef (psf, "b", BHWv (b->originator), BHWz (sizeof (b->originator))) ;
+	psf_binheader_writef (psf, "b", BHWv (b->originator_reference), BHWz (sizeof (b->originator_reference))) ;
+	psf_binheader_writef (psf, "b", BHWv (b->origination_date), BHWz (sizeof (b->origination_date))) ;
+	psf_binheader_writef (psf, "b", BHWv (b->origination_time), BHWz (sizeof (b->origination_time))) ;
+	psf_binheader_writef (psf, "442", BHW4 (b->time_reference_low), BHW4 (b->time_reference_high), BHW2 (b->version)) ;
+	psf_binheader_writef (psf, "b", BHWv (b->umid), BHWz (sizeof (b->umid))) ;
+	psf_binheader_writef (psf, "22", BHW2 (b->loudness_value), BHW2 (b->loudness_range)) ;
+	psf_binheader_writef (psf, "222", BHW2 (b->max_true_peak_level), BHW2 (b->max_momentary_loudness), BHW2 (b->max_shortterm_loudness)) ;
+	psf_binheader_writef (psf, "z", BHWz (180)) ;
+
+	if (b->coding_history_size > 0)
+		psf_binheader_writef (psf, "b", BHWv (b->coding_history), BHWz (b->coding_history_size)) ;
+
+	return 0 ;
+} /* wavlike_write_bext_chunk */
+
+int
+wavlike_read_cart_chunk (SF_PRIVATE *psf, uint32_t chunksize)
+{	SF_CART_INFO_16K *c ;
+	uint32_t bytes = 0 ;
+	int k ;
+
+	if (chunksize < WAV_CART_MIN_CHUNK_SIZE)
+	{	psf_log_printf (psf, "cart : %u (should be >= %d)\n", chunksize, WAV_CART_MIN_CHUNK_SIZE) ;
+		psf_binheader_readf (psf, "j", chunksize) ;
+		return 0 ;
+		} ;
+	if (chunksize > WAV_CART_MAX_CHUNK_SIZE)
+	{	psf_log_printf (psf, "cart : %u (should be < %d)\n", chunksize, WAV_CART_MAX_CHUNK_SIZE) ;
+		psf_binheader_readf (psf, "j", chunksize) ;
+		return 0 ;
+		} ;
+
+	/*
+	**	SF_CART_INFO_16K has an extra field 'tag_text_size' that isn't part
+	**	of the chunk, so don't include it in the size check.
+	*/
+	if (chunksize >= sizeof (SF_CART_INFO_16K) - 4)
+	{	psf_log_printf (psf, "cart : %u too big to be handled\n", chunksize) ;
+		psf_binheader_readf (psf, "j", chunksize) ;
+		return 0 ;
+		} ;
+
+	psf_log_printf (psf, "cart : %u\n", chunksize) ;
+
+	if (psf->cart_16k)
+	{	psf_log_printf (psf, "  Found more than one cart chunk, using last one.\n") ;
+		free (psf->cart_16k) ;
+		psf->cart_16k = NULL ;
+		} ;
+
+	if ((psf->cart_16k = cart_var_alloc ()) == NULL)
+	{	psf->error = SFE_MALLOC_FAILED ;
+		return psf->error ;
+		} ;
+
+	c = psf->cart_16k ;
+	bytes += psf_binheader_readf (psf, "b", c->version, sizeof (c->version)) ;
+	bytes += psf_binheader_readf (psf, "b", c->title, sizeof (c->title)) ;
+	bytes += psf_binheader_readf (psf, "b", c->artist, sizeof (c->artist)) ;
+	bytes += psf_binheader_readf (psf, "b", c->cut_id, sizeof (c->cut_id)) ;
+	bytes += psf_binheader_readf (psf, "b", c->client_id, sizeof (c->client_id)) ;
+	bytes += psf_binheader_readf (psf, "b", c->category, sizeof (c->category)) ;
+	bytes += psf_binheader_readf (psf, "b", c->classification, sizeof (c->classification)) ;
+	bytes += psf_binheader_readf (psf, "b", c->out_cue, sizeof (c->out_cue)) ;
+	bytes += psf_binheader_readf (psf, "b", c->start_date, sizeof (c->start_date)) ;
+	bytes += psf_binheader_readf (psf, "b", c->start_time, sizeof (c->start_time)) ;
+	bytes += psf_binheader_readf (psf, "b", c->end_date, sizeof (c->end_date)) ;
+	bytes += psf_binheader_readf (psf, "b", c->end_time, sizeof (c->end_time)) ;
+	bytes += psf_binheader_readf (psf, "b", c->producer_app_id, sizeof (c->producer_app_id)) ;
+	bytes += psf_binheader_readf (psf, "b", c->producer_app_version, sizeof (c->producer_app_version)) ;
+	bytes += psf_binheader_readf (psf, "b", c->user_def, sizeof (c->user_def)) ;
+	bytes += psf_binheader_readf (psf, "e4", &c->level_reference, sizeof (c->level_reference)) ;
+
+	for (k = 0 ; k < ARRAY_LEN (c->post_timers) ; k++)
+		bytes += psf_binheader_readf (psf, "b4", &c->post_timers [k].usage, make_size_t (4), &c->post_timers [k].value) ;
+
+	bytes += psf_binheader_readf (psf, "b", c->reserved, sizeof (c->reserved)) ;
+	bytes += psf_binheader_readf (psf, "b", c->url, sizeof (c->url)) ;
+
+	if (chunksize > WAV_CART_MIN_CHUNK_SIZE)
+	{	/* File has tag text. */
+		c->tag_text_size = chunksize - WAV_CART_MIN_CHUNK_SIZE ;
+		bytes += psf_binheader_readf (psf, "b", c->tag_text, make_size_t (c->tag_text_size)) ;
+		} ;
+
+	if (bytes < chunksize)
+		psf_log_printf (psf, "  %d trailing bytes in cart chunk.\n", chunksize - bytes) ;
+
+	return 0 ;
+} /* wavlike_read_cart_chunk */
+
+int
+wavlike_write_cart_chunk (SF_PRIVATE *psf)
+{	SF_CART_INFO_16K *c ;
+	int k ;
+
+	if (psf->cart_16k == NULL)
+		return -1 ;
+
+	c = psf->cart_16k ;
+	psf_binheader_writef (psf, "m4", BHWm (cart_MARKER), BHW4 (WAV_CART_MIN_CHUNK_SIZE + c->tag_text_size)) ;
+	/*
+	**	Note that it is very important that the field widths of the SF_CART_INFO
+	**	struct match those of the cart chunk fields.
+	*/
+	psf_binheader_writef (psf, "b", BHWv (c->version), BHWz (sizeof (c->version))) ;
+	psf_binheader_writef (psf, "b", BHWv (c->title), BHWz (sizeof (c->title))) ;
+	psf_binheader_writef (psf, "b", BHWv (c->artist), BHWz (sizeof (c->artist))) ;
+	psf_binheader_writef (psf, "b", BHWv (c->cut_id), BHWz (sizeof (c->cut_id))) ;
+	psf_binheader_writef (psf, "b", BHWv (c->client_id), BHWz (sizeof (c->client_id))) ;
+	psf_binheader_writef (psf, "b", BHWv (c->category), BHWz (sizeof (c->category))) ;
+	psf_binheader_writef (psf, "b", BHWv (c->classification), BHWz (sizeof (c->classification))) ;
+	psf_binheader_writef (psf, "b", BHWv (c->out_cue), BHWz (sizeof (c->out_cue))) ;
+	psf_binheader_writef (psf, "b", BHWv (c->start_date), BHWz (sizeof (c->start_date))) ;
+	psf_binheader_writef (psf, "b", BHWv (c->start_time), BHWz (sizeof (c->start_time))) ;
+	psf_binheader_writef (psf, "b", BHWv (c->end_date), BHWz (sizeof (c->end_date))) ;
+	psf_binheader_writef (psf, "b", BHWv (c->end_time), BHWz (sizeof (c->end_time))) ;
+	psf_binheader_writef (psf, "b", BHWv (c->producer_app_id), BHWz (sizeof (c->producer_app_id))) ;
+	psf_binheader_writef (psf, "b", BHWv (c->producer_app_version), BHWz (sizeof (c->producer_app_version))) ;
+	psf_binheader_writef (psf, "b", BHWv (c->user_def), BHWz (sizeof (c->user_def))) ;
+	psf_binheader_writef (psf, "e4", BHW4 (c->level_reference)) ;
+
+	for (k = 0 ; k < ARRAY_LEN (c->post_timers) ; k++)
+		psf_binheader_writef (psf, "b4", BHWv (c->post_timers [k].usage), BHWz (4), BHW4 (c->post_timers [k].value)) ;
+
+	psf_binheader_writef (psf, "z", BHWz (sizeof (c->reserved))) ;	// just write zeros, we don't have any other use for it
+	psf_binheader_writef (psf, "b", BHWv (c->url), BHWz (sizeof (c->url))) ;
+
+	if (c->tag_text_size > 0)
+		psf_binheader_writef (psf, "b", BHWv (c->tag_text), BHWz (c->tag_text_size)) ;
+
+	return 0 ;
+} /* wavlike_write_cart_chunk */
+
+int
+wavlike_subchunk_parse (SF_PRIVATE *psf, int chunk, uint32_t chunk_length)
+{	sf_count_t	current_pos ;
+	char		buffer [2048] ;
+	uint32_t 	chunk_size, bytesread = 0 ;
+
+	current_pos = psf_fseek (psf, 0, SEEK_CUR) ;
+
+	if (chunk_length <= 8)
+	{	/* This case is for broken files generated by PEAK. */
+		psf_log_printf (psf, "%M : %u (weird length)\n", chunk, chunk_length) ;
+		psf_binheader_readf (psf, "mj", &chunk, chunk_length - 4) ;
+		psf_log_printf (psf, "  %M\n", chunk) ;
+		return 0 ;
+		} ;
+
+	if (current_pos + chunk_length > psf->filelength)
+	{	psf_log_printf (psf, "%M : %u (should be %d)\n", chunk, chunk_length, (int) (psf->filelength - current_pos)) ;
+		chunk_length = psf->filelength - current_pos ;
+		}
+	else
+		psf_log_printf (psf, "%M : %u\n", chunk, chunk_length) ;
+
+	while (bytesread < chunk_length)
+	{	uint32_t thisread ;
+
+		if ((thisread = psf_binheader_readf (psf, "m", &chunk)) == 0)
+			break ;
+		bytesread += thisread ;
+
+		switch (chunk)
+		{	case adtl_MARKER :
+			case INFO_MARKER :
+					/* These markers don't contain anything, not even a chunk length. */
+					psf_log_printf (psf, "  %M\n", chunk) ;
+					continue ;
+
+			case exif_MARKER :
+					psf_log_printf (psf, "  %M\n", chunk) ;
+					if (chunk_length > bytesread)
+						bytesread += exif_subchunk_parse (psf, chunk_length - bytesread) ;
+					continue ;
+
+			case data_MARKER :
+					psf_log_printf (psf, "  %M inside a LIST block??? Backing out.\n", chunk) ;
+					/* Jump back four bytes and return to caller. */
+					psf_binheader_readf (psf, "j", -4) ;
+					return 0 ;
+
+			case 0 :
+					/*
+					**	Four zero bytes where a marker was expected. Assume this means
+					**	the rest of the chunk is garbage.
+					*/
+					psf_log_printf (psf, "    *** Found weird-ass zero marker. Jumping to end of chunk.\n") ;
+					goto cleanup_subchunk_parse ;
+
+			default :
+					break ;
+			} ;
+
+		switch (chunk)
+		{	case ISFT_MARKER :
+			case ICOP_MARKER :
+			case IARL_MARKER :
+			case IART_MARKER :
+			case ICMT_MARKER :
+			case ICRD_MARKER :
+			case IENG_MARKER :
+			case IGNR_MARKER :
+			case INAM_MARKER :
+			case IPRD_MARKER :
+			case ISBJ_MARKER :
+			case ISRC_MARKER :
+			case IAUT_MARKER :
+			case ITRK_MARKER :
+					bytesread += psf_binheader_readf (psf, "4", &chunk_size) ;
+					chunk_size += (chunk_size & 1) ;
+					if (chunk_size >= SIGNED_SIZEOF (buffer) || bytesread + chunk_size > chunk_length)
+					{	psf_log_printf (psf, "  *** %M : %u (too big)\n", chunk, chunk_size) ;
+						goto cleanup_subchunk_parse ;
+						} ;
+
+					bytesread += psf_binheader_readf (psf, "b", buffer, chunk_size) ;
+					buffer [chunk_size] = 0 ;
+					psf_log_printf (psf, "    %M : %s\n", chunk, buffer) ;
+					break ;
+
+			case labl_MARKER :
+					{	int mark_id ;
+
+						bytesread += psf_binheader_readf (psf, "44", &chunk_size, &mark_id) ;
+						chunk_size -= 4 ;
+						chunk_size += (chunk_size & 1) ;
+						if (chunk_size < 1 || chunk_size >= SIGNED_SIZEOF (buffer) || bytesread + chunk_size > chunk_length)
+						{	psf_log_printf (psf, "  *** %M : %u (too big)\n", chunk, chunk_size) ;
+							goto cleanup_subchunk_parse ;
+							} ;
+
+						bytesread += psf_binheader_readf (psf, "b", buffer, chunk_size) ;
+						buffer [chunk_size] = 0 ;
+
+						if (mark_id < 10) /* avoid swamping log buffer with labels */
+							psf_log_printf (psf, "    %M : %u : %s\n", chunk, mark_id, buffer) ;
+						else if (mark_id == 10)
+							psf_log_printf (psf, "    (Skipping)\n") ;
+
+						if (psf->cues)
+ 						{	unsigned int i = 0 ;
+
+							/* find id to store label */
+							while (i < psf->cues->cue_count && psf->cues->cue_points [i].indx != mark_id)
+								i++ ;
+
+							if (i < psf->cues->cue_count)
+								memcpy (psf->cues->cue_points [i].name, buffer, sizeof (psf->cues->cue_points [i].name)) ;
+							} ;
+						} ;
+					break ;
+
+			case DISP_MARKER :
+			case ltxt_MARKER :
+			case note_MARKER :
+					bytesread += psf_binheader_readf (psf, "4", &chunk_size) ;
+					chunk_size += (chunk_size & 1) ;
+					if (chunk_size >= SIGNED_SIZEOF (buffer) || bytesread + chunk_size > chunk_length)
+					{	psf_log_printf (psf, "  *** %M : %u (too big)\n", chunk, chunk_size) ;
+						goto cleanup_subchunk_parse ;
+						} ;
+
+					psf_log_printf (psf, "    %M : %u\n", chunk, chunk_size) ;
+					goto cleanup_subchunk_parse ;
+
+			default :
+					bytesread += psf_binheader_readf (psf, "4", &chunk_size) ;
+					chunk_size += (chunk_size & 1) ;
+					if (bytesread + chunk_size > chunk_length)
+					{	psf_log_printf (psf, "  *** %M : %u (too big)\n", chunk, chunk_size) ;
+						goto cleanup_subchunk_parse ;
+						}
+					else
+					{	psf_log_printf (psf, "    %M : %u\n", chunk, chunk_size) ;
+						bytesread += psf_binheader_readf (psf, "j", chunk_size) ;
+						} ;
+					break ;
+			} ;
+
+		switch (chunk)
+		{	case ISFT_MARKER :
+					psf_store_string (psf, SF_STR_SOFTWARE, buffer) ;
+					break ;
+			case ICOP_MARKER :
+					psf_store_string (psf, SF_STR_COPYRIGHT, buffer) ;
+					break ;
+			case INAM_MARKER :
+					psf_store_string (psf, SF_STR_TITLE, buffer) ;
+					break ;
+			case IART_MARKER :
+					psf_store_string (psf, SF_STR_ARTIST, buffer) ;
+					break ;
+			case ICMT_MARKER :
+					psf_store_string (psf, SF_STR_COMMENT, buffer) ;
+					break ;
+			case ICRD_MARKER :
+					psf_store_string (psf, SF_STR_DATE, buffer) ;
+					break ;
+			case IGNR_MARKER :
+					psf_store_string (psf, SF_STR_GENRE, buffer) ;
+					break ;
+			case IPRD_MARKER :
+					psf_store_string (psf, SF_STR_ALBUM, buffer) ;
+					break ;
+			case ITRK_MARKER :
+					psf_store_string (psf, SF_STR_TRACKNUMBER, buffer) ;
+					break ;
+			} ;
+		} ;
+
+cleanup_subchunk_parse :
+
+	if (chunk_length > bytesread)
+		bytesread += psf_binheader_readf (psf, "j", chunk_length - bytesread) ;
+
+	return 0 ;
+} /* wavlike_subchunk_parse */
+
+void
+wavlike_write_strings (SF_PRIVATE *psf, int location)
+{	int	k, prev_head_index, saved_head_index ;
+
+	if (psf_location_string_count (psf, location) == 0)
+		return ;
+
+	prev_head_index = psf->header.indx + 4 ;
+
+	psf_binheader_writef (psf, "m4m", BHWm (LIST_MARKER), BHW4 (0xBADBAD), BHWm (INFO_MARKER)) ;
+
+	for (k = 0 ; k < SF_MAX_STRINGS ; k++)
+	{	if (psf->strings.data [k].type == 0)
+			break ;
+		if (psf->strings.data [k].type < 0 || psf->strings.data [k].flags != location)
+			continue ;
+
+		switch (psf->strings.data [k].type)
+		{	case SF_STR_SOFTWARE :
+				psf_binheader_writef (psf, "ms", BHWm (ISFT_MARKER), BHWs (psf->strings.storage + psf->strings.data [k].offset)) ;
+				break ;
+
+			case SF_STR_TITLE :
+				psf_binheader_writef (psf, "ms", BHWm (INAM_MARKER), BHWs (psf->strings.storage + psf->strings.data [k].offset)) ;
+				break ;
+
+			case SF_STR_COPYRIGHT :
+				psf_binheader_writef (psf, "ms", BHWm (ICOP_MARKER), BHWs (psf->strings.storage + psf->strings.data [k].offset)) ;
+				break ;
+
+			case SF_STR_ARTIST :
+				psf_binheader_writef (psf, "ms", BHWm (IART_MARKER), BHWs (psf->strings.storage + psf->strings.data [k].offset)) ;
+				break ;
+
+			case SF_STR_COMMENT :
+				psf_binheader_writef (psf, "ms", BHWm (ICMT_MARKER), BHWs (psf->strings.storage + psf->strings.data [k].offset)) ;
+				break ;
+
+			case SF_STR_DATE :
+				psf_binheader_writef (psf, "ms", BHWm (ICRD_MARKER), BHWs (psf->strings.storage + psf->strings.data [k].offset)) ;
+				break ;
+
+			case SF_STR_GENRE :
+				psf_binheader_writef (psf, "ms", BHWm (IGNR_MARKER), BHWs (psf->strings.storage + psf->strings.data [k].offset)) ;
+				break ;
+
+			case SF_STR_ALBUM :
+				psf_binheader_writef (psf, "ms", BHWm (IPRD_MARKER), BHWs (psf->strings.storage + psf->strings.data [k].offset)) ;
+				break ;
+
+			case SF_STR_TRACKNUMBER :
+				psf_binheader_writef (psf, "ms", BHWm (ITRK_MARKER), BHWs (psf->strings.storage + psf->strings.data [k].offset)) ;
+				break ;
+
+			default :
+				break ;
+			} ;
+		} ;
+
+	saved_head_index = psf->header.indx ;
+	psf->header.indx = prev_head_index ;
+	psf_binheader_writef (psf, "4", BHW4 (saved_head_index - prev_head_index - 4)) ;
+	psf->header.indx = saved_head_index ;
+
+} /* wavlike_write_strings */
+
+int
+wavlike_read_peak_chunk (SF_PRIVATE * psf, size_t chunk_size)
+{	char		buffer [256] ;
+	uint32_t uk ;
+
+	if (chunk_size != WAVLIKE_PEAK_CHUNK_SIZE (psf->sf.channels))
+	{	psf_binheader_readf (psf, "j", chunk_size) ;
+		psf_log_printf (psf, "*** File PEAK chunk size doesn't fit with number of channels (%d).\n", psf->sf.channels) ;
+		return SFE_WAV_BAD_PEAK ;
+		} ;
+
+	if (psf->peak_info)
+	{	psf_log_printf (psf, "*** Found existing peak info, using last one.\n") ;
+		free (psf->peak_info) ;
+		psf->peak_info = NULL ;
+		} ;
+	if ((psf->peak_info = peak_info_calloc (psf->sf.channels)) == NULL)
+		return SFE_MALLOC_FAILED ;
+
+	/* read in rest of PEAK chunk. */
+	psf_binheader_readf (psf, "44", & (psf->peak_info->version), & (psf->peak_info->timestamp)) ;
+
+	if (psf->peak_info->version != 1)
+		psf_log_printf (psf, "  version    : %d *** (should be version 1)\n", psf->peak_info->version) ;
+	else
+		psf_log_printf (psf, "  version    : %d\n", psf->peak_info->version) ;
+
+	psf_log_printf (psf, "  time stamp : %d\n", psf->peak_info->timestamp) ;
+	psf_log_printf (psf, "    Ch   Position       Value\n") ;
+
+	for (uk = 0 ; uk < (uint32_t) psf->sf.channels ; uk++)
+	{	float value ;
+		uint32_t position ;
+
+		psf_binheader_readf (psf, "f4", &value, &position) ;
+		psf->peak_info->peaks [uk].value = value ;
+		psf->peak_info->peaks [uk].position = position ;
+
+		snprintf (buffer, sizeof (buffer), "    %2d   %-12" PRId64 "   %g\n",
+				uk, psf->peak_info->peaks [uk].position, psf->peak_info->peaks [uk].value) ;
+		buffer [sizeof (buffer) - 1] = 0 ;
+		psf_log_printf (psf, "%s", buffer) ;
+		} ;
+
+	return 0 ;
+} /* wavlike_read_peak_chunk */
+
+void
+wavlike_write_peak_chunk (SF_PRIVATE * psf)
+{	int k ;
+
+	if (psf->peak_info == NULL)
+		return ;
+
+	psf_binheader_writef (psf, "m4", BHWm (PEAK_MARKER), BHW4 (WAVLIKE_PEAK_CHUNK_SIZE (psf->sf.channels))) ;
+	psf_binheader_writef (psf, "44", BHW4 (1), BHW4 (time (NULL))) ;
+	for (k = 0 ; k < psf->sf.channels ; k++)
+		psf_binheader_writef (psf, "ft8", BHWf (psf->peak_info->peaks [k].value), BHW8 (psf->peak_info->peaks [k].position)) ;
+} /* wavlike_write_peak_chunk */
+
+/*==============================================================================
+*/
+
+static int
+exif_fill_and_sink (SF_PRIVATE *psf, char* buf, size_t bufsz, size_t toread)
+{
+	size_t bytesread = 0 ;
+
+	buf [0] = 0 ;
+	bufsz -= 1 ;
+	if (toread < bufsz)
+		bufsz = toread ;
+	bytesread = psf_binheader_readf (psf, "b", buf, bufsz) ;
+	buf [bufsz] = 0 ;
+
+	if (bytesread == bufsz && toread > bufsz)
+		bytesread += psf_binheader_readf (psf, "j", toread - bufsz) ;
+
+	return bytesread ;
+} /* exif_fill_and_sink */
+
+/*
+** Exif specification for audio files, at JEITA CP-3451 Exif 2.2 section 5
+** (Exif Audio File Specification) http://www.exif.org/Exif2-2.PDF
+*/
+static int
+exif_subchunk_parse (SF_PRIVATE *psf, uint32_t length)
+{	uint32_t marker, dword = 0, vmajor = -1, vminor = -1, bytesread = 0 ;
+	char buf [4096] ;
+	int thisread ;
+
+	while (bytesread < length)
+	{
+		if ((thisread = psf_binheader_readf (psf, "m", &marker)) == 0)
+			break ;
+		bytesread += thisread ;
+
+		switch (marker)
+		{
+			case 0 : /* camera padding? */
+				break ;
+
+			case ever_MARKER :
+				bytesread += psf_binheader_readf (psf, "j4", 4, &dword) ;
+				vmajor = 10 * (((dword >> 24) & 0xff) - '0') + (((dword >> 16) & 0xff) - '0') ;
+				vminor = 10 * (((dword >> 8) & 0xff) - '0') + ((dword & 0xff) - '0') ;
+				psf_log_printf (psf, "    EXIF Version : %u.%02u\n", vmajor, vminor) ;
+				break ;
+
+			case olym_MARKER :
+				bytesread += psf_binheader_readf (psf, "4", &dword) ;
+				psf_log_printf (psf, "%M : %u\n", marker, dword) ;
+				if (dword > length || bytesread + dword > length)
+					break ;
+				dword += (dword & 1) ;
+				bytesread += psf_binheader_readf (psf, "j", dword) ;
+				break ;
+
+			case emnt_MARKER : /* design information: null-terminated string */
+			case emdl_MARKER : /* model name ; null-terminated string */
+			case ecor_MARKER : /* manufacturer: null-terminated string */
+			case etim_MARKER : /* creation time: null-terminated string in the format "hour:minute:second.subsecond" */
+			case erel_MARKER : /* relation info: null-terminated string (filename) */
+			case eucm_MARKER : /* user comment: 4-byte size follows, then possibly unicode data */
+				bytesread += psf_binheader_readf (psf, "4", &dword) ;
+				bytesread += sizeof (dword) ;
+				dword += (dword & 1) ;
+
+				if (dword >= sizeof (buf))
+				{	psf_log_printf (psf, "*** Marker '%M' is too big %u\n\n", marker, dword) ;
+					return bytesread ;
+					} ;
+
+				bytesread += exif_fill_and_sink (psf, buf, sizeof (buf), dword) ;
+
+				/* BAD - don't know what's going on here -- maybe a bug in the camera */
+				/* field should be NULL-terminated but there's no room for it with the reported number */
+				/*  example output:     emdl : 8 (EX-Z1050) */
+				if (marker == emdl_MARKER && dword == strlen (buf) /* should be >= strlen+1*/)
+				{	psf_log_printf (psf, "    *** field size too small for string (sinking 2 bytes)\n") ;
+					bytesread += psf_binheader_readf (psf, "j", 2) ;
+					} ;
+
+				psf_log_printf (psf, "    %M : %u (%s)\n", marker, dword, buf) ;
+				if (dword > length)
+					return bytesread ;
+				break ;
+
+			default :
+				psf_log_printf (psf, "    *** %M (%u): -- ignored --\n", marker, marker) ;
+				break ;
+			} ;
+		} ;
+
+	return bytesread ;
+} /* exif_subchunk_parse */
+
+void
+wavlike_write_custom_chunks (SF_PRIVATE * psf)
+{	uint32_t k ;
+
+	for (k = 0 ; k < psf->wchunks.used ; k++)
+		psf_binheader_writef (psf, "m4b", BHWm (psf->wchunks.chunks [k].mark32), BHW4 (psf->wchunks.chunks [k].len),
+							BHWv (psf->wchunks.chunks [k].data), BHWz (psf->wchunks.chunks [k].len)) ;
+
+} /* wavlike_write_custom_chunks */
diff --git a/src/wavlike.h b/src/wavlike.h
new file mode 100644
index 0000000..686287d
--- /dev/null
+++ b/src/wavlike.h
@@ -0,0 +1,374 @@
+/*
+** Copyright (C) 1999-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/* This file contains definitions commong to WAV and W64 files. */
+
+
+#ifndef WAVLIKE_H_INCLUDED
+#define WAVLIKE_H_INCLUDED
+
+/*------------------------------------------------------------------------------
+** Chunk markers.
+*/
+
+#define adtl_MARKER		MAKE_MARKER ('a', 'd', 't', 'l')
+#define bext_MARKER		MAKE_MARKER ('b', 'e', 'x', 't')
+#define cart_MARKER		MAKE_MARKER ('c', 'a', 'r', 't')
+#define data_MARKER		MAKE_MARKER ('d', 'a', 't', 'a')
+#define labl_MARKER		MAKE_MARKER ('l', 'a', 'b', 'l')
+#define ltxt_MARKER		MAKE_MARKER ('l', 't', 'x', 't')
+#define note_MARKER		MAKE_MARKER ('n', 'o', 't', 'e')
+#define DISP_MARKER		MAKE_MARKER ('D', 'I', 'S', 'P')
+#define INFO_MARKER		MAKE_MARKER ('I', 'N', 'F', 'O')
+#define LIST_MARKER		MAKE_MARKER ('L', 'I', 'S', 'T')
+#define PAD_MARKER		MAKE_MARKER ('P', 'A', 'D', ' ')
+#define PEAK_MARKER		MAKE_MARKER ('P', 'E', 'A', 'K')
+
+#define ISFT_MARKER		MAKE_MARKER ('I', 'S', 'F', 'T')
+#define ICRD_MARKER		MAKE_MARKER ('I', 'C', 'R', 'D')
+#define ICOP_MARKER		MAKE_MARKER ('I', 'C', 'O', 'P')
+#define IARL_MARKER		MAKE_MARKER ('I', 'A', 'R', 'L')
+#define IART_MARKER		MAKE_MARKER ('I', 'A', 'R', 'T')
+#define INAM_MARKER		MAKE_MARKER ('I', 'N', 'A', 'M')
+#define IENG_MARKER		MAKE_MARKER ('I', 'E', 'N', 'G')
+#define IGNR_MARKER		MAKE_MARKER ('I', 'G', 'N', 'R')
+#define ICOP_MARKER		MAKE_MARKER ('I', 'C', 'O', 'P')
+#define IPRD_MARKER		MAKE_MARKER ('I', 'P', 'R', 'D')
+#define ISRC_MARKER		MAKE_MARKER ('I', 'S', 'R', 'C')
+#define ISBJ_MARKER		MAKE_MARKER ('I', 'S', 'B', 'J')
+#define ICMT_MARKER		MAKE_MARKER ('I', 'C', 'M', 'T')
+#define IAUT_MARKER		MAKE_MARKER ('I', 'A', 'U', 'T')
+#define ITRK_MARKER		MAKE_MARKER ('I', 'T', 'R', 'K')
+
+#define exif_MARKER		MAKE_MARKER ('e', 'x', 'i', 'f')
+#define ever_MARKER		MAKE_MARKER ('e', 'v', 'e', 'r')
+#define etim_MARKER		MAKE_MARKER ('e', 't', 'i', 'm')
+#define ecor_MARKER		MAKE_MARKER ('e', 'c', 'o', 'r')
+#define emdl_MARKER		MAKE_MARKER ('e', 'm', 'd', 'l')
+#define emnt_MARKER		MAKE_MARKER ('e', 'm', 'n', 't')
+#define erel_MARKER		MAKE_MARKER ('e', 'r', 'e', 'l')
+#define eucm_MARKER		MAKE_MARKER ('e', 'u', 'c', 'm')
+#define olym_MARKER		MAKE_MARKER ('o', 'l', 'y', 'm')
+
+/*------------------------------------------------------------------------------
+** List of known WAV format tags
+*/
+
+enum
+{
+	/* keep sorted for wavlike_format_str() */
+	WAVE_FORMAT_UNKNOWN					= 0x0000,		/* Microsoft Corporation */
+	WAVE_FORMAT_PCM						= 0x0001, 		/* Microsoft PCM format */
+	WAVE_FORMAT_MS_ADPCM				= 0x0002,		/* Microsoft ADPCM */
+	WAVE_FORMAT_IEEE_FLOAT				= 0x0003,		/* Micrososft 32 bit float format */
+	WAVE_FORMAT_VSELP					= 0x0004,		/* Compaq Computer Corporation */
+	WAVE_FORMAT_IBM_CVSD				= 0x0005,		/* IBM Corporation */
+	WAVE_FORMAT_ALAW					= 0x0006,		/* Microsoft Corporation */
+	WAVE_FORMAT_MULAW					= 0x0007,		/* Microsoft Corporation */
+	WAVE_FORMAT_OKI_ADPCM				= 0x0010,		/* OKI */
+	WAVE_FORMAT_IMA_ADPCM				= 0x0011,		/* Intel Corporation */
+	WAVE_FORMAT_MEDIASPACE_ADPCM		= 0x0012,		/* Videologic */
+	WAVE_FORMAT_SIERRA_ADPCM			= 0x0013,		/* Sierra Semiconductor Corp */
+	WAVE_FORMAT_G723_ADPCM				= 0x0014,		/* Antex Electronics Corporation */
+	WAVE_FORMAT_DIGISTD					= 0x0015,		/* DSP Solutions, Inc. */
+	WAVE_FORMAT_DIGIFIX					= 0x0016,		/* DSP Solutions, Inc. */
+	WAVE_FORMAT_DIALOGIC_OKI_ADPCM		= 0x0017,		/*  Dialogic Corporation  */
+	WAVE_FORMAT_MEDIAVISION_ADPCM		= 0x0018,		/*  Media Vision, Inc. */
+	WAVE_FORMAT_CU_CODEC				= 0x0019,		/* Hewlett-Packard Company */
+	WAVE_FORMAT_YAMAHA_ADPCM			= 0x0020,		/* Yamaha Corporation of America */
+	WAVE_FORMAT_SONARC					= 0x0021,		/* Speech Compression */
+	WAVE_FORMAT_DSPGROUP_TRUESPEECH 	= 0x0022,		/* DSP Group, Inc */
+	WAVE_FORMAT_ECHOSC1					= 0x0023,		/* Echo Speech Corporation */
+	WAVE_FORMAT_AUDIOFILE_AF36			= 0x0024,		/* Audiofile, Inc. */
+	WAVE_FORMAT_APTX					= 0x0025,		/* Audio Processing Technology */
+	WAVE_FORMAT_AUDIOFILE_AF10			= 0x0026,		/* Audiofile, Inc. */
+	WAVE_FORMAT_PROSODY_1612			= 0x0027,		/* Aculab plc */
+	WAVE_FORMAT_LRC						= 0x0028,		/* Merging Technologies S.A. */
+	WAVE_FORMAT_DOLBY_AC2				= 0x0030,		/* Dolby Laboratories */
+	WAVE_FORMAT_GSM610					= 0x0031,		/* Microsoft Corporation */
+	WAVE_FORMAT_MSNAUDIO				= 0x0032,		/* Microsoft Corporation */
+	WAVE_FORMAT_ANTEX_ADPCME			= 0x0033, 		/* Antex Electronics Corporation */
+	WAVE_FORMAT_CONTROL_RES_VQLPC		= 0x0034,		/* Control Resources Limited */
+	WAVE_FORMAT_DIGIREAL				= 0x0035,		/* DSP Solutions, Inc. */
+	WAVE_FORMAT_DIGIADPCM				= 0x0036,		/* DSP Solutions, Inc. */
+	WAVE_FORMAT_CONTROL_RES_CR10		= 0x0037,		/* Control Resources Limited */
+	WAVE_FORMAT_NMS_VBXADPCM			= 0x0038,		/* Natural MicroSystems */
+	WAVE_FORMAT_ROLAND_RDAC				= 0x0039,		/* Roland */
+	WAVE_FORMAT_ECHOSC3					= 0x003A,		/* Echo Speech Corporation */
+	WAVE_FORMAT_ROCKWELL_ADPCM			= 0x003B,		/* Rockwell International */
+	WAVE_FORMAT_ROCKWELL_DIGITALK		= 0x003C, 		/* Rockwell International */
+	WAVE_FORMAT_XEBEC					= 0x003D,		/* Xebec Multimedia Solutions Limited */
+	WAVE_FORMAT_G721_ADPCM				= 0x0040,		/* Antex Electronics Corporation */
+	WAVE_FORMAT_G728_CELP				= 0x0041,		/* Antex Electronics Corporation */
+	WAVE_FORMAT_MSG723					= 0x0042,		/* Microsoft Corporation */
+	WAVE_FORMAT_MPEG					= 0x0050,		/* Microsoft Corporation */
+	WAVE_FORMAT_RT24					= 0x0052,		/* InSoft Inc. */
+	WAVE_FORMAT_PAC						= 0x0053,		/* InSoft Inc. */
+	WAVE_FORMAT_MPEGLAYER3				= 0x0055,		/* MPEG 3 Layer 1 */
+	WAVE_FORMAT_LUCENT_G723				= 0x0059,		/* Lucent Technologies */
+	WAVE_FORMAT_CIRRUS					= 0x0060,		/* Cirrus Logic */
+	WAVE_FORMAT_ESPCM					= 0x0061,		/* ESS Technology */
+	WAVE_FORMAT_VOXWARE					= 0x0062,		/* Voxware Inc */
+	WAVE_FORMAT_CANOPUS_ATRAC			= 0x0063,		/* Canopus, Co., Ltd. */
+	WAVE_FORMAT_G726_ADPCM				= 0x0064,		/* APICOM */
+	WAVE_FORMAT_G722_ADPCM				= 0x0065,		/* APICOM */
+	WAVE_FORMAT_DSAT					= 0x0066,		/* Microsoft Corporation */
+	WAVE_FORMAT_DSAT_DISPLAY			= 0x0067,		/* Microsoft Corporation */
+	WAVE_FORMAT_VOXWARE_BYTE_ALIGNED	= 0x0069,		/* Voxware Inc. */
+	WAVE_FORMAT_VOXWARE_AC8				= 0x0070,		/* Voxware Inc. */
+	WAVE_FORMAT_VOXWARE_AC10			= 0x0071,		/* Voxware Inc. */
+	WAVE_FORMAT_VOXWARE_AC16			= 0x0072,		/* Voxware Inc. */
+	WAVE_FORMAT_VOXWARE_AC20			= 0x0073,		/* Voxware Inc. */
+	WAVE_FORMAT_VOXWARE_RT24			= 0x0074,		/* Voxware Inc. */
+	WAVE_FORMAT_VOXWARE_RT29			= 0x0075,		/* Voxware Inc. */
+	WAVE_FORMAT_VOXWARE_RT29HW			= 0x0076,		/* Voxware Inc. */
+	WAVE_FORMAT_VOXWARE_VR12			= 0x0077,		/* Voxware Inc. */
+	WAVE_FORMAT_VOXWARE_VR18			= 0x0078,		/* Voxware Inc. */
+	WAVE_FORMAT_VOXWARE_TQ40			= 0x0079,		/* Voxware Inc. */
+	WAVE_FORMAT_SOFTSOUND				= 0x0080,		/* Softsound, Ltd. */
+	WAVE_FORMAT_VOXARE_TQ60				= 0x0081,		/* Voxware Inc. */
+	WAVE_FORMAT_MSRT24					= 0x0082,		/* Microsoft Corporation */
+	WAVE_FORMAT_G729A					= 0x0083,		/* AT&T Laboratories */
+	WAVE_FORMAT_MVI_MV12				= 0x0084,		/* Motion Pixels */
+	WAVE_FORMAT_DF_G726					= 0x0085,		/* DataFusion Systems (Pty) (Ltd) */
+	WAVE_FORMAT_DF_GSM610				= 0x0086,		/* DataFusion Systems (Pty) (Ltd) */
+	/* removed because duplicate */
+	/* WAVE_FORMAT_ISIAUDIO				= 0x0088, */	/* Iterated Systems, Inc. */
+	WAVE_FORMAT_ONLIVE					= 0x0089,		/* OnLive! Technologies, Inc. */
+	WAVE_FORMAT_SBC24					= 0x0091,		/* Siemens Business Communications Systems */
+	WAVE_FORMAT_DOLBY_AC3_SPDIF			= 0x0092,		/* Sonic Foundry */
+	WAVE_FORMAT_ZYXEL_ADPCM				= 0x0097,		/* ZyXEL Communications, Inc. */
+	WAVE_FORMAT_PHILIPS_LPCBB			= 0x0098,		/* Philips Speech Processing */
+	WAVE_FORMAT_PACKED					= 0x0099,		/* Studer Professional Audio AG */
+	WAVE_FORMAT_RHETOREX_ADPCM			= 0x0100,		/* Rhetorex, Inc. */
+
+	/* removed because of the following */
+	/* WAVE_FORMAT_IRAT					= 0x0101,*/		/* BeCubed Software Inc. */
+
+	/* these three are unofficial */
+	IBM_FORMAT_MULAW					= 0x0101,		/* IBM mu-law format */
+	IBM_FORMAT_ALAW						= 0x0102,		/* IBM a-law format */
+	IBM_FORMAT_ADPCM					= 0x0103,		/* IBM AVC Adaptive Differential PCM format */
+
+	WAVE_FORMAT_VIVO_G723				= 0x0111,		/* Vivo Software */
+	WAVE_FORMAT_VIVO_SIREN				= 0x0112,		/* Vivo Software */
+	WAVE_FORMAT_DIGITAL_G723			= 0x0123,		/* Digital Equipment Corporation */
+	WAVE_FORMAT_CREATIVE_ADPCM			= 0x0200,		/* Creative Labs, Inc */
+	WAVE_FORMAT_CREATIVE_FASTSPEECH8 	= 0x0202,		/* Creative Labs, Inc */
+	WAVE_FORMAT_CREATIVE_FASTSPEECH10 	= 0x0203,		/* Creative Labs, Inc */
+	WAVE_FORMAT_QUARTERDECK				= 0x0220,		/* Quarterdeck Corporation */
+	WAVE_FORMAT_FM_TOWNS_SND			= 0x0300,		/* Fujitsu Corporation */
+	WAVE_FORMAT_BZV_DIGITAL				= 0x0400,		/* Brooktree Corporation */
+	WAVE_FORMAT_VME_VMPCM				= 0x0680,		/* AT&T Labs, Inc. */
+	WAVE_FORMAT_OLIGSM					= 0x1000,		/* Ing C. Olivetti & C., S.p.A. */
+	WAVE_FORMAT_OLIADPCM				= 0x1001,		/* Ing C. Olivetti & C., S.p.A. */
+	WAVE_FORMAT_OLICELP					= 0x1002,		/* Ing C. Olivetti & C., S.p.A. */
+	WAVE_FORMAT_OLISBC					= 0x1003,		/* Ing C. Olivetti & C., S.p.A. */
+	WAVE_FORMAT_OLIOPR					= 0x1004,		/* Ing C. Olivetti & C., S.p.A. */
+	WAVE_FORMAT_LH_CODEC				= 0x1100,		/* Lernout & Hauspie */
+	WAVE_FORMAT_NORRIS					= 0x1400,		/* Norris Communications, Inc. */
+	/* removed because duplicate */
+	/* WAVE_FORMAT_ISIAUDIO				= 0x1401, */	/* AT&T Labs, Inc. */
+	WAVE_FORMAT_SOUNDSPACE_MUSICOMPRESS	= 0x1500,		/* AT&T Labs, Inc. */
+	WAVE_FORMAT_DVM						= 0x2000,		/* FAST Multimedia AG */
+	WAVE_FORMAT_INTERWAV_VSC112			= 0x7150,		/* ????? */
+
+	WAVE_FORMAT_IPP_ITU_G_723_1			= 0x7230,		/* Intel Performance Primitives g723 codec. */
+
+	WAVE_FORMAT_EXTENSIBLE				= 0xFFFE
+} ;
+
+typedef	struct
+{	unsigned short	format ;
+	unsigned short	channels ;
+	unsigned int	samplerate ;
+	unsigned int	bytespersec ;
+	unsigned short	blockalign ;
+	unsigned short	bitwidth ;
+} MIN_WAV_FMT ;
+
+typedef	struct
+{	unsigned short	format ;
+	unsigned short	channels ;
+	unsigned int	samplerate ;
+	unsigned int	bytespersec ;
+	unsigned short	blockalign ;
+	unsigned short	bitwidth ;
+	unsigned short	extrabytes ;
+	unsigned short	dummy ;
+} WAV_FMT_SIZE20 ;
+
+typedef	struct
+{	unsigned short	format ;
+	unsigned short	channels ;
+	unsigned int	samplerate ;
+	unsigned int	bytespersec ;
+	unsigned short	blockalign ;
+	unsigned short	bitwidth ;
+	unsigned short	extrabytes ;
+	unsigned short	samplesperblock ;
+	unsigned short	numcoeffs ;
+	struct
+	{	short	coeff1 ;
+		short	coeff2 ;
+	}	coeffs [7] ;
+} MS_ADPCM_WAV_FMT ;
+
+typedef	struct
+{	unsigned short	format ;
+	unsigned short	channels ;
+	unsigned int	samplerate ;
+	unsigned int	bytespersec ;
+	unsigned short	blockalign ;
+	unsigned short	bitwidth ;
+	unsigned short	extrabytes ;
+	unsigned short	samplesperblock ;
+} IMA_ADPCM_WAV_FMT ;
+
+typedef	struct
+{	unsigned short	format ;
+	unsigned short	channels ;
+	unsigned int	samplerate ;
+	unsigned int	bytespersec ;
+	unsigned short	blockalign ;
+	unsigned short	bitwidth ;
+	unsigned short	extrabytes ;
+	unsigned short	auxblocksize ;
+} G72x_ADPCM_WAV_FMT ;
+
+
+typedef	struct
+{	unsigned short	format ;
+	unsigned short	channels ;
+	unsigned int	samplerate ;
+	unsigned int	bytespersec ;
+	unsigned short	blockalign ;
+	unsigned short	bitwidth ;
+	unsigned short	extrabytes ;
+	unsigned short	samplesperblock ;
+} GSM610_WAV_FMT ;
+
+typedef struct
+{	unsigned short	format ;
+	unsigned short	channels ;
+	unsigned int	samplerate ;
+	unsigned int	bytespersec ;
+	unsigned short	blockalign ;
+	unsigned short	bitwidth ;
+	unsigned short	extrabytes ;
+	unsigned short	id ;
+	unsigned int	flags ;
+	unsigned short	blocksize ;
+	unsigned short	samplesperblock ;
+	unsigned short	codecdelay ;
+} MPEGLAYER3_WAV_FMT ;
+
+typedef struct
+{	unsigned int	esf_field1 ;
+	unsigned short	esf_field2 ;
+	unsigned short	esf_field3 ;
+	unsigned char	esf_field4 [8] ;
+} EXT_SUBFORMAT ;
+
+typedef	struct
+{	unsigned short	format ;
+	unsigned short	channels ;
+	unsigned int	samplerate ;
+	unsigned int	bytespersec ;
+	unsigned short	blockalign ;
+	unsigned short	bitwidth ;
+	unsigned short	extrabytes ;
+	unsigned short	validbits ;
+	unsigned int	channelmask ;
+	EXT_SUBFORMAT	esf ;
+} EXTENSIBLE_WAV_FMT ;
+
+typedef union
+{	unsigned short		format ;
+	MIN_WAV_FMT			min ;
+	IMA_ADPCM_WAV_FMT	ima ;
+	MS_ADPCM_WAV_FMT	msadpcm ;
+	G72x_ADPCM_WAV_FMT	g72x ;
+	EXTENSIBLE_WAV_FMT	ext ;
+	GSM610_WAV_FMT		gsm610 ;
+	MPEGLAYER3_WAV_FMT	mpeg3 ;
+	WAV_FMT_SIZE20		size20 ;
+	char				padding [512] ;
+} WAV_FMT ;
+
+typedef struct
+{	int frames ;
+} FACT_CHUNK ;
+
+typedef struct
+{	/* For ambisonic commands */
+	int	wavex_ambisonic ;
+	unsigned wavex_channelmask ;
+
+	/* Set to true when 'fmt ' chunk is ambiguous.*/
+	int fmt_is_broken ;
+	WAV_FMT wav_fmt ;
+
+	/*
+	** Set to true when RF64 should be converted back to RIFF when writing the
+	** header.
+	*/
+	int rf64_downgrade ;
+} WAVLIKE_PRIVATE ;
+
+#define		WAVLIKE_GSM610_BLOCKSIZE	65
+#define		WAVLIKE_GSM610_SAMPLES		320
+
+#define		WAVLIKE_PEAK_CHUNK_SIZE(ch) (2 * sizeof (int) + ch * (sizeof (float) + sizeof (int)))
+
+/*------------------------------------------------------------------------------------
+**	Functions defined in wav_ms_adpcm.c
+*/
+
+#define	WAVLIKE_MSADPCM_ADAPT_COEFF_COUNT	7
+
+void	wavlike_msadpcm_write_adapt_coeffs (SF_PRIVATE *psf) ;
+
+/*------------------------------------------------------------------------------------
+**	Functions defined in wavlike.c
+*/
+
+char const* wavlike_format_str (int k) ;
+
+int		wavlike_srate2blocksize (int srate_chan_product) ;
+int		wavlike_read_fmt_chunk (SF_PRIVATE *psf, int fmtsize) ;
+void	wavlike_write_guid (SF_PRIVATE *psf, const EXT_SUBFORMAT * subformat) ;
+void	wavlike_analyze (SF_PRIVATE *psf) ;
+int		wavlike_gen_channel_mask (const int *chan_map, int channels) ;
+
+int		wavlike_read_bext_chunk (SF_PRIVATE *psf, uint32_t chunksize) ;
+int		wavlike_write_bext_chunk (SF_PRIVATE *psf) ;
+
+int		wavlike_read_cart_chunk (SF_PRIVATE *psf, uint32_t chunksize) ;
+int		wavlike_write_cart_chunk (SF_PRIVATE *psf) ;
+
+int		wavlike_subchunk_parse	(SF_PRIVATE *psf, int chunk, uint32_t length) ;
+void	wavlike_write_strings (SF_PRIVATE *psf, int location) ;
+
+int		wavlike_read_peak_chunk (SF_PRIVATE * psf, size_t chunk_size) ;
+void	wavlike_write_peak_chunk (SF_PRIVATE * psf) ;
+
+void	wavlike_write_custom_chunks (SF_PRIVATE * psf) ;
+
+#endif
+
diff --git a/src/windows.c b/src/windows.c
new file mode 100644
index 0000000..c11ed9b
--- /dev/null
+++ b/src/windows.c
@@ -0,0 +1,70 @@
+/*
+** Copyright (C) 2009-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*
+**	This needs to be a separate file so that we don't have to include
+**	<windows.h> elsewhere (too many symbol clashes).
+*/
+
+
+#include "sfconfig.h"
+
+#if OS_IS_WIN32
+#include <windows.h>
+
+#include "sndfile.h"
+#include "common.h"
+
+extern int sf_errno ;
+
+SNDFILE*
+sf_wchar_open (LPCWSTR wpath, int mode, SF_INFO *sfinfo)
+{	SF_PRIVATE 	*psf ;
+	char utf8name [SF_BUFFER_LEN] ;
+	DWORD dwError ;
+
+	if ((psf = psf_allocate ()) == NULL)
+	{	sf_errno = SFE_MALLOC_FAILED ;
+		return	NULL ;
+		} ;
+
+	psf_init_files (psf) ;
+
+	if (WideCharToMultiByte (CP_UTF8, 0, wpath, -1, utf8name, sizeof (utf8name), NULL, NULL) == 0)
+	{	dwError = GetLastError () ;
+		if (dwError == ERROR_INSUFFICIENT_BUFFER)
+			sf_errno = SFE_FILENAME_TOO_LONG ;
+		else
+			sf_errno = SF_ERR_UNSUPPORTED_ENCODING ;
+
+		sf_close (psf) ;
+
+		return NULL ;
+		} ;
+
+	psf_log_printf (psf, "File : '%s' (utf-8 converted from ucs-2)\n", utf8name) ;
+
+	psf_copy_filename (psf, utf8name) ;
+	psf->file.mode = mode ;
+
+	psf->error = psf_fopen (psf) ;
+
+	return psf_open_file (psf, sfinfo) ;
+} /* sf_wchar_open */
+
+#endif
diff --git a/src/wve.c b/src/wve.c
new file mode 100644
index 0000000..4b9fc8a
--- /dev/null
+++ b/src/wve.c
@@ -0,0 +1,209 @@
+/*
+** Copyright (C) 2002-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2007 Reuben Thomas
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include	"sfconfig.h"
+
+#include	<stdio.h>
+#include	<fcntl.h>
+#include	<string.h>
+#include	<ctype.h>
+
+#include	"sndfile.h"
+#include	"sfendian.h"
+#include	"common.h"
+
+/*------------------------------------------------------------------------------
+** Macros to handle big/little endian issues, and other magic numbers.
+*/
+
+#define ALAW_MARKER			MAKE_MARKER ('A', 'L', 'a', 'w')
+#define SOUN_MARKER			MAKE_MARKER ('S', 'o', 'u', 'n')
+#define DFIL_MARKER			MAKE_MARKER ('d', 'F', 'i', 'l')
+#define ESSN_MARKER			MAKE_MARKER ('e', '*', '*', '\0')
+#define PSION_VERSION		((unsigned short) 3856)
+#define PSION_DATAOFFSET	0x20
+
+/*------------------------------------------------------------------------------
+** Private static functions.
+*/
+
+static int	wve_read_header (SF_PRIVATE *psf) ;
+static int	wve_write_header (SF_PRIVATE *psf, int calc_length) ;
+static int	wve_close (SF_PRIVATE *psf) ;
+
+/*------------------------------------------------------------------------------
+** Public function.
+*/
+
+int
+wve_open (SF_PRIVATE *psf)
+{	int	error = 0 ;
+
+	if (psf->is_pipe)
+		return SFE_WVE_NO_PIPE ;
+
+	if (psf->file.mode == SFM_READ || (psf->file.mode == SFM_RDWR && psf->filelength > 0))
+	{	if ((error = wve_read_header (psf)))
+			return error ;
+		} ;
+
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	if ((SF_CONTAINER (psf->sf.format)) != SF_FORMAT_WVE)
+			return	SFE_BAD_OPEN_FORMAT ;
+
+		psf->endian = SF_ENDIAN_BIG ;
+
+		if ((error = wve_write_header (psf, SF_FALSE)))
+			return error ;
+
+		psf->write_header = wve_write_header ;
+		} ;
+
+	psf->blockwidth = psf->bytewidth * psf->sf.channels ;
+
+	psf->container_close = wve_close ;
+
+	error = alaw_init (psf) ;
+
+	return error ;
+} /* wve_open */
+
+/*------------------------------------------------------------------------------
+*/
+
+static int
+wve_read_header (SF_PRIVATE *psf)
+{	int marker ;
+	unsigned short version, padding, repeats, trash ;
+	unsigned datalength ;
+
+	/* Set position to start of file to begin reading header. */
+	psf_binheader_readf (psf, "pm", 0, &marker) ;
+	if (marker != ALAW_MARKER)
+	{	psf_log_printf (psf, "Could not find '%M'\n", ALAW_MARKER) ;
+		return SFE_WVE_NOT_WVE ;
+		} ;
+
+	psf_binheader_readf (psf, "m", &marker) ;
+	if (marker != SOUN_MARKER)
+	{	psf_log_printf (psf, "Could not find '%M'\n", SOUN_MARKER) ;
+		return SFE_WVE_NOT_WVE ;
+		} ;
+
+	psf_binheader_readf (psf, "m", &marker) ;
+	if (marker != DFIL_MARKER)
+	{	psf_log_printf (psf, "Could not find '%M'\n", DFIL_MARKER) ;
+		return SFE_WVE_NOT_WVE ;
+		} ;
+
+	psf_binheader_readf (psf, "m", &marker) ;
+	if (marker != ESSN_MARKER)
+	{	psf_log_printf (psf, "Could not find '%M'\n", ESSN_MARKER) ;
+		return SFE_WVE_NOT_WVE ;
+		} ;
+
+	psf_binheader_readf (psf, "E2", &version) ;
+
+	psf_log_printf (psf, "Psion Palmtop Alaw (.wve)\n"
+			"  Sample Rate : 8000\n"
+			"  Channels    : 1\n"
+			"  Encoding    : A-law\n") ;
+
+	if (version != PSION_VERSION)
+		psf_log_printf (psf, "Psion version %d should be %d\n", version, PSION_VERSION) ;
+
+	psf_binheader_readf (psf, "E4", &datalength) ;
+	psf->dataoffset = PSION_DATAOFFSET ;
+	if (datalength != psf->filelength - psf->dataoffset)
+	{	psf->datalength = psf->filelength - psf->dataoffset ;
+		psf_log_printf (psf, "Data length %d should be %D\n", datalength, psf->datalength) ;
+		}
+	else
+		psf->datalength = datalength ;
+
+	psf_binheader_readf (psf, "E22222", &padding, &repeats, &trash, &trash, &trash) ;
+
+	psf->sf.format		= SF_FORMAT_WVE | SF_FORMAT_ALAW ;
+	psf->sf.samplerate	= 8000 ;
+	psf->sf.frames		= psf->datalength ;
+	psf->sf.channels	= 1 ;
+
+	return SFE_NO_ERROR ;
+} /* wve_read_header */
+
+/*------------------------------------------------------------------------------
+*/
+
+static int
+wve_write_header (SF_PRIVATE *psf, int calc_length)
+{	sf_count_t	current ;
+	unsigned datalen ;
+
+	current = psf_ftell (psf) ;
+
+	if (calc_length)
+	{	psf->filelength = psf_get_filelen (psf) ;
+
+		psf->datalength = psf->filelength - psf->dataoffset ;
+		if (psf->dataend)
+			psf->datalength -= psf->filelength - psf->dataend ;
+
+		psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ;
+		} ;
+
+	/* Reset the current header length to zero. */
+	psf->header.ptr [0] = 0 ;
+	psf->header.indx = 0 ;
+	psf_fseek (psf, 0, SEEK_SET) ;
+
+	/* Write header. */
+	datalen = psf->datalength ;
+	psf_binheader_writef (psf, "Emmmm", BHWm (ALAW_MARKER), BHWm (SOUN_MARKER), BHWm (DFIL_MARKER), BHWm (ESSN_MARKER)) ;
+	psf_binheader_writef (psf, "E2422222", BHW2 (PSION_VERSION), BHW4 (datalen), BHW2 (0), BHW2 (0), BHW2 (0), BHW2 (0), BHW2 (0)) ;
+	psf_fwrite (psf->header.ptr, psf->header.indx, 1, psf) ;
+
+	if (psf->sf.channels != 1)
+		return SFE_CHANNEL_COUNT ;
+
+	if (psf->error)
+		return psf->error ;
+
+	psf->dataoffset = psf->header.indx ;
+
+	if (current > 0)
+		psf_fseek (psf, current, SEEK_SET) ;
+
+	return psf->error ;
+} /* wve_write_header */
+
+/*------------------------------------------------------------------------------
+*/
+
+static int
+wve_close (SF_PRIVATE *psf)
+{
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	/*  Now we know for certain the length of the file we can re-write
+		**	the header.
+		*/
+		wve_write_header (psf, SF_TRUE) ;
+		} ;
+
+	return 0 ;
+} /* wve_close */
diff --git a/src/xi.c b/src/xi.c
new file mode 100644
index 0000000..e816947
--- /dev/null
+++ b/src/xi.c
@@ -0,0 +1,1223 @@
+/*
+** Copyright (C) 2003-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+
+#include "sndfile.h"
+#include "sfendian.h"
+#include "common.h"
+
+#define	MAX_XI_SAMPLES	16
+
+/*------------------------------------------------------------------------------
+** Private static functions and tyepdefs.
+*/
+
+typedef struct
+{	/* Warning, this filename is NOT nul terminated. */
+	char	filename [22] ;
+	char	software [20] ;
+	char	sample_name [22] ;
+
+	int		loop_begin, loop_end ;
+	int		sample_flags ;
+
+	/* Data for encoder and decoder. */
+	short	last_16 ;
+} XI_PRIVATE ;
+
+static int	xi_close		(SF_PRIVATE *psf) ;
+static int	xi_write_header (SF_PRIVATE *psf, int calc_length) ;
+static int	xi_read_header	(SF_PRIVATE *psf) ;
+static int	dpcm_init 		(SF_PRIVATE *psf) ;
+
+
+static sf_count_t	dpcm_seek (SF_PRIVATE *psf, int mode, sf_count_t offset) ;
+
+/*------------------------------------------------------------------------------
+** Public function.
+*/
+
+int
+xi_open	(SF_PRIVATE *psf)
+{	XI_PRIVATE *pxi ;
+	int		subformat, error = 0 ;
+
+	if (psf->is_pipe)
+		return SFE_XI_NO_PIPE ;
+
+	if (psf->codec_data)
+		pxi = psf->codec_data ;
+	else if ((pxi = calloc (1, sizeof (XI_PRIVATE))) == NULL)
+		return SFE_MALLOC_FAILED ;
+
+	psf->codec_data = pxi ;
+
+	if (psf->file.mode == SFM_READ || (psf->file.mode == SFM_RDWR && psf->filelength > 0))
+	{	if ((error = xi_read_header (psf)))
+			return error ;
+		} ;
+
+	subformat = SF_CODEC (psf->sf.format) ;
+
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	if ((SF_CONTAINER (psf->sf.format)) != SF_FORMAT_XI)
+			return	SFE_BAD_OPEN_FORMAT ;
+
+		psf->endian = SF_ENDIAN_LITTLE ;
+		psf->sf.channels = 1 ; /* Always mono */
+		psf->sf.samplerate = 44100 ; /* Always */
+
+		/* Set up default instrument and software name. */
+		memcpy (pxi->filename, "Default Name            ", sizeof (pxi->filename)) ;
+		memcpy (pxi->software, PACKAGE_NAME "-" PACKAGE_VERSION "               ", sizeof (pxi->software)) ;
+
+		memset (pxi->sample_name, 0, sizeof (pxi->sample_name)) ;
+		snprintf (pxi->sample_name, sizeof (pxi->sample_name), "%s", "Sample #1") ;
+
+		pxi->sample_flags = (subformat == SF_FORMAT_DPCM_16) ? 16 : 0 ;
+
+		if (xi_write_header (psf, SF_FALSE))
+			return psf->error ;
+
+		psf->write_header = xi_write_header ;
+		} ;
+
+	psf->container_close = xi_close ;
+	psf->seek = dpcm_seek ;
+
+	psf->sf.seekable = SF_FALSE ;
+
+	psf->blockwidth = psf->bytewidth * psf->sf.channels ;
+
+	switch (subformat)
+	{	case SF_FORMAT_DPCM_8 :		/* 8-bit differential PCM. */
+		case SF_FORMAT_DPCM_16 :	/* 16-bit differential PCM. */
+				error = dpcm_init (psf) ;
+				break ;
+
+		default : break ;
+		} ;
+
+	return error ;
+} /* xi_open */
+
+/*------------------------------------------------------------------------------
+*/
+
+static int
+xi_close	(SF_PRIVATE * UNUSED (psf))
+{
+	return 0 ;
+} /* xi_close */
+
+/*==============================================================================
+*/
+
+static sf_count_t dpcm_read_dsc2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
+static sf_count_t dpcm_read_dsc2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
+static sf_count_t dpcm_read_dsc2f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
+static sf_count_t dpcm_read_dsc2d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
+
+static sf_count_t dpcm_write_s2dsc (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
+static sf_count_t dpcm_write_i2dsc (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
+static sf_count_t dpcm_write_f2dsc (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
+static sf_count_t dpcm_write_d2dsc (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
+
+static sf_count_t dpcm_read_dles2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
+static sf_count_t dpcm_read_dles2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
+static sf_count_t dpcm_read_dles2f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
+static sf_count_t dpcm_read_dles2d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
+
+static sf_count_t dpcm_write_s2dles (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
+static sf_count_t dpcm_write_i2dles (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
+static sf_count_t dpcm_write_f2dles (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
+static sf_count_t dpcm_write_d2dles (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
+
+static int
+dpcm_init (SF_PRIVATE *psf)
+{	if (psf->bytewidth == 0 || psf->sf.channels == 0)
+		return SFE_INTERNAL ;
+
+	psf->blockwidth = psf->bytewidth * psf->sf.channels ;
+
+	if (psf->file.mode == SFM_READ || psf->file.mode == SFM_RDWR)
+	{	switch (psf->bytewidth)
+		{	case 1 :
+					psf->read_short		= dpcm_read_dsc2s ;
+					psf->read_int		= dpcm_read_dsc2i ;
+					psf->read_float		= dpcm_read_dsc2f ;
+					psf->read_double	= dpcm_read_dsc2d ;
+					break ;
+			case 2 :
+					psf->read_short		= dpcm_read_dles2s ;
+					psf->read_int		= dpcm_read_dles2i ;
+					psf->read_float		= dpcm_read_dles2f ;
+					psf->read_double	= dpcm_read_dles2d ;
+					break ;
+			default :
+				psf_log_printf (psf, "dpcm_init() returning SFE_UNIMPLEMENTED\n") ;
+				return SFE_UNIMPLEMENTED ;
+			} ;
+		} ;
+
+	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+	{	switch (psf->bytewidth)
+		{	case 1 :
+					psf->write_short	= dpcm_write_s2dsc ;
+					psf->write_int		= dpcm_write_i2dsc ;
+					psf->write_float	= dpcm_write_f2dsc ;
+					psf->write_double	= dpcm_write_d2dsc ;
+					break ;
+			case 2 :
+					psf->write_short	= dpcm_write_s2dles ;
+					psf->write_int		= dpcm_write_i2dles ;
+					psf->write_float	= dpcm_write_f2dles ;
+					psf->write_double	= dpcm_write_d2dles ;
+					break ;
+			default :
+				psf_log_printf (psf, "dpcm_init() returning SFE_UNIMPLEMENTED\n") ;
+				return SFE_UNIMPLEMENTED ;
+			} ;
+		} ;
+
+	psf->filelength = psf_get_filelen (psf) ;
+	psf->datalength = (psf->dataend) ? psf->dataend - psf->dataoffset :
+							psf->filelength - psf->dataoffset ;
+	psf->sf.frames = psf->datalength / psf->blockwidth ;
+
+	return 0 ;
+} /* dpcm_init */
+
+/*==============================================================================
+*/
+
+static sf_count_t
+dpcm_seek (SF_PRIVATE *psf, int mode, sf_count_t offset)
+{	BUF_UNION	ubuf ;
+	XI_PRIVATE	*pxi ;
+	int			total, bufferlen, len ;
+
+	if ((pxi = psf->codec_data) == NULL)
+		return SFE_INTERNAL ;
+
+	if (psf->datalength < 0 || psf->dataoffset < 0)
+	{	psf->error = SFE_BAD_SEEK ;
+		return	PSF_SEEK_ERROR ;
+		} ;
+
+	if (offset == 0)
+	{	psf_fseek (psf, psf->dataoffset, SEEK_SET) ;
+		pxi->last_16 = 0 ;
+		return 0 ;
+		} ;
+
+	if (offset < 0 || offset > psf->sf.frames)
+	{	psf->error = SFE_BAD_SEEK ;
+		return	PSF_SEEK_ERROR ;
+		} ;
+
+	if (mode != SFM_READ)
+	{	/* What to do about write??? */
+		psf->error = SFE_BAD_SEEK ;
+		return	PSF_SEEK_ERROR ;
+		} ;
+
+	psf_fseek (psf, psf->dataoffset, SEEK_SET) ;
+
+	if ((SF_CODEC (psf->sf.format)) == SF_FORMAT_DPCM_16)
+	{	total = offset ;
+		bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+		while (total > 0)
+		{	len = (total > bufferlen) ? bufferlen : total ;
+			total -= (int) dpcm_read_dles2s (psf, ubuf.sbuf, len) ;
+			} ;
+		}
+	else
+	{	total = offset ;
+		bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+		while (total > 0)
+		{	len = (total > bufferlen) ? bufferlen : total ;
+			total -= (int) dpcm_read_dsc2s (psf, ubuf.sbuf, len) ;
+			} ;
+		} ;
+
+	return offset ;
+} /* dpcm_seek */
+
+
+static int
+xi_write_header (SF_PRIVATE *psf, int UNUSED (calc_length))
+{	XI_PRIVATE	*pxi ;
+	sf_count_t	current ;
+	const char	*string ;
+
+	if ((pxi = psf->codec_data) == NULL)
+		return SFE_INTERNAL ;
+
+	current = psf_ftell (psf) ;
+
+	/* Reset the current header length to zero. */
+	psf->header.ptr [0] = 0 ;
+	psf->header.indx = 0 ;
+	psf_fseek (psf, 0, SEEK_SET) ;
+
+	string = "Extended Instrument: " ;
+	psf_binheader_writef (psf, "b", BHWv (string), BHWz (strlen (string))) ;
+	psf_binheader_writef (psf, "b1", BHWv (pxi->filename), BHWz (sizeof (pxi->filename)), BHW1 (0x1A)) ;
+
+	/* Write software version and two byte XI version. */
+	psf_binheader_writef (psf, "eb2", BHWv (pxi->software), BHWz (sizeof (pxi->software)), BHW2 ((1 << 8) + 2)) ;
+
+	/*
+	** Jump note numbers (96), volume envelope (48), pan envelope (48),
+	** volume points (1), pan points (1)
+	*/
+	psf_binheader_writef (psf, "z", BHWz ((size_t) (96 + 48 + 48 + 1 + 1))) ;
+
+	/* Jump volume loop (3 bytes), pan loop (3), envelope flags (3), vibrato (3)
+	** fade out (2), 22 unknown bytes, and then write sample_count (2 bytes).
+	*/
+	psf_binheader_writef (psf, "ez2z2", BHWz ((size_t) (4 * 3)), BHW2 (0x1234), BHWz (22), BHW2 (1)) ;
+
+	pxi->loop_begin = 0 ;
+	pxi->loop_end = 0 ;
+
+	psf_binheader_writef (psf, "et844", BHW8 (psf->sf.frames), BHW4 (pxi->loop_begin), BHW4 (pxi->loop_end)) ;
+
+	/* volume, fine tune, flags, pan, note, namelen */
+	psf_binheader_writef (psf, "111111", BHW1 (128), BHW1 (0), BHW1 (pxi->sample_flags), BHW1 (128), BHW1 (0), BHW1 (strlen (pxi->sample_name))) ;
+
+	psf_binheader_writef (psf, "b", BHWv (pxi->sample_name), BHWz (sizeof (pxi->sample_name))) ;
+
+	/* Header construction complete so write it out. */
+	psf_fwrite (psf->header.ptr, psf->header.indx, 1, psf) ;
+
+	if (psf->error)
+		return psf->error ;
+
+	psf->dataoffset = psf->header.indx ;
+
+	if (current > 0)
+		psf_fseek (psf, current, SEEK_SET) ;
+
+	return psf->error ;
+} /* xi_write_header */
+
+static int
+xi_read_header (SF_PRIVATE *psf)
+{	char	buffer [64], name [32] ;
+	short	version, fade_out, sample_count ;
+	int		k, loop_begin, loop_end ;
+	int 	sample_sizes [MAX_XI_SAMPLES] ;
+
+	psf_binheader_readf (psf, "pb", 0, buffer, 21) ;
+
+	memset (sample_sizes, 0, sizeof (sample_sizes)) ;
+
+	buffer [20] = 0 ;
+	if (strcmp (buffer, "Extended Instrument:") != 0)
+		return SFE_XI_BAD_HEADER ;
+
+	memset (buffer, 0, sizeof (buffer)) ;
+	psf_binheader_readf (psf, "b", buffer, 23) ;
+
+	if (buffer [22] != 0x1A)
+		return SFE_XI_BAD_HEADER ;
+
+	buffer [22] = 0 ;
+	for (k = 21 ; k >= 0 && buffer [k] == ' ' ; k --)
+		buffer [k] = 0 ;
+
+	psf_log_printf (psf, "Extended Instrument : %s\n", buffer) ;
+	psf_store_string (psf, SF_STR_TITLE, buffer) ;
+
+	psf_binheader_readf (psf, "be2", buffer, 20, &version) ;
+	buffer [19] = 0 ;
+	for (k = 18 ; k >= 0 && buffer [k] == ' ' ; k --)
+		buffer [k] = 0 ;
+
+	psf_log_printf (psf, "Software : %s\nVersion  : %d.%02d\n", buffer, version / 256, version % 256) ;
+	psf_store_string (psf, SF_STR_SOFTWARE, buffer) ;
+
+	/* Jump note numbers (96), volume envelope (48), pan envelope (48),
+	** volume points (1), pan points (1)
+	*/
+	psf_binheader_readf (psf, "j", 96 + 48 + 48 + 1 + 1) ;
+
+	psf_binheader_readf (psf, "b", buffer, 12) ;
+	psf_log_printf (psf, "Volume Loop\n  sustain : %u\n  begin   : %u\n  end     : %u\n",
+						buffer [0], buffer [1], buffer [2]) ;
+	psf_log_printf (psf, "Pan Loop\n  sustain : %u\n  begin   : %u\n  end     : %u\n",
+						buffer [3], buffer [4], buffer [5]) ;
+	psf_log_printf (psf, "Envelope Flags\n  volume  : 0x%X\n  pan     : 0x%X\n",
+				buffer [6] & 0xFF, buffer [7] & 0xFF) ;
+
+	psf_log_printf (psf, "Vibrato\n  type    : %u\n  sweep   : %u\n  depth   : %u\n  rate    : %u\n",
+				buffer [8], buffer [9], buffer [10], buffer [11]) ;
+
+	/*
+	** Read fade_out then jump reserved (2 bytes) and ???? (20 bytes) and
+	** sample_count.
+	*/
+	psf_binheader_readf (psf, "e2j2", &fade_out, 2 + 20, &sample_count) ;
+	psf_log_printf (psf, "Fade out  : %d\n", fade_out) ;
+
+	/* XI file can contain up to 16 samples. */
+	if (sample_count > MAX_XI_SAMPLES)
+		return SFE_XI_EXCESS_SAMPLES ;
+
+	if (psf->instrument == NULL && (psf->instrument = psf_instrument_alloc ()) == NULL)
+		return SFE_MALLOC_FAILED ;
+
+	psf->instrument->basenote = 0 ;
+	/* Log all data for each sample. */
+	for (k = 0 ; k < sample_count ; k++)
+	{	psf_binheader_readf (psf, "e444", &(sample_sizes [k]), &loop_begin, &loop_end) ;
+
+		/* Read 5 know bytes, 1 unknown byte and 22 name bytes. */
+		psf_binheader_readf (psf, "bb", buffer, 6, name, 22) ;
+		name [21] = 0 ;
+
+		psf_log_printf (psf, "Sample #%d\n  name    : %s\n", k + 1, name) ;
+
+		psf_log_printf (psf, "  size    : %d\n", sample_sizes [k]) ;
+
+		psf_log_printf (psf, "  loop\n    begin : %d\n    end   : %d\n", loop_begin, loop_end) ;
+
+		psf_log_printf (psf, "  volume  : %u\n  f. tune : %d\n  flags   : 0x%02X ",
+					buffer [0] & 0xFF, buffer [1] & 0xFF, buffer [2] & 0xFF) ;
+
+		psf_log_printf (psf, " (") ;
+		if (buffer [2] & 1)
+			psf_log_printf (psf, " Loop") ;
+		if (buffer [2] & 2)
+			psf_log_printf (psf, " PingPong") ;
+		psf_log_printf (psf, (buffer [2] & 16) ? " 16bit" : " 8bit") ;
+		psf_log_printf (psf, " )\n") ;
+
+		psf_log_printf (psf, "  pan     : %u\n  note    : %d\n  namelen : %d\n",
+					buffer [3] & 0xFF, buffer [4], buffer [5]) ;
+
+		psf->instrument->basenote = buffer [4] ;
+		if (buffer [2] & 1)
+		{	psf->instrument->loop_count = 1 ;
+			psf->instrument->loops [0].mode = (buffer [2] & 2) ? SF_LOOP_ALTERNATING : SF_LOOP_FORWARD ;
+			psf->instrument->loops [0].start = loop_begin ;
+			psf->instrument->loops [0].end = loop_end ;
+			} ;
+
+		if (k != 0)
+			continue ;
+
+		if (buffer [2] & 16)
+		{	psf->sf.format = SF_FORMAT_XI | SF_FORMAT_DPCM_16 ;
+			psf->bytewidth = 2 ;
+			}
+		else
+		{	psf->sf.format = SF_FORMAT_XI | SF_FORMAT_DPCM_8 ;
+			psf->bytewidth = 1 ;
+			} ;
+		} ;
+
+	while (sample_count > 1 && sample_sizes [sample_count - 1] == 0)
+		sample_count -- ;
+
+	/* Currently, we can only handle 1 sample per file. */
+
+	if (sample_count > 2)
+	{	psf_log_printf (psf, "*** Sample count is less than 16 but more than 1.\n") ;
+		psf_log_printf (psf, "  sample count : %d    sample_sizes [%d] : %d\n",
+						sample_count, sample_count - 1, sample_sizes [sample_count - 1]) ;
+		return SFE_XI_EXCESS_SAMPLES ;
+		} ;
+
+	psf->datalength = sample_sizes [0] ;
+
+	psf->dataoffset = psf_ftell (psf) ;
+	if (psf->dataoffset < 0)
+	{	psf_log_printf (psf, "*** Bad Data Offset : %D\n", psf->dataoffset) ;
+		return SFE_BAD_OFFSET ;
+		} ;
+	psf_log_printf (psf, "Data Offset : %D\n", psf->dataoffset) ;
+
+	if (psf->dataoffset + psf->datalength > psf->filelength)
+	{	psf_log_printf (psf, "*** File seems to be truncated. Should be at least %D bytes long.\n",
+				psf->dataoffset + sample_sizes [0]) ;
+		psf->datalength = psf->filelength - psf->dataoffset ;
+		} ;
+
+	if (psf_fseek (psf, psf->dataoffset, SEEK_SET) != psf->dataoffset)
+		return SFE_BAD_SEEK ;
+
+	psf->endian = SF_ENDIAN_LITTLE ;
+	psf->sf.channels = 1 ; /* Always mono */
+	psf->sf.samplerate = 44100 ; /* Always */
+
+	psf->blockwidth = psf->sf.channels * psf->bytewidth ;
+
+	if (! psf->sf.frames && psf->blockwidth)
+		psf->sf.frames = (psf->filelength - psf->dataoffset) / psf->blockwidth ;
+
+	psf->instrument->gain = 1 ;
+	psf->instrument->velocity_lo = psf->instrument->key_lo = 0 ;
+	psf->instrument->velocity_hi = psf->instrument->key_hi = 127 ;
+
+	return 0 ;
+} /* xi_read_header */
+
+/*==============================================================================
+*/
+
+static void dsc2s_array (XI_PRIVATE *pxi, signed char *src, int count, short *dest) ;
+static void dsc2i_array (XI_PRIVATE *pxi, signed char *src, int count, int *dest) ;
+static void dsc2f_array (XI_PRIVATE *pxi, signed char *src, int count, float *dest, float normfact) ;
+static void dsc2d_array (XI_PRIVATE *pxi, signed char *src, int count, double *dest, double normfact) ;
+
+static void dles2s_array (XI_PRIVATE *pxi, short *src, int count, short *dest) ;
+static void dles2i_array (XI_PRIVATE *pxi, short *src, int count, int *dest) ;
+static void dles2f_array (XI_PRIVATE *pxi, short *src, int count, float *dest, float normfact) ;
+static void dles2d_array (XI_PRIVATE *pxi, short *src, int count, double *dest, double normfact) ;
+
+static sf_count_t
+dpcm_read_dsc2s (SF_PRIVATE *psf, short *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	XI_PRIVATE	*pxi ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+
+	if ((pxi = psf->codec_data) == NULL)
+		return 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ucbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.scbuf, sizeof (signed char), bufferlen, psf) ;
+		dsc2s_array (pxi, ubuf.scbuf, readcount, ptr + total) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* dpcm_read_dsc2s */
+
+static sf_count_t
+dpcm_read_dsc2i (SF_PRIVATE *psf, int *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	XI_PRIVATE	*pxi ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+
+	if ((pxi = psf->codec_data) == NULL)
+		return 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ucbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.scbuf, sizeof (signed char), bufferlen, psf) ;
+		dsc2i_array (pxi, ubuf.scbuf, readcount, ptr + total) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* dpcm_read_dsc2i */
+
+static sf_count_t
+dpcm_read_dsc2f (SF_PRIVATE *psf, float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	XI_PRIVATE	*pxi ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+	float		normfact ;
+
+	if ((pxi = psf->codec_data) == NULL)
+		return 0 ;
+
+	normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x80) : 1.0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ucbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.scbuf, sizeof (signed char), bufferlen, psf) ;
+		dsc2f_array (pxi, ubuf.scbuf, readcount, ptr + total, normfact) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* dpcm_read_dsc2f */
+
+static sf_count_t
+dpcm_read_dsc2d (SF_PRIVATE *psf, double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	XI_PRIVATE	*pxi ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+	double		normfact ;
+
+	if ((pxi = psf->codec_data) == NULL)
+		return 0 ;
+
+	normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((double) 0x80) : 1.0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ucbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.scbuf, sizeof (signed char), bufferlen, psf) ;
+		dsc2d_array (pxi, ubuf.scbuf, readcount, ptr + total, normfact) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* dpcm_read_dsc2d */
+
+/*------------------------------------------------------------------------------
+*/
+
+static sf_count_t
+dpcm_read_dles2s (SF_PRIVATE *psf, short *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	XI_PRIVATE	*pxi ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+
+	if ((pxi = psf->codec_data) == NULL)
+		return 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.sbuf, sizeof (short), bufferlen, psf) ;
+		dles2s_array (pxi, ubuf.sbuf, readcount, ptr + total) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* dpcm_read_dles2s */
+
+static sf_count_t
+dpcm_read_dles2i (SF_PRIVATE *psf, int *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	XI_PRIVATE	*pxi ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+
+	if ((pxi = psf->codec_data) == NULL)
+		return 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.sbuf, sizeof (short), bufferlen, psf) ;
+		dles2i_array (pxi, ubuf.sbuf, readcount, ptr + total) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* dpcm_read_dles2i */
+
+static sf_count_t
+dpcm_read_dles2f (SF_PRIVATE *psf, float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	XI_PRIVATE	*pxi ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+	float		normfact ;
+
+	if ((pxi = psf->codec_data) == NULL)
+		return 0 ;
+
+	normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x8000) : 1.0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.sbuf, sizeof (short), bufferlen, psf) ;
+		dles2f_array (pxi, ubuf.sbuf, readcount, ptr + total, normfact) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* dpcm_read_dles2f */
+
+static sf_count_t
+dpcm_read_dles2d (SF_PRIVATE *psf, double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	XI_PRIVATE	*pxi ;
+	int			bufferlen, readcount ;
+	sf_count_t	total = 0 ;
+	double		normfact ;
+
+	if ((pxi = psf->codec_data) == NULL)
+		return 0 ;
+
+	normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((double) 0x8000) : 1.0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		readcount = (int) psf_fread (ubuf.sbuf, sizeof (short), bufferlen, psf) ;
+		dles2d_array (pxi, ubuf.sbuf, readcount, ptr + total, normfact) ;
+		total += readcount ;
+		if (readcount < bufferlen)
+			break ;
+		len -= readcount ;
+		} ;
+
+	return total ;
+} /* dpcm_read_dles2d */
+
+/*==============================================================================
+*/
+
+static void s2dsc_array (XI_PRIVATE *pxi, const short *src, signed char *dest, int count) ;
+static void i2dsc_array (XI_PRIVATE *pxi, const int *src, signed char *dest, int count) ;
+static void f2dsc_array (XI_PRIVATE *pxi, const float *src, signed char *dest, int count, float normfact) ;
+static void d2dsc_array (XI_PRIVATE *pxi, const double *src, signed char *dest, int count, double normfact) ;
+
+static void	s2dles_array (XI_PRIVATE *pxi, const short *src, short *dest, int count) ;
+static void i2dles_array (XI_PRIVATE *pxi, const int *src, short *dest, int count) ;
+static void f2dles_array (XI_PRIVATE *pxi, const float *src, short *dest, int count, float normfact) ;
+static void d2dles_array (XI_PRIVATE *pxi, const double *src, short *dest, int count, double normfact) ;
+
+
+static sf_count_t
+dpcm_write_s2dsc (SF_PRIVATE *psf, const short *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	XI_PRIVATE	*pxi ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	if ((pxi = psf->codec_data) == NULL)
+		return 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ucbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		s2dsc_array (pxi, ptr + total, ubuf.scbuf, bufferlen) ;
+		writecount = (int) psf_fwrite (ubuf.scbuf, sizeof (signed char), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* dpcm_write_s2dsc */
+
+static sf_count_t
+dpcm_write_i2dsc (SF_PRIVATE *psf, const int *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	XI_PRIVATE	*pxi ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	if ((pxi = psf->codec_data) == NULL)
+		return 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ucbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		i2dsc_array (pxi, ptr + total, ubuf.scbuf, bufferlen) ;
+		writecount = (int) psf_fwrite (ubuf.scbuf, sizeof (signed char), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* dpcm_write_i2dsc */
+
+static sf_count_t
+dpcm_write_f2dsc (SF_PRIVATE *psf, const float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	XI_PRIVATE	*pxi ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+	float		normfact ;
+
+	if ((pxi = psf->codec_data) == NULL)
+		return 0 ;
+
+	normfact = (psf->norm_float == SF_TRUE) ? (1.0 * 0x7F) : 1.0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ucbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		f2dsc_array (pxi, ptr + total, ubuf.scbuf, bufferlen, normfact) ;
+		writecount = (int) psf_fwrite (ubuf.scbuf, sizeof (signed char), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* dpcm_write_f2dsc */
+
+static sf_count_t
+dpcm_write_d2dsc (SF_PRIVATE *psf, const double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	XI_PRIVATE	*pxi ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+	double		normfact ;
+
+	if ((pxi = psf->codec_data) == NULL)
+		return 0 ;
+
+	normfact = (psf->norm_double == SF_TRUE) ? (1.0 * 0x7F) : 1.0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.ucbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		d2dsc_array (pxi, ptr + total, ubuf.scbuf, bufferlen, normfact) ;
+		writecount = (int) psf_fwrite (ubuf.scbuf, sizeof (signed char), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* dpcm_write_d2dsc */
+
+
+static sf_count_t
+dpcm_write_s2dles (SF_PRIVATE *psf, const short *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	XI_PRIVATE	*pxi ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	if ((pxi = psf->codec_data) == NULL)
+		return 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		s2dles_array (pxi, ptr + total, ubuf.sbuf, bufferlen) ;
+		writecount = (int) psf_fwrite (ubuf.sbuf, sizeof (short), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* dpcm_write_s2dles */
+
+static sf_count_t
+dpcm_write_i2dles (SF_PRIVATE *psf, const int *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	XI_PRIVATE	*pxi ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+
+	if ((pxi = psf->codec_data) == NULL)
+		return 0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		i2dles_array (pxi, ptr + total, ubuf.sbuf, bufferlen) ;
+		writecount = (int) psf_fwrite (ubuf.sbuf, sizeof (short), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* dpcm_write_i2dles */
+
+static sf_count_t
+dpcm_write_f2dles (SF_PRIVATE *psf, const float *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	XI_PRIVATE	*pxi ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+	float		normfact ;
+
+	if ((pxi = psf->codec_data) == NULL)
+		return 0 ;
+
+	normfact = (psf->norm_float == SF_TRUE) ? (1.0 * 0x7FFF) : 1.0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		f2dles_array (pxi, ptr + total, ubuf.sbuf, bufferlen, normfact) ;
+		writecount = (int) psf_fwrite (ubuf.sbuf, sizeof (short), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* dpcm_write_f2dles */
+
+static sf_count_t
+dpcm_write_d2dles (SF_PRIVATE *psf, const double *ptr, sf_count_t len)
+{	BUF_UNION	ubuf ;
+	XI_PRIVATE	*pxi ;
+	int			bufferlen, writecount ;
+	sf_count_t	total = 0 ;
+	double		normfact ;
+
+	if ((pxi = psf->codec_data) == NULL)
+		return 0 ;
+
+	normfact = (psf->norm_double == SF_TRUE) ? (1.0 * 0x7FFF) : 1.0 ;
+
+	bufferlen = ARRAY_LEN (ubuf.sbuf) ;
+
+	while (len > 0)
+	{	if (len < bufferlen)
+			bufferlen = (int) len ;
+		d2dles_array (pxi, ptr + total, ubuf.sbuf, bufferlen, normfact) ;
+		writecount = (int) psf_fwrite (ubuf.sbuf, sizeof (short), bufferlen, psf) ;
+		total += writecount ;
+		if (writecount < bufferlen)
+			break ;
+		len -= writecount ;
+		} ;
+
+	return total ;
+} /* dpcm_write_d2dles */
+
+
+/*==============================================================================
+*/
+
+static void
+dsc2s_array (XI_PRIVATE *pxi, signed char *src, int count, short *dest)
+{	signed char	last_val ;
+	int			k ;
+
+	last_val = pxi->last_16 >> 8 ;
+
+	for (k = 0 ; k < count ; k++)
+	{	last_val += src [k] ;
+		dest [k] = arith_shift_left (last_val, 8) ;
+		} ;
+
+	pxi->last_16 = arith_shift_left (last_val, 8) ;
+} /* dsc2s_array */
+
+static void
+dsc2i_array (XI_PRIVATE *pxi, signed char *src, int count, int *dest)
+{	signed char	last_val ;
+	int			k ;
+
+	last_val = pxi->last_16 >> 8 ;
+
+	for (k = 0 ; k < count ; k++)
+	{	last_val += src [k] ;
+		dest [k] = arith_shift_left (last_val, 24) ;
+		} ;
+
+	pxi->last_16 = arith_shift_left (last_val, 8) ;
+} /* dsc2i_array */
+
+static void
+dsc2f_array (XI_PRIVATE *pxi, signed char *src, int count, float *dest, float normfact)
+{	signed char	last_val ;
+	int			k ;
+
+	last_val = pxi->last_16 >> 8 ;
+
+	for (k = 0 ; k < count ; k++)
+	{	last_val += src [k] ;
+		dest [k] = last_val * normfact ;
+		} ;
+
+	pxi->last_16 = arith_shift_left (last_val, 8) ;
+} /* dsc2f_array */
+
+static void
+dsc2d_array (XI_PRIVATE *pxi, signed char *src, int count, double *dest, double normfact)
+{	signed char	last_val ;
+	int			k ;
+
+	last_val = pxi->last_16 >> 8 ;
+
+	for (k = 0 ; k < count ; k++)
+	{	last_val += src [k] ;
+		dest [k] = last_val * normfact ;
+		} ;
+
+	pxi->last_16 = arith_shift_left (last_val, 8) ;
+} /* dsc2d_array */
+
+/*------------------------------------------------------------------------------
+*/
+
+static void
+s2dsc_array (XI_PRIVATE *pxi, const short *src, signed char *dest, int count)
+{	signed char	last_val, current ;
+	int			k ;
+
+	last_val = pxi->last_16 >> 8 ;
+
+	for (k = 0 ; k < count ; k++)
+	{	current = src [k] >> 8 ;
+		dest [k] = current - last_val ;
+		last_val = current ;
+		} ;
+
+	pxi->last_16 = arith_shift_left (last_val, 8) ;
+} /* s2dsc_array */
+
+static void
+i2dsc_array (XI_PRIVATE *pxi, const int *src, signed char *dest, int count)
+{	signed char	last_val, current ;
+	int			k ;
+
+	last_val = pxi->last_16 >> 8 ;
+
+	for (k = 0 ; k < count ; k++)
+	{	current = src [k] >> 24 ;
+		dest [k] = current - last_val ;
+		last_val = current ;
+		} ;
+
+	pxi->last_16 = arith_shift_left (last_val, 8) ;
+} /* i2dsc_array */
+
+static void
+f2dsc_array (XI_PRIVATE *pxi, const float *src, signed char *dest, int count, float normfact)
+{	signed char	last_val, current ;
+	int			k ;
+
+	last_val = pxi->last_16 >> 8 ;
+
+	for (k = 0 ; k < count ; k++)
+	{	current = psf_lrintf (src [k] * normfact) ;
+		dest [k] = current - last_val ;
+		last_val = current ;
+		} ;
+
+	pxi->last_16 = arith_shift_left (last_val, 8) ;
+} /* f2dsc_array */
+
+static void
+d2dsc_array (XI_PRIVATE *pxi, const double *src, signed char *dest, int count, double normfact)
+{	signed char	last_val, current ;
+	int			k ;
+
+	last_val = pxi->last_16 >> 8 ;
+
+	for (k = 0 ; k < count ; k++)
+	{	current = psf_lrint (src [k] * normfact) ;
+		dest [k] = current - last_val ;
+		last_val = current ;
+		} ;
+
+	pxi->last_16 = arith_shift_left (last_val, 8) ;
+} /* d2dsc_array */
+
+/*==============================================================================
+*/
+
+static void
+dles2s_array (XI_PRIVATE *pxi, short *src, int count, short *dest)
+{	short	last_val ;
+	int		k ;
+
+	last_val = pxi->last_16 ;
+
+	for (k = 0 ; k < count ; k++)
+	{	last_val += LE2H_16 (src [k]) ;
+		dest [k] = last_val ;
+		} ;
+
+	pxi->last_16 = last_val ;
+} /* dles2s_array */
+
+static void
+dles2i_array (XI_PRIVATE *pxi, short *src, int count, int *dest)
+{	short	last_val ;
+	int		k ;
+
+	last_val = pxi->last_16 ;
+
+	for (k = 0 ; k < count ; k++)
+	{	last_val += LE2H_16 (src [k]) ;
+		dest [k] = arith_shift_left (last_val, 16) ;
+		} ;
+
+	pxi->last_16 = last_val ;
+} /* dles2i_array */
+
+static void
+dles2f_array (XI_PRIVATE *pxi, short *src, int count, float *dest, float normfact)
+{	short	last_val ;
+	int		k ;
+
+	last_val = pxi->last_16 ;
+
+	for (k = 0 ; k < count ; k++)
+	{	last_val += LE2H_16 (src [k]) ;
+		dest [k] = last_val * normfact ;
+		} ;
+
+	pxi->last_16 = last_val ;
+} /* dles2f_array */
+
+static void
+dles2d_array (XI_PRIVATE *pxi, short *src, int count, double *dest, double normfact)
+{	short	last_val ;
+	int		k ;
+
+	last_val = pxi->last_16 ;
+
+	for (k = 0 ; k < count ; k++)
+	{	last_val += LE2H_16 (src [k]) ;
+		dest [k] = last_val * normfact ;
+		} ;
+
+	pxi->last_16 = last_val ;
+} /* dles2d_array */
+
+/*------------------------------------------------------------------------------
+*/
+
+static void
+s2dles_array (XI_PRIVATE *pxi, const short *src, short *dest, int count)
+{	short	diff, last_val ;
+	int		k ;
+
+	last_val = pxi->last_16 ;
+
+	for (k = 0 ; k < count ; k++)
+	{	diff = src [k] - last_val ;
+		dest [k] = LE2H_16 (diff) ;
+		last_val = src [k] ;
+		} ;
+
+	pxi->last_16 = last_val ;
+} /* s2dles_array */
+
+static void
+i2dles_array (XI_PRIVATE *pxi, const int *src, short *dest, int count)
+{	short	diff, last_val ;
+	int		k ;
+
+	last_val = pxi->last_16 ;
+
+	for (k = 0 ; k < count ; k++)
+	{	diff = (src [k] >> 16) - last_val ;
+		dest [k] = LE2H_16 (diff) ;
+		last_val = src [k] >> 16 ;
+		} ;
+
+	pxi->last_16 = last_val ;
+} /* i2dles_array */
+
+static void
+f2dles_array (XI_PRIVATE *pxi, const float *src, short *dest, int count, float normfact)
+{	short	diff, last_val, current ;
+	int		k ;
+
+	last_val = pxi->last_16 ;
+
+	for (k = 0 ; k < count ; k++)
+	{	current = psf_lrintf (src [k] * normfact) ;
+		diff = current - last_val ;
+		dest [k] = LE2H_16 (diff) ;
+		last_val = current ;
+		} ;
+
+	pxi->last_16 = last_val ;
+} /* f2dles_array */
+
+static void
+d2dles_array (XI_PRIVATE *pxi, const double *src, short *dest, int count, double normfact)
+{	short	diff, last_val, current ;
+	int		k ;
+
+	last_val = pxi->last_16 ;
+
+	for (k = 0 ; k < count ; k++)
+	{	current = psf_lrint (src [k] * normfact) ;
+		diff = current - last_val ;
+		dest [k] = LE2H_16 (diff) ;
+		last_val = current ;
+		} ;
+
+	pxi->last_16 = last_val ;
+} /* d2dles_array */
diff --git a/vcpkg.json b/vcpkg.json
new file mode 100644
index 0000000..de8a948
--- /dev/null
+++ b/vcpkg.json
@@ -0,0 +1,39 @@
+{
+    "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json",
+    "name": "libsndfile",
+    "description": "A library for reading and writing audio files",
+    "version-semver": "1.2.0",
+    "default-features": [
+        "external-libs",
+        "mpeg"
+    ],
+    "features": {
+        "external-libs": {
+            "description": "Enable FLAC, Vorbis, and Opus codecs",
+            "dependencies": [
+                "libvorbis",
+                "libflac",
+                "opus"
+            ]
+        },
+        "mpeg": {
+            "description": "Enable MPEG codecs",
+            "dependencies": [
+                "mpg123",
+                "mp3lame"
+            ]
+        },
+        "regtest": {
+            "description": "Build regtest",
+            "dependencies": [
+                "sqlite3"
+            ]
+        },
+        "experimental": {
+            "description": "Enable experimental code",
+            "dependencies": [
+                "speex"
+            ]
+        }
+    }
+}