Assuming the Android Ndk is installed at location `/path/to/toolchain`, building
libsndfile for Android (arm-linux-androideabi) should be as simple as:
```
-./autogen.sh
+autoreconf -vif
export ANDROID_TOOLCHAIN_HOME=/path/to/android/toolchain
./Scripts/android-configure.sh
make
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).
-## [Unreleased]
+## [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
list (APPEND VCPKG_MANIFEST_FEATURES "regtest")
endif ()
-project(libsndfile VERSION 1.1.0)
+project(libsndfile VERSION 1.2.0)
#
# Variables
DESCRIPTION "Free Lossless Audio Codec Library"
PURPOSE "Enables FLAC support"
)
-set_package_properties (Lame PROPERTIES
+set_package_properties (mp3lame PROPERTIES
TYPE RECOMMENDED
URL "https://lame.sourceforge.io/"
DESCRIPTION "High quality MPEG Audio Layer III (MP3) encoder"
configure_file (src/config.h.cmake src/config.h)
-configure_file (include/sndfile.h.in include/sndfile.h)
-
if (INSTALL_PKGCONFIG_MODULE)
set (prefix ${CMAKE_INSTALL_PREFIX})
endif ()
if (ENABLE_MPEG)
set (EXTERNAL_MPEG_REQUIRE "libmpg123")
- get_filename_component(LAME_WE ${LAME_LIBRARY} NAME_WE)
- if (LAME_HIP_LIBRARY)
- get_filename_component(LAME_HIP_WE ${LAME_HIP_LIBRARY} NAME_WE)
- endif ()
- if (CMAKE_IMPORT_LIBRARY_PREFIX)
- string (REGEX REPLACE "^${CMAKE_IMPORT_LIBRARY_PREFIX}" "" LAME_WE_NO_PREFIX ${LAME_WE})
- if (LAME_HIP_LIBRARY)
- string (REGEX REPLACE "^${CMAKE_IMPORT_LIBRARY_PREFIX}" "" LAME_HIP_WE_NO_PREFIX ${LAME_HIP_WE})
- endif ()
- endif ()
- set (EXTERNAL_MPEG_LIBS "-l${LAME_WE_NO_PREFIX}")
- if (LAME_HIP_LIBRARY)
- set (EXTERNAL_MPEG_LIBS "${MPEG_LIBS} -l${LAME_HIP_WE}")
- endif ()
+ set (EXTERNAL_MPEG_LIBS "-lmp3lame")
endif ()
configure_file (sndfile.pc.in sndfile.pc @ONLY)
# Public libsndfile headers
set (sndfile_HDRS
+ include/sndfile.h
include/sndfile.hh
- ${CMAKE_CURRENT_BINARY_DIR}/include/sndfile.h
)
#
$<$<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}>:Lame::Lame>
+ $<$<BOOL:${HAVE_MPEG}>:mp3lame::mp3lame>
)
set_target_properties (sndfile PROPERTIES
PUBLIC_HEADER "${sndfile_HDRS}"
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
SYMBOL_FILES = src/Symbols.gnu-binutils src/Symbols.darwin src/libsndfile-1.def src/Symbols.os2 src/Symbols.static
-EXTRA_DIST += include/sndfile.h.in src/config.h.in src/test_endswap.tpl src/test_endswap.def \
+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/make-static-lib-hidden-privates.sh \
src/config.h.cmake
#===============================================================================
lib_LTLIBRARIES = src/libsndfile.la
-include_HEADERS = include/sndfile.hh
-nodist_include_HEADERS = include/sndfile.h
+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)
Once the build environment has been set up, building and testing libsndfile is
as simple as:
- ./autogen.sh
+ autoreconf -vif
./configure --enable-werror
make
make check
ifeq ($(disable_ogg_crc), true)
echo "Ogg/CRC enabled"
(cd Build && git clone https://github.com/xiph/ogg $(ogg_version))
- (cd Build/$(ogg_version) && ./autogen.sh && CFLAGS=-fPIC ./configure $(config_options) --disable-crc && make all install)
+ (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))
touch $@
configure : configure.ac
- ./autogen.sh
+ autoreconf -vif
Build/Stamp/configure : Build/Stamp/install-libs configure
PKG_CONFIG_LIBDIR=Build/lib/pkgconfig ./configure
+++ /dev/null
-#!/bin/sh
-# Run this to set up the build system: configure, makefiles, etc.
-# (based on the version in enlightenment's cvs)
-
-package="libsndfile"
-
-olddir=`pwd`
-srcdir=`dirname $0`
-test -z "$srcdir" && srcdir=.
-
-cd "$srcdir"
-DIE=0
-
-printf "checking for autogen ... "
-result="yes"
-(autogen --version) < /dev/null > /dev/null 2>&1 || {
- echo
- echo "You must have GNU autogen installed to compile $package."
- echo "Download the appropriate package for your distribution,"
- echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/"
- result="no"
- DIE=1
-}
-echo $result
-
-printf "checking for autoconf ... "
-result="yes"
-(autoconf --version) < /dev/null > /dev/null 2>&1 || {
- echo
- echo "You must have autoconf installed to compile $package."
- echo "Download the appropriate package for your distribution,"
- echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/"
- result="no"
- DIE=1
-}
-echo $result
-
-VERSIONGREP="sed -e s/.*[^0-9\.]\([0-9][0-9]*\.[0-9][0-9]*\).*/\1/"
-VERSIONMKMAJ="sed -e s/\([0-9][0-9]*\)[^0-9].*/\\1/"
-VERSIONMKMIN="sed -e s/.*[0-9][0-9]*\.//"
-
-# do we need automake?
-if test -r Makefile.am; then
- AM_OPTIONS=`fgrep AUTOMAKE_OPTIONS Makefile.am`
- AM_NEEDED=`echo $AM_OPTIONS | $VERSIONGREP`
- if test x"$AM_NEEDED" = "x$AM_OPTIONS"; then
- AM_NEEDED=""
- fi
- if test -z $AM_NEEDED; then
- printf "checking for automake ... "
- AUTOMAKE=automake
- ACLOCAL=aclocal
- if ($AUTOMAKE --version < /dev/null > /dev/null 2>&1); then
- echo "yes"
- else
- echo "no"
- AUTOMAKE=
- fi
- else
- printf "checking for automake $AM_NEEDED or later ... "
- majneeded=`echo $AM_NEEDED | $VERSIONMKMAJ`
- minneeded=`echo $AM_NEEDED | $VERSIONMKMIN`
- for am in automake-$AM_NEEDED automake$AM_NEEDED \
- automake automake-1.7 automake-1.8 automake-1.9 automake-1.10; do
- ($am --version < /dev/null > /dev/null 2>&1) || continue
- ver=`$am --version < /dev/null | head -n 1 | $VERSIONGREP`
- maj=`echo $ver | $VERSIONMKMAJ`
- min=`echo $ver | $VERSIONMKMIN`
- if test $maj -eq $majneeded -a $min -ge $minneeded; then
- AUTOMAKE=$am
- echo $AUTOMAKE
- break
- fi
- done
- test -z $AUTOMAKE && echo "no"
- printf "checking for aclocal $AM_NEEDED or later ... "
- for ac in aclocal-$AM_NEEDED aclocal$AM_NEEDED \
- aclocal aclocal-1.7 aclocal-1.8 aclocal-1.9 aclocal-1.10; do
- ($ac --version < /dev/null > /dev/null 2>&1) || continue
- ver=`$ac --version < /dev/null | head -n 1 | $VERSIONGREP`
- maj=`echo $ver | $VERSIONMKMAJ`
- min=`echo $ver | $VERSIONMKMIN`
- if test $maj -eq $majneeded -a $min -ge $minneeded; then
- ACLOCAL=$ac
- echo $ACLOCAL
- break
- fi
- done
- test -z $ACLOCAL && echo "no"
- fi
- test -z $AUTOMAKE || test -z $ACLOCAL && {
- echo
- echo "You must have automake installed to compile $package."
- echo "Download the appropriate package for your distribution,"
- echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/"
- exit 1
- }
-fi
-
-printf "checking for libtool ... "
-for LIBTOOLIZE in libtoolize glibtoolize nope; do
- ($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 && break
-done
-if test x$LIBTOOLIZE = xnope; then
- echo "nope."
- LIBTOOLIZE=libtoolize
-else
- echo $LIBTOOLIZE
-fi
-($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 || {
- echo
- echo "You must have libtool installed to compile $package."
- echo "Download the appropriate package for your system,"
- echo "or get the source from one of the GNU ftp sites"
- echo "listed in http://www.gnu.org/order/ftp.html"
- DIE=1
-}
-
-printf "checking for pkg-config ... "
-result="yes"
-(pkg-config --version) < /dev/null > /dev/null 2>&1 || {
- echo
- echo "You must have pkg-config installed to compile $package."
- echo "Download the appropriate package for your distribution."
- result="no"
- DIE=1
-}
-echo $result
-
-
-printf "checking for python ... "
-result="yes"
-(python --version) < /dev/null > /dev/null 2>&1 || {
- echo
- echo "You must have Python installed to compile $package."
- echo "Download the appropriate package for your distribution,"
- echo "or get the source tarball at http://python.org/"
- result="no"
- DIE=1
-}
-echo $result
-
-if test "$DIE" -eq 1; then
- exit 1
-fi
-
-echo "Generating configuration files for $package, please wait ... "
-
-echo " $ACLOCAL $ACLOCAL_FLAGS"
-$ACLOCAL $ACLOCAL_FLAGS || exit 1
-echo " $LIBTOOLIZE --automake --force"
-$LIBTOOLIZE --automake --force || exit 1
-echo " autoheader"
-autoheader || exit 1
-echo " $AUTOMAKE --add-missing $AUTOMAKE_FLAGS"
-$AUTOMAKE --add-missing $AUTOMAKE_FLAGS || exit 1
-echo " autoconf"
-autoconf || exit 1
-
-# Generate the src/cmake-config.h.in from src/config.h.in.
-# CMake process src/cmake-config.h to create src/config.h.
-rm -f src/config.h src/cmake-config.h
-
-cd $olddir
-
-if test -d .git/ ; then
- fprecommit=.git/hooks/pre-commit
- if test ! -f $fprecommit ; then
- echo
- echo "Installing git pre-commit hook for this project."
- printf "#/bin/sh\nexec Scripts/git-pre-commit-hook\n" > $fprecommit
- chmod u+x $fprecommit
- echo
- fi
- fi
-
+++ /dev/null
-# - Find lame
-# Find the native lame includes and libraries
-#
-# LAME_INCLUDE_DIRS - where to find lame.h, etc.
-# LAME_LIBRARIES - List of libraries when using lame.
-# LAME_FOUND - True if Lame found.
-
-if (LAME_INCLUDE_DIR)
- # Already in cache, be silent
- set(LAME_FIND_QUIETLY TRUE)
-endif ()
-
-find_path (LAME_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 (LAME_LIBRARY
- NAMES
- mp3lame
- mp3lame_static
- libmp3lame
- libmp3lame_static
- libmp3lame-static
- HINTS
- ${LAME_ROOT}
- )
-
-find_library (LAME_HIP_LIBRARY
- NAMES
- mpghip-static
- libmpghip-static
- HINTS
- ${LAME_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 (Lame
- REQUIRED_VARS
- LAME_LIBRARY
- LAME_INCLUDE_DIR
- )
-
-if (LAME_FOUND)
- set (LAME_LIBRARIES ${LAME_LIBRARY} ${LAME_HIP_LIBRARY})
- set (LAME_INCLUDE_DIRS ${LAME_INCLUDE_DIR})
-
- if (NOT TARGET Lame::Lame)
- add_library (Lame::Lame UNKNOWN IMPORTED)
- set_target_properties (Lame::Lame PROPERTIES
- INTERFACE_INCLUDE_DIRECTORIES "${LAME_INCLUDE_DIRS}"
- IMPORTED_LOCATION "${LAME_LIBRARY}"
- )
- if (LAME_HIP_LIBRARY)
- set_property (TARGET Lame::Lame APPEND PROPERTY
- INTERFACE_LINK_LIBRARIES "${LAME_HIP_LIBRARY}")
- endif ()
- endif ()
-endif ()
-
-mark_as_advanced(LAME_INCLUDE_DIR LAME_LIBRARY LAME_HIP_LIBRARY)
--- /dev/null
+# - 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)
add_definitions(${LARGE_FILES_DEFINITIONS})
endif ()
-if (WIN32)
- set(TYPEOF_SF_COUNT_T __int64)
-else ()
- set(TYPEOF_SF_COUNT_T int64_t)
-endif ()
-set (SF_COUNT_MAX 0x7fffffffffffffffll)
-
if (CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
find_package (Sndio)
elseif (NOT WIN32)
set (HAVE_EXTERNAL_XIPH_LIBS 0)
endif ()
-find_package (Lame)
+find_package (mp3lame)
find_package (Mpg123 1.25.10)
-if (LAME_FOUND AND (TARGET MPG123::libmpg123))
+if (TARGET mp3lame::mp3lame AND (TARGET MPG123::libmpg123))
set (HAVE_MPEG_LIBS 1)
else ()
set (HAVE_MPEG_LIBS 0)
check_type_size (void* SIZEOF_VOIDP)
endif()
-if ((SIZEOF_OFF_T EQUAL 8) OR (SIZEOF_LOFF_T EQUAL 8) OR (SIZEOF_OFF64_T EQUAL 8))
- set (TYPEOF_SF_COUNT_T "int64_t")
- set (SF_COUNT_MAX "0x7FFFFFFFFFFFFFFFLL")
- set (SIZEOF_SF_COUNT_T 8)
-else ()
- if (WIN32)
- set (TYPEOF_SF_COUNT_T "__int64")
- set (SF_COUNT_MAX "0x7FFFFFFFFFFFFFFFLL")
- set (SIZEOF_SF_COUNT_T 8)
- else ()
- message ("")
- message ("*** The configure process has determined that this system is capable")
- message ("*** of Large File Support but has not been able to find a type which")
- message ("*** is an unambiguous 64 bit file offset.")
- message ("*** Please contact the author to help resolve this problem.")
- message ("")
- message (FATAL_ERROR "Bad file offset type.")
- endif ()
-endif ()
-
-check_type_size (${TYPEOF_SF_COUNT_T} SIZEOF_SF_COUNT_T)
-
if (NOT WIN32)
check_library_exists (m floor "" LIBM_REQUIRED)
if (LIBM_REQUIRED)
endif ()
if (SndFile_WITH_MPEG AND NOT @BUILD_SHARED_LIBS@)
- find_dependency (Lame)
+ find_dependency (mp3lame)
find_dependency (MPG123)
endif ()
dnl Require autoconf version >= 2.69
AC_PREREQ([2.69])
-AC_INIT([libsndfile],[1.1.0],[sndfile@mega-nerd.com],
+AC_INIT([libsndfile],[1.2.0],[sndfile@mega-nerd.com],
[libsndfile],[http://libsndfile.github.io/libsndfile/])
dnl Check whether we want to set defaults for CFLAGS, CXXFLAGS, CPPFLAGS and LDFLAGS
dnl This is libtool version of library, we add it to `--version-info` property.
m4_define([lt_current], [1])
-m4_define([lt_revision], [34])
+m4_define([lt_revision], [35])
m4_define([lt_age], [0])
dnl This is ABI version for linker scripts, CMake uses the same format for
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])],
- [have_ossfuzzers=yes], [have_ossfuzzers=no])
-AM_CONDITIONAL([USE_OSSFUZZERS], [test "x$have_ossfuzzers" = "xyes"])
+ [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"])
AC_CHECK_SIZEOF([int64_t], [8])
AC_CHECK_SIZEOF([long long], [8])
-dnl ====================================================================================
-dnl Find an appropriate type for sf_count_t.
-dnl On systems supporting files larger than 2 Gig, sf_count_t must be a 64 bit value.
-dnl Unfortunately there is more than one way of ensuring this so need to do some
-dnl pretty rigourous testing here.
-
dnl Check for common 64 bit file offset types.
AC_CHECK_SIZEOF([off_t], [1])
AS_CASE([$host_os],
[mingw32*], [
- TYPEOF_SF_COUNT_T="__int64"
- SF_COUNT_MAX="0x7FFFFFFFFFFFFFFFLL"
- SIZEOF_SF_COUNT_T=8
AC_DEFINE([__USE_MINGW_ANSI_STDIO], [1], [Set to 1 to use C99 printf/snprintf in MinGW.])
],
- [linux-android*], [
- TYPEOF_SF_COUNT_T="int64_t"
- SF_COUNT_MAX="0x7FFFFFFFFFFFFFFFLL"
- SIZEOF_SF_COUNT_T=8
- ],
-
[
- SIZEOF_SF_COUNT_T=0
AS_IF([test "x$ac_cv_sizeof_off_t" = "x8"], [
dnl If sizeof (off_t) is 8, no further checking is needed.
- TYPEOF_SF_COUNT_T="int64_t"
- SF_COUNT_MAX="0x7FFFFFFFFFFFFFFFLL"
- SIZEOF_SF_COUNT_T=8
], [
dnl Save the old sizeof (off_t) value and then unset it to see if it
dnl changes when Large File Support is enabled.
])
AC_CHECK_SIZEOF(off_t,1)
-
- AS_IF([test "x$ac_cv_sizeof_off_t" = "x8"], [
- TYPEOF_SF_COUNT_T="int64_t"
- SF_COUNT_MAX="0x7FFFFFFFFFFFFFFFLL"
- SIZEOF_SF_COUNT_T=8
- ], [test "x$TYPEOF_SF_COUNT_T" = "xunknown"], [
- AS_ECHO([""])
- AS_ECHO(["*** The configure process has determined that this system is capable"])
- AS_ECHO(["*** of Large File Support but has not been able to find a type which"])
- AS_ECHO(["*** is an unambiguous 64 bit file offset."])
- AS_ECHO(["*** Please contact the author to help resolve this problem."])
- AS_ECHO([""])
- AC_MSG_ERROR([[Bad file offset type.]])
- ])
])
])
-AS_CASE([$host_vendor],
- [vita], [
- TYPEOF_SF_COUNT_T="int64_t"
- SF_COUNT_MAX="0x7FFFFFFFFFFFFFFFLL"
- SIZEOF_SF_COUNT_T=8
- ])
-
-AS_IF([test "x$SIZEOF_SF_COUNT_T" = "x4"], [
- SF_COUNT_MAX="0x7FFFFFFF"
- ])
-
-AC_DEFINE_UNQUOTED([TYPEOF_SF_COUNT_T], [${TYPEOF_SF_COUNT_T}], [Set to long if unknown.])
-AC_SUBST(TYPEOF_SF_COUNT_T)
-
-AC_DEFINE_UNQUOTED([SIZEOF_SF_COUNT_T], [${SIZEOF_SF_COUNT_T}], [Set to sizeof (long) if unknown.])
-AC_SUBST(SIZEOF_SF_COUNT_T)
-
-AC_DEFINE_UNQUOTED([SF_COUNT_MAX], [${SF_COUNT_MAX}], [Set to maximum allowed value of sf_count_t type.])
-AC_SUBST(SF_COUNT_MAX)
-
AC_TYPE_SSIZE_T
dnl ====================================================================================
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_CONFIG_FILES([
Makefile Octave/Makefile
- src/version-metadata.rc include/sndfile.h
+ src/version-metadata.rc
tests/test_wrapper.sh tests/pedantic-header-test.sh
libsndfile.spec sndfile.pc
Scripts/build-test-tarball.mk
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 **samplerate** field
-that are greater than `655350` and values ​​for field **channels** that are
-greater than `1024`. These values ​​represent the maximum theoretical limit and
-may be less for specific formats.
+**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**.
| [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_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_BITRATE_MODE](#sfc_get_bitrate_mode) | Get bitrate mode. |
+| [SFC_SET_BITRATE_MODE](#sfc_set_bitrate_mode) | Set bitrate mode. |
---
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
Add a unique identifier for the new file type.
-Edit src/sndfile.h.in and find the enum containing the SF_FORMAT_XXX identifiers.
+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
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,
** and the Microsoft compiler.
*/
-typedef @TYPEOF_SF_COUNT_T@ sf_count_t ;
+typedef int64_t sf_count_t ;
#ifndef SF_COUNT_MAX
-#define SF_COUNT_MAX @SF_COUNT_MAX@
+#define SF_COUNT_MAX INT64_MAX
#endif
# libasound2-dev libflac-dev libogg-dev libopus-dev libvorbis-dev
# Compile the fuzzer.
-./autogen.sh
+autoreconf -vif
./configure --disable-shared --enable-ossfuzzers
make V=1
%define __spec_check_pre exit 0
Name: libsndfile
-Version: 1.1.0
+Version: 1.2.0
Release: 0
License: LGPL-2.1+
Summary: C library for reading and writing sound files
char *cptr ;
int instr_found = 0, mark_found = 0 ;
- if (psf->filelength > SF_PLATFORM_S64 (0xffffffff))
+ if (psf->filelength > 0xFFFFFFFFLL)
psf_log_printf (psf, "Warning : filelength > 0xffffffff. This is bad!!!!\n") ;
if ((paiff = psf->container_data) == NULL)
strptr = istr ;
while (*strptr)
{ c = *strptr++ ;
- log_putchar (psf, c) ;
+ log_putchar (psf, psf_isprint (c) ? c : '.') ;
} ;
break ;
psf->header.ptr [psf->header.indx++] = (x >> 24) ;
} /* header_put_le_int */
-#if (SIZEOF_SF_COUNT_T == 8)
-
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++] = (x >> 56) ;
} /* header_put_le_8byte */
-#else
-#error "SIZEOF_SF_COUNT_T != 8"
-#endif
-
int
psf_binheader_writef (SF_PRIVATE *psf, const char *format, ...)
{ va_list argptr ;
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 ;
- psf_fseek (psf, position, SEEK_CUR) ;
+ 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 ;
} ;
for (int i = 0 ; i < count ; i++)
{ scaled_value = src [i] * normfact ;
-#if CPU_CLIPS_POSITIVE == 0
if (scaled_value >= (1.0 * 0x7FFF))
{ dest [i] = 0x7FFF ;
continue ;
} ;
-#endif
-#if CPU_CLIPS_NEGATIVE == 0
if (scaled_value <= (-8.0 * 0x1000))
{ dest [i] = -0x7FFF - 1 ;
continue ;
} ;
-#endif
dest [i] = psf_lrintf (scaled_value) ;
} ;
for (int i = 0 ; i < count ; i++)
{ scaled_value = src [i] * normfact ;
-#if CPU_CLIPS_POSITIVE == 0
if (scaled_value >= (1.0 * 0x7FFF))
{ dest [i] = 0x7FFF ;
continue ;
} ;
-#endif
-#if CPU_CLIPS_NEGATIVE == 0
if (scaled_value <= (-8.0 * 0x1000))
{ dest [i] = -0x7FFF - 1 ;
continue ;
} ;
-#endif
dest [i] = psf_lrint (scaled_value) ;
} ;
#error "This code is not designed to be compiled with a C++ compiler."
#endif
-#ifdef INT64_C
-# define SF_PLATFORM_S64(x) INT64_C (x)
-#elif (SIZEOF_LONG == 8)
-# define SF_PLATFORM_S64(x) x##l
-#elif (SIZEOF_LONG_LONG == 8)
-# define SF_PLATFORM_S64(x) x##ll
-#elif COMPILER_IS_GCC
-# define SF_PLATFORM_S64(x) x##ll
-#elif OS_IS_WIN32
-# define SF_PLATFORM_S64(x) x##I64
-#else
-# error "Don't know how to define a 64 bit integer constant."
-#endif
-
/*
#define SF_MAX_CHANNELS 1024
-/* Max FLAC sample rate : https://xiph.org/flac/format.html */
-#define SF_MAX_SAMPLERATE 655350
-
/*
* Macros for spliting the format file of SF_INFO into container type,
/* Define to the version of this package. */
#define PACKAGE_VERSION "@CPACK_PACKAGE_VERSION_FULL@"
-/* Set to maximum allowed value of sf_count_t type. */
-#define SF_COUNT_MAX @SF_COUNT_MAX@
-
/* The size of `double', as computed by sizeof. */
@SIZEOF_DOUBLE_CODE@
/* The size of `off_t', as computed by sizeof. */
@SIZEOF_OFF_T_CODE@
-/* Set to sizeof (long) if unknown. */
-@SIZEOF_SF_COUNT_T_CODE@
-
/* The size of `short', as computed by sizeof. */
@SIZEOF_SHORT_CODE@
/* The size of `wchar_t', as computed by sizeof. */
@SIZEOF_WCHAR_T_CODE@
-/* Set to long if unknown. */
-#define TYPEOF_SF_COUNT_T @TYPEOF_SF_COUNT_T@
-
/* Enable extensions on AIX 3, Interix. */
#ifndef _ALL_SOURCE
# undef _ALL_SOURCE
{ for (int i = 0 ; i < count ; i++)
{ double tmp = scale * src [i] ;
- if (CPU_CLIPS_POSITIVE == 0 && tmp > 32767.0)
+ if (tmp > 32767.0)
dest [i] = SHRT_MAX ;
- else if (CPU_CLIPS_NEGATIVE == 0 && tmp < -32768.0)
+ else if (tmp < -32768.0)
dest [i] = SHRT_MIN ;
else
dest [i] = psf_lrint (tmp) ;
static sf_count_t
psf_get_filelen_fd (int fd)
{
-#if (SIZEOF_OFF_T == 4 && SIZEOF_SF_COUNT_T == 8 && HAVE_FSTAT64)
+#if (SIZEOF_OFF_T == 4 && HAVE_FSTAT64)
struct stat64 statbuf ;
if (fstat64 (fd, &statbuf) == -1)
} ;
#if defined (WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
- if (!pfile->use_wchar)
- return INVALID_HANDLE_VALUE ;
-
CREATEFILE2_EXTENDED_PARAMETERS cfParams = { 0 } ;
cfParams.dwSize = sizeof (CREATEFILE2_EXTENDED_PARAMETERS) ;
cfParams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL ;
#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,
while (total < len)
{ pflac->ptr = ptr + total ;
- readlen = (len - total > 0x1000000) ? 0x1000000 : (unsigned) (len - 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 ;
while (total < len)
{ pflac->ptr = ptr + total ;
- readlen = (len - total > 0x1000000) ? 0x1000000 : (unsigned) (len - 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 ;
while (total < len)
{ pflac->ptr = ptr + total ;
- readlen = (len - total > 0x1000000) ? 0x1000000 : (unsigned) (len - 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 ;
while (total < len)
{ pflac->ptr = ptr + total ;
- readlen = (len - total > 0x1000000) ? 0x1000000 : (unsigned) (len - total) ;
+ readlen = (len - total > READ_LOOP_MAX_LEN) ? READ_LOOP_MAX_LEN : (unsigned) (len - total) ;
current = flac_read_loop (psf, readlen) ;
if (current == 0)
for (int i = 0 ; i < count ; i++)
{ scaled_value = src [i] * normfact ;
- if (CPU_CLIPS_POSITIVE == 0 && scaled_value >= (1.0 * 0x7F))
+ if (scaled_value >= (1.0 * 0x7F))
{ dest [i] = 0x7F ;
continue ;
} ;
- if (CPU_CLIPS_NEGATIVE == 0 && scaled_value <= (-8.0 * 0x10))
+ if (scaled_value <= (-8.0 * 0x10))
{ dest [i] = -0x80 ;
continue ;
} ;
for (int i = 0 ; i < count ; i++)
{ scaled_value = src [i] * normfact ;
- if (CPU_CLIPS_POSITIVE == 0 && scaled_value >= (1.0 * 0x7FFF))
+ if (scaled_value >= (1.0 * 0x7FFF))
{ dest [i] = 0x7FFF ;
continue ;
} ;
- if (CPU_CLIPS_NEGATIVE == 0 && scaled_value <= (-8.0 * 0x1000))
+ if (scaled_value <= (-8.0 * 0x1000))
{ dest [i] = -0x8000 ;
continue ;
} ;
for (int i = 0 ; i < count ; i++)
{ scaled_value = src [i] * normfact ;
- if (CPU_CLIPS_POSITIVE == 0 && scaled_value >= (1.0 * 0x7FFFFF))
+ if (scaled_value >= (1.0 * 0x7FFFFF))
{ dest [i] = 0x7FFFFF ;
continue ;
} ;
- if (CPU_CLIPS_NEGATIVE == 0 && scaled_value <= (-8.0 * 0x100000))
+ if (scaled_value <= (-8.0 * 0x100000))
{ dest [i] = -0x800000 ;
continue ;
}
for (int i = 0 ; i < count ; i++)
{ scaled_value = src [i] * normfact ;
- if (CPU_CLIPS_POSITIVE == 0 && scaled_value >= (1.0 * 0x7F))
+ if (scaled_value >= (1.0 * 0x7F))
{ dest [i] = 0x7F ;
continue ;
} ;
- if (CPU_CLIPS_NEGATIVE == 0 && scaled_value <= (-8.0 * 0x10))
+ if (scaled_value <= (-8.0 * 0x10))
{ dest [i] = -0x80 ;
continue ;
} ;
for (int i = 0 ; i < count ; i++)
{ scaled_value = src [i] * normfact ;
- if (CPU_CLIPS_POSITIVE == 0 && scaled_value >= (1.0 * 0x7FFF))
+ if (scaled_value >= (1.0 * 0x7FFF))
{ dest [i] = 0x7FFF ;
continue ;
} ;
- if (CPU_CLIPS_NEGATIVE == 0 && scaled_value <= (-8.0 * 0x1000))
+ if (scaled_value <= (-8.0 * 0x1000))
{ dest [i] = -0x8000 ;
continue ;
} ;
for (int i = 0 ; i < count ; i++)
{ scaled_value = src [i] * normfact ;
- if (CPU_CLIPS_POSITIVE == 0 && scaled_value >= (1.0 * 0x7FFFFF))
+ if (scaled_value >= (1.0 * 0x7FFFFF))
{ dest [i] = 0x7FFFFF ;
continue ;
} ;
- if (CPU_CLIPS_NEGATIVE == 0 && scaled_value <= (-8.0 * 0x100000))
+ if (scaled_value <= (-8.0 * 0x100000))
{ dest [i] = -0x800000 ;
continue ;
} ;
{ for (int i = 0 ; i < count ; i++)
{ float tmp = scale * src [i] ;
- if (CPU_CLIPS_POSITIVE == 0 && tmp > 32767.0)
+ if (tmp > 32767.0)
dest [i] = SHRT_MAX ;
- else if (CPU_CLIPS_NEGATIVE == 0 && tmp < -32768.0)
+ else if (tmp < -32768.0)
dest [i] = SHRT_MIN ;
else
dest [i] = psf_lrintf (tmp) ;
else
pg72x->blocks_total = psf->datalength / pg72x->blocksize ;
- psf->sf.frames = pg72x->blocks_total * pg72x->samplesperblock ;
+ psf->sf.frames = (sf_count_t) pg72x->blocks_total * pg72x->samplesperblock ;
psf_g72x_decode_block (psf, pg72x) ;
}
pgsm610->blocks = psf->datalength / pgsm610->blocksize + 1 ;
} ;
- psf->sf.frames = pgsm610->samplesperblock * pgsm610->blocks ;
+ psf->sf.frames = (sf_count_t) pgsm610->samplesperblock * pgsm610->blocks ;
psf_fseek (psf, psf->dataoffset, SEEK_SET) ;
error = mpg123_read (pmp3d->pmh, (unsigned char *) ptr, len * sizeof (float), &done) ;
- if (error == MPG123_OK)
+ if (error == MPG123_OK || error == MPG123_DONE)
return done / sizeof (float) ;
- if (error == MPG123_DONE)
- return 0 ;
-
if (error == MPG123_NEW_FORMAT)
{ psf->error = SFE_MALFORMED_FILE ;
return -1 ;
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) ;
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_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)
+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 ;
if (buffering)
ogg_stream_reset (&odata->ostream) ;
/* Check to see if the last packet continues. */
- if (page.header [27 + page.header [26] - 1] == 255)
+ if (ogg_page_continues (&page))
{ ogg_page_search_continued_data (odata, &page) ;
/*
** If we have a continued packet, remember the offset of
** 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 ;
+ /*
+ ** 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 > 48000)
+ if (target_gp - gp > gp_rate)
{ /* Out by over a second. Try another bisection. */
break ;
}
((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 *) ;
/*
** 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 offsets.
+** 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 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 */
#define OGG_OPUS_COMMENT_PAD (512) /* Same as oggenc default */
/*
-** Opus packets can be any multiple of 2.5ms (at 48kHz). We use the recommended
+** 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)
/*
-** How long does it take for a decoder to converge (avoiding flush on seek.
+** 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 (80 * 48) /* 80 milliseconds */
+#define OGG_OPUS_PREROLL (660 * 48) /* 660 milliseconds (33 packets of 20ms) */
typedef struct
{ uint8_t version ;
{ uint32_t serialno ;
OpusHeader header ;
- /* Granule position before the current packet */
+ /* Encode: Granule position after the previous packet.
+ * Decode: Granule position after the current packet */
uint64_t pkt_pos ;
- /* Granule position at the end of the current page (encode: last completed) */
+ /* 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 */
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_seek_null_read (SF_PRIVATE *psf, sf_count_t offset) ;
-static sf_count_t ogg_opus_seek_manual (SF_PRIVATE *psf, uint64_t target_gp) ;
-static int ogg_opus_seek_page_search (SF_PRIVATE *psf, uint64_t target_gp) ;
+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 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", h->version) ;
- psf_log_printf (psf, " Channels : %d\n", h->channels) ;
- psf_log_printf (psf, " Preskip : %d samples @48kHz\n", h->preskip) ;
- psf_log_printf (psf, " Input Samplerate : %d Hz\n", h->input_samplerate) ;
- psf_log_printf (psf, " Gain : %d.%d\n", arith_shift_right (h->gain & 0xF0, 8), h->gain & 0x0F) ;
+ 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", h->channel_mapping) ; 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", h->nb_streams) ;
- psf_log_printf (psf, " streams coupled : %d\n", h->nb_coupled) ;
+ 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,", h->stream_map [i]) ;
- psf_log_printf (psf, "%d]\n", h->stream_map [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 */
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", h->version) ;
+ { psf_log_printf (psf, "Opus : Unknown / unsupported embedding scheme version: %d.\n", (int) h->version) ;
return SFE_UNIMPLEMENTED ;
} ;
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) / oopus->sr_factor) ;
+ 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
** MAY defer this action until it decodes the last packet
** completed on that page.
*/
- psf_log_printf (psf, "Opus : Mid-strem page's granule position %d is less than total samples of %d\n", oopus->pg_pos, pkt_granulepos) ;
+ 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 ;
} ;
else
nbytes = ogg_stream_pageout_fill (&odata->ostream, &odata->opage, 255 * 255) ;
if (nbytes > 0)
- { /*
- ** LibOgg documentation is noted as being bad by it's author. Ogg
- ** page header byte 26 is the segment count.
- */
- oopus->u.encode.last_segments -= odata->opage.header [26] ;
+ { oopus->u.encode.last_segments -= ogg_page_segments (&odata->opage) ;
oopus->pg_pos = oopus->pkt_pos ;
ogg_write_page (psf, &odata->opage) ;
}
{ 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) ;
} /* ogg_opus_analyze_file */
/*
-** ogg_opus_seek_null_read
+** ogg_opus_null_read
**
** Decode samples, doing nothing with them, until the desired granule position
** is reached.
*/
static sf_count_t
-ogg_opus_seek_null_read (SF_PRIVATE *psf, sf_count_t offset)
+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 ;
- sf_count_t readlen ;
-
- total = oopus->pkt_pos / oopus->sr_factor ;
- total += oopus->loc ;
+ total = (oopus->pkt_pos / oopus->sr_factor) - (oopus->len - oopus->loc) ;
for ( ; total < offset ; )
- { if (oopus->loc == oopus->len)
+ { 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 ;
/*
*/
oopus->loc = 0 ;
} ;
-
- readlen = SF_MIN ((int) (offset - total), (oopus->len - oopus->loc)) ;
- if (readlen > 0)
- { total += readlen ;
- oopus->loc += readlen ;
- } ;
} ;
return total ;
-} /* ogg_opus_seek_null_read */
+} /* 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_seek_page_search (SF_PRIVATE *psf, uint64_t target_gp)
+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 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 ;
- /* Adjust the target to give time to converge. */
- if (target_gp >= OGG_OPUS_PREROLL)
- target_gp -= OGG_OPUS_PREROLL ;
- if (target_gp < pcm_start)
- target_gp = pcm_start ;
-
- /* Seek to beginning special case */
- if (target_gp < pcm_start + (uint64_t) oopus->header.preskip)
- end = begin ;
- else
- end = oopus->u.decode.last_offset ;
-
- ogg_stream_seek_page_search (psf, odata, target_gp, pcm_start, pcm_end, &best_gp, begin, end) ;
+ /* 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) ;
- /* Gain decoder settings survive resets. */
return 0 ;
-} /* ogg_opus_seek_page_search */
+} /* 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_seek_manual (SF_PRIVATE *psf, uint64_t target_gp)
+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 (target_gp > OGG_OPUS_PREROLL)
- target_gp -= OGG_OPUS_PREROLL ;
- if (target_gp < oopus->pg_pos)
- target_gp = oopus->pg_pos ;
-
if (oopus->pg_pos > target_gp)
{ ogg_stream_reset (&odata->ostream) ;
pos = ogg_sync_fseek (psf, psf->dataoffset, SEEK_SET) ;
} ;
return 1 ;
-} /* ogg_opus_seek_manual */
+} /* ogg_opus_page_seek_manual */
static sf_count_t
ogg_opus_seek (SF_PRIVATE *psf, int mode, sf_count_t offset)
-{ OPUS_PRIVATE *oopus = (OPUS_PRIVATE *) psf->codec_data ;
- uint64_t target_gp ;
- uint64_t current ;
+{ 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. */
return PSF_SEEK_ERROR ;
} ;
- current = oopus->pkt_pos + oopus->loc * oopus->sr_factor ;
- /*
- ** Remember, there are preskip granulepos worth of samples at the front of
- ** the stream which are bunk. Also, granule positions can be offset.
- */
- target_gp = offset * oopus->sr_factor + oopus->u.decode.gp_start + oopus->header.preskip ;
+ /* 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_seek_manual (psf, target_gp) ;
- }
- else
- { /*
- ** Avoid seeking in the file if where we want is just ahead or exactly
- ** were we are. To avoid needing to flush the decoder we choose pre-
- ** roll plus 10ms.
- */
- if (target_gp < current || target_gp - current > OGG_OPUS_PREROLL + 10 * 48)
- { ret = ogg_opus_seek_page_search (psf, target_gp) ;
+ 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
** from the beginning has the advantage of finding where the
** file goes bad.
*/
- ret = ogg_opus_seek_manual (psf, target_gp) ;
+ ret = ogg_opus_page_seek_manual (psf, preroll_gp) ;
if (ret < 0)
{ /*
** If were here, and there is no error, we can be pretty
} ;
} ;
} ;
+
+ /*
+ ** 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_seek_null_read (psf, target_gp / oopus->sr_factor) ;
+ 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 */
*((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 ;
}
ogg_sync_fseek (psf, saved_offset, SEEK_SET) ;
}
- psf_log_printf (psf, "PCM offset : %d\n", vdata->pcm_start) ;
+ 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) ;
+ psf_log_printf (psf, "PCM end : %D\n", vdata->pcm_end) ;
else
psf_log_printf (psf, "PCM end : unknown\n") ;
static int
vorbis_command (SF_PRIVATE *psf, int command, void * data, int datasize)
-{ VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ;
+{ OGG_PRIVATE* odata = psf->container_data ;
+ VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ;
switch (command)
{ case SFC_SET_COMPRESSION_LEVEL :
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 ;
} ;
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->pcm_end, &best_gp, psf->dataoffset, vdata->last_page, vdata->vinfo.rate) ;
if (ret < 0)
return ret ;
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] = 127 ;
continue ;
} ;
-#endif
-#if CPU_CLIPS_NEGATIVE == 0
if (scaled_value <= (-8.0 * 0x10000000))
{ dest [i] = -128 ;
continue ;
} ;
-#endif
dest [i] = psf_lrintf (scaled_value) >> 24 ;
} ;
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] = 0xFF ;
continue ;
} ;
-#endif
-#if CPU_CLIPS_NEGATIVE == 0
if (scaled_value <= (-8.0 * 0x10000000))
{ dest [i] = 0 ;
continue ;
} ;
-#endif
dest [i] = (psf_lrintf (scaled_value) >> 24) + 128 ;
} ;
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 [1] = 0xFF ;
ucptr [0] = 0x7F ;
continue ;
} ;
-#endif
-#if CPU_CLIPS_NEGATIVE == 0
if (scaled_value <= (-8.0 * 0x10000000))
{ ucptr [1] = 0x00 ;
ucptr [0] = 0x80 ;
continue ;
} ;
-#endif
value = psf_lrintf (scaled_value) ;
ucptr [1] = value >> 16 ;
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] = 0x7F ;
continue ;
} ;
-#endif
-#if CPU_CLIPS_NEGATIVE == 0
if (scaled_value <= (-8.0 * 0x10000000))
{ ucptr [0] = 0x00 ;
ucptr [1] = 0x80 ;
continue ;
} ;
-#endif
value = psf_lrintf (scaled_value) ;
ucptr [0] = value >> 16 ;
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] = 127 ;
continue ;
} ;
-#endif
-#if CPU_CLIPS_NEGATIVE == 0
if (scaled_value <= (-8.0 * 0x10000000))
{ dest [i] = -128 ;
continue ;
} ;
-#endif
dest [i] = psf_lrintf (scaled_value) >> 24 ;
} ;
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] = 255 ;
continue ;
} ;
-#endif
-#if CPU_CLIPS_NEGATIVE == 0
if (scaled_value <= (-8.0 * 0x10000000))
{ dest [i] = 0 ;
continue ;
} ;
-#endif
dest [i] = (psf_lrint (src [i] * normfact) >> 24) + 128 ;
} ;
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 [1] = 0xFF ;
ucptr [0] = 0x7F ;
continue ;
} ;
-#endif
-#if CPU_CLIPS_NEGATIVE == 0
if (scaled_value <= (-8.0 * 0x10000000))
{ ucptr [1] = 0x00 ;
ucptr [0] = 0x80 ;
continue ;
} ;
-#endif
value = psf_lrint (scaled_value) ;
ucptr [1] = value >> 16 ;
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] = 0x7F ;
continue ;
} ;
-#endif
-#if CPU_CLIPS_NEGATIVE == 0
if (scaled_value <= (-8.0 * 0x10000000))
{ ucptr [0] = 0x00 ;
ucptr [1] = 0x80 ;
continue ;
} ;
-#endif
value = psf_lrint (scaled_value) ;
ucptr [0] = value >> 16 ;
} /* 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 ;
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 (0xFF, 0xE0, 0, 0)) == MAKE_MARKER (0xFF, 0xE0, 0, 0) && /* Frame sync */
- (buffer [0] & MAKE_MARKER (0, 0x18, 0, 0)) != MAKE_MARKER (0, 0x08, 0, 0) && /* Valid MPEG version */
- (buffer [0] & MAKE_MARKER (0, 0x06, 0, 0)) != MAKE_MARKER (0, 0, 0, 0) && /* Valid layer description */
- (buffer [0] & MAKE_MARKER (0, 0, 0xF0, 0)) != MAKE_MARKER (0, 0, 0xF0, 0) && /* Valid bitrate */
- (buffer [0] & MAKE_MARKER (0, 0, 0x0C, 0)) != MAKE_MARKER (0, 0, 0x0C, 0)) /* Valid samplerate */
- return SF_FORMAT_MPEG ;
-
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") ;
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 ;
if (buffer [0] == MAKE_MARKER ('a', 'j', 'k', 'g'))
return 0 /*-SF_FORMAT_SHN-*/ ;
- /* This must be the last one. */
+ /* 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) || (sfinfo->samplerate > SF_MAX_SAMPLERATE))
+{ if (sfinfo->samplerate < 1)
return 0 ;
if (sfinfo->frames < 0)
return 0 ;
int filetype = 0, parsestage = 0, done = 0 ;
int bytecount = 0, channels ;
- if (psf->filelength > SF_PLATFORM_S64 (0xffffffff))
+ if (psf->filelength > 0xFFFFFFFFLL)
psf_log_printf (psf, "Warning : filelength > 0xffffffff. This is bad!!!!\n") ;
memset (&vhdr, 0, sizeof (vhdr)) ;
{
SF_PRIVATE sf_private, *psf ;
const char * filename = "conversion.bin" ;
- int64_t i64 = SF_PLATFORM_S64 (0x0123456789abcdef), t64 = 0 ;
+ int64_t i64 = 0x0123456789ABCDEFLL, t64 = 0 ;
char format_str [16] ;
char test_name [64] ;
char i8 = 12, t8 = 0 ;
uint32_t marker, chunk_size = 0, RIFFsize = 0, done = 0 ;
int parsestage = 0, error, format = 0 ;
- if (psf->is_pipe == 0 && psf->filelength > SF_PLATFORM_S64 (0xffffffff))
+ 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)
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 */
switch (chunk)
{ case adtl_MARKER :
case INFO_MARKER :
- /* These markers don't contain anything, not even a chunk lebgth. */
+ /* These markers don't contain anything, not even a chunk length. */
psf_log_printf (psf, " %M\n", chunk) ;
continue ;
** the rest of the chunk is garbage.
*/
psf_log_printf (psf, " *** Found weird-ass zero marker. Jumping to end of chunk.\n") ;
- if (bytesread < chunk_length)
- bytesread += psf_binheader_readf (psf, "j", chunk_length - bytesread) ;
- psf_log_printf (psf, " *** Offset is now : 0x%X\n", psf_fseek (psf, 0, SEEK_CUR)) ;
- return 0 ;
+ goto cleanup_subchunk_parse ;
default :
break ;
case ITRK_MARKER :
bytesread += psf_binheader_readf (psf, "4", &chunk_size) ;
chunk_size += (chunk_size & 1) ;
- if (chunk_size >= SIGNED_SIZEOF (buffer) || chunk_size >= chunk_length)
+ 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, "44", &chunk_size, &mark_id) ;
chunk_size -= 4 ;
chunk_size += (chunk_size & 1) ;
- if (chunk_size < 1 || chunk_size >= SIGNED_SIZEOF (buffer) || chunk_size >= chunk_length)
+ 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 ;
} ;
case note_MARKER :
bytesread += psf_binheader_readf (psf, "4", &chunk_size) ;
chunk_size += (chunk_size & 1) ;
- if (chunk_size >= SIGNED_SIZEOF (buffer) || chunk_size >= chunk_length)
+ 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 ;
} ;
default :
bytesread += psf_binheader_readf (psf, "4", &chunk_size) ;
chunk_size += (chunk_size & 1) ;
- psf_log_printf (psf, " *** %M : %u\n", chunk, chunk_size) ;
if (bytesread + chunk_size > chunk_length)
- { bytesread += psf_binheader_readf (psf, "j", chunk_length - bytesread + 4) ;
- continue ;
+ { 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) ;
-
- if (chunk_size >= chunk_length)
- return 0 ;
+ } ;
break ;
} ;
title = file.getString (SF_STR_TITLE) ;
if (title == NULL)
- { printf ("\n\n%s %d : Error : No title.\n\n", __func__, __LINE__) ;
+ { printf ("\n\n%s %d : Error : No title for %s.\n\n", __func__, __LINE__, filename) ;
exit (1) ;
} ;
} /* bad_wav_test */
static void
+wav_list_recover_test (const char * filename)
+{ SNDFILE *sndfile ;
+ SF_INFO sfinfo ;
+
+ FILE *file ;
+ const char data [] =
+ "RIFF" "J\000\000\000"
+ "WAVE" "fmt " "\020\000\000\000" "\001\000\001\000D\254\000\000\210X\001\000\002\000\020\000"
+ "LIST" "\014\000\000\000" "test" "\010\000\000\000" "1234" /* test is 4 bytes short inside LIST container */
+ "data" "\004\000\000\000" "abcd" ;
+
+ print_test_name (__func__, filename) ;
+
+ if ((file = fopen (filename, "w")) == NULL)
+ { printf ("\n\nLine %d : fopen returned NULL.\n", __LINE__) ;
+ exit (1) ;
+ } ;
+
+ exit_if_true (fwrite (data, sizeof (data) - 1, 1, file) != 1, "\n\nLine %d : fwrite failed.\n", __LINE__) ;
+ fclose (file) ;
+
+ memset (&sfinfo, 0, sizeof (sfinfo)) ;
+ sndfile = sf_open (filename, SFM_READ, &sfinfo) ;
+
+ if (!sndfile)
+ { printf ("\n\nLine %d : expected recovery from bogus LIST content.\n", __LINE__) ;
+ exit (1) ;
+ } ;
+
+ if (sfinfo.frames != 2)
+ { printf ("\n\nLine %d : Should have read data chunk with 2 stereo frames, got %ld.\n\n", __LINE__, (long)sfinfo.frames) ;
+ exit (1) ;
+ } ;
+
+ unlink (filename) ;
+ puts ("ok") ;
+} /* wav_list_recover_test */
+
+static void
error_close_test (void)
{ static short buffer [SHORT_BUFFER] ;
const char *filename = "error_close.wav" ;
no_file_test ("no_file.wav") ;
zero_length_test ("zero_length.wav") ;
bad_wav_test ("bad_wav.wav") ;
+ wav_list_recover_test ("list_recover.wav") ;
unrecognised_test () ;
CC=`echo "@CC@" | sed "s/.*shave cc //"`
# Compile with -Werror and -pedantic.
- $CC -std=c99 -Werror -pedantic -I@top_srcdir@/src -I@abs_top_builddir@/src -I@abs_top_builddir@/include -c @top_srcdir@/tests/sfversion.c -o /dev/null
+ $CC -std=c99 -Werror -pedantic -I@top_srcdir@/src -I@abs_top_builddir@/src -I@top_srcdir@/include -c @top_srcdir@/tests/sfversion.c -o /dev/null
# Check compiler return status.
if test $? -ne 0 ; then
#include <string.h>
#include <ctype.h>
#include <math.h>
+#include <float.h>
#include <fcntl.h>
#include <sys/stat.h>
#define O_BINARY 0
#endif
+
+/*
+** Compare for equality, with epsilon
+*/
+static inline int
+equals_short (const short a, const short b)
+{ return (a == b);
+} /* equals_short */
+static inline int
+equals_int (const int a, const int b)
+{ return (a == b);
+} /* equals_int */
+static inline int
+equals_float (const float a, const float b)
+{ return (fabsf(a - b) <= FLT_EPSILON);
+} /* equals_float */
+static inline int
+equals_double (const double a, const double b)
+{ return (fabs(a - b) <= DBL_EPSILON);
+} /* equals_double */
+
+
[+ FOR float_type +]
void
gen_windowed_sine_[+ (get "name") +] ([+ (get "name") +] *data, int len, double maximum)
unsigned k ;
for (k = 0 ; k < count ; k++)
- if (expected [k] != actual [k])
- { printf ("\n\nLine %d : Error at index %d, got " [+ (get "format_str") +] ", should be " [+ (get "format_str") +] ".\n\n", line_num, k, actual [k], expected [k]) ;
+ if (!equals_[+ (get "io_element") +](expected [k], actual [k]))
+ { printf ("\n\nLine %d : Error at index %d, got " [+ (get "format_str") +] ", should be " [+ (get "format_str") +] "(delta=" [+ (get "format_str") +] " ).\n\n", line_num, k, actual [k], expected [k], actual [k] - expected [k]) ;
exit (1) ;
} ;