summaryrefslogtreecommitdiff
path: root/lib/speexdsp/libspeexdsp
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2023-08-10 19:12:38 +1000
committerjacqueline <me@jacqueline.id.au>2023-08-10 19:12:38 +1000
commit958160aa545e3d91b2a4f1a367817e73d298e8a9 (patch)
tree190e6591a6dda1f0d9651c7e127666ead2a3373b /lib/speexdsp/libspeexdsp
parentd8fc77101dcf80a3643a00b3446dca1e390ce997 (diff)
downloadtangara-fw-958160aa545e3d91b2a4f1a367817e73d298e8a9.tar.gz
Use the libspeexdsp resampler
AFAICT it runs a little slower? but it's fixed point, and has much better understood audio characteristics.
Diffstat (limited to 'lib/speexdsp/libspeexdsp')
-rw-r--r--lib/speexdsp/libspeexdsp/Makefile.am46
-rw-r--r--lib/speexdsp/libspeexdsp/Makefile.in824
-rw-r--r--lib/speexdsp/libspeexdsp/_kiss_fft_guts.h160
-rw-r--r--lib/speexdsp/libspeexdsp/arch.h232
-rw-r--r--lib/speexdsp/libspeexdsp/bfin.h15
-rw-r--r--lib/speexdsp/libspeexdsp/buffer.c176
-rw-r--r--lib/speexdsp/libspeexdsp/config.h13
-rw-r--r--lib/speexdsp/libspeexdsp/echo_diagnostic.m72
-rw-r--r--lib/speexdsp/libspeexdsp/fftwrap.c448
-rw-r--r--lib/speexdsp/libspeexdsp/fftwrap.h58
-rw-r--r--lib/speexdsp/libspeexdsp/filterbank.c227
-rw-r--r--lib/speexdsp/libspeexdsp/filterbank.h66
-rw-r--r--lib/speexdsp/libspeexdsp/fixed_arm4.h135
-rw-r--r--lib/speexdsp/libspeexdsp/fixed_arm5e.h160
-rw-r--r--lib/speexdsp/libspeexdsp/fixed_bfin.h141
-rw-r--r--lib/speexdsp/libspeexdsp/fixed_debug.h497
-rw-r--r--lib/speexdsp/libspeexdsp/fixed_generic.h106
-rw-r--r--lib/speexdsp/libspeexdsp/jitter.c839
-rw-r--r--lib/speexdsp/libspeexdsp/kiss_fft.c523
-rw-r--r--lib/speexdsp/libspeexdsp/kiss_fft.h108
-rw-r--r--lib/speexdsp/libspeexdsp/kiss_fftr.c297
-rw-r--r--lib/speexdsp/libspeexdsp/kiss_fftr.h51
-rw-r--r--lib/speexdsp/libspeexdsp/math_approx.h332
-rw-r--r--lib/speexdsp/libspeexdsp/mdf.c1279
-rw-r--r--lib/speexdsp/libspeexdsp/misc_bfin.h56
-rw-r--r--lib/speexdsp/libspeexdsp/os_support.h169
-rw-r--r--lib/speexdsp/libspeexdsp/preprocess.c1215
-rw-r--r--lib/speexdsp/libspeexdsp/pseudofloat.h379
-rw-r--r--lib/speexdsp/libspeexdsp/resample.c1239
-rw-r--r--lib/speexdsp/libspeexdsp/resample_neon.h339
-rw-r--r--lib/speexdsp/libspeexdsp/resample_sse.h128
-rw-r--r--lib/speexdsp/libspeexdsp/scal.c293
-rw-r--r--lib/speexdsp/libspeexdsp/smallft.c1261
-rw-r--r--lib/speexdsp/libspeexdsp/smallft.h46
-rw-r--r--lib/speexdsp/libspeexdsp/testdenoise.c44
-rw-r--r--lib/speexdsp/libspeexdsp/testecho.c53
-rw-r--r--lib/speexdsp/libspeexdsp/testjitter.c75
-rw-r--r--lib/speexdsp/libspeexdsp/testresample.c86
-rw-r--r--lib/speexdsp/libspeexdsp/testresample2.c93
-rw-r--r--lib/speexdsp/libspeexdsp/vorbis_psy.h97
40 files changed, 12378 insertions, 0 deletions
diff --git a/lib/speexdsp/libspeexdsp/Makefile.am b/lib/speexdsp/libspeexdsp/Makefile.am
new file mode 100644
index 00000000..b2ab1875
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/Makefile.am
@@ -0,0 +1,46 @@
+# Disable automatic dependency tracking if using other tools than gcc and gmake
+#AUTOMAKE_OPTIONS = no-dependencies
+
+EXTRA_DIST=echo_diagnostic.m
+
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include/speex -I$(top_builddir) @FFT_CFLAGS@
+
+lib_LTLIBRARIES = libspeexdsp.la
+
+# Sources for compilation in the library
+if BUILD_KISS_FFT
+ FFTSRC=kiss_fft.c _kiss_fft_guts.h kiss_fft.h kiss_fftr.c kiss_fftr.h
+else
+if BUILD_SMALLFT
+ FFTSRC=smallft.c
+else
+ FFTSRC=
+endif
+endif
+
+libspeexdsp_la_SOURCES = preprocess.c jitter.c mdf.c fftwrap.c filterbank.c resample.c buffer.c scal.c $(FFTSRC)
+
+noinst_HEADERS = arch.h bfin.h \
+ fixed_arm4.h \
+ fixed_arm5e.h fixed_bfin.h fixed_debug.h \
+ math_approx.h misc_bfin.h \
+ fftwrap.h \
+ filterbank.h fixed_generic.h os_support.h \
+ pseudofloat.h smallft.h vorbis_psy.h resample_sse.h resample_neon.h
+
+libspeexdsp_la_LDFLAGS = -no-undefined -version-info @SPEEXDSP_LT_CURRENT@:@SPEEXDSP_LT_REVISION@:@SPEEXDSP_LT_AGE@
+libspeexdsp_la_LIBADD = $(LIBM)
+
+if BUILD_EXAMPLES
+noinst_PROGRAMS = testdenoise testecho testjitter testresample testresample2
+testdenoise_SOURCES = testdenoise.c
+testdenoise_LDADD = libspeexdsp.la @FFT_LIBS@
+testecho_SOURCES = testecho.c
+testecho_LDADD = libspeexdsp.la @FFT_LIBS@
+testjitter_SOURCES = testjitter.c
+testjitter_LDADD = libspeexdsp.la @FFT_LIBS@
+testresample_SOURCES = testresample.c
+testresample_LDADD = libspeexdsp.la @FFT_LIBS@ @LIBM@
+testresample2_SOURCES = testresample2.c
+testresample2_LDADD = libspeexdsp.la @FFT_LIBS@ @LIBM@
+endif
diff --git a/lib/speexdsp/libspeexdsp/Makefile.in b/lib/speexdsp/libspeexdsp/Makefile.in
new file mode 100644
index 00000000..4339ff97
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/Makefile.in
@@ -0,0 +1,824 @@
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2021 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Disable automatic dependency tracking if using other tools than gcc and gmake
+#AUTOMAKE_OPTIONS = no-dependencies
+
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+@BUILD_EXAMPLES_TRUE@noinst_PROGRAMS = testdenoise$(EXEEXT) \
+@BUILD_EXAMPLES_TRUE@ testecho$(EXEEXT) testjitter$(EXEEXT) \
+@BUILD_EXAMPLES_TRUE@ testresample$(EXEEXT) \
+@BUILD_EXAMPLES_TRUE@ testresample2$(EXEEXT)
+subdir = libspeexdsp
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+PROGRAMS = $(noinst_PROGRAMS)
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(libdir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libspeexdsp_la_DEPENDENCIES = $(am__DEPENDENCIES_1)
+am__libspeexdsp_la_SOURCES_DIST = preprocess.c jitter.c mdf.c \
+ fftwrap.c filterbank.c resample.c buffer.c scal.c smallft.c \
+ kiss_fft.c _kiss_fft_guts.h kiss_fft.h kiss_fftr.c kiss_fftr.h
+@BUILD_KISS_FFT_FALSE@@BUILD_SMALLFT_TRUE@am__objects_1 = smallft.lo
+@BUILD_KISS_FFT_TRUE@am__objects_1 = kiss_fft.lo kiss_fftr.lo
+am_libspeexdsp_la_OBJECTS = preprocess.lo jitter.lo mdf.lo fftwrap.lo \
+ filterbank.lo resample.lo buffer.lo scal.lo $(am__objects_1)
+libspeexdsp_la_OBJECTS = $(am_libspeexdsp_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libspeexdsp_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libspeexdsp_la_LDFLAGS) $(LDFLAGS) -o \
+ $@
+am__testdenoise_SOURCES_DIST = testdenoise.c
+@BUILD_EXAMPLES_TRUE@am_testdenoise_OBJECTS = testdenoise.$(OBJEXT)
+testdenoise_OBJECTS = $(am_testdenoise_OBJECTS)
+@BUILD_EXAMPLES_TRUE@testdenoise_DEPENDENCIES = libspeexdsp.la
+am__testecho_SOURCES_DIST = testecho.c
+@BUILD_EXAMPLES_TRUE@am_testecho_OBJECTS = testecho.$(OBJEXT)
+testecho_OBJECTS = $(am_testecho_OBJECTS)
+@BUILD_EXAMPLES_TRUE@testecho_DEPENDENCIES = libspeexdsp.la
+am__testjitter_SOURCES_DIST = testjitter.c
+@BUILD_EXAMPLES_TRUE@am_testjitter_OBJECTS = testjitter.$(OBJEXT)
+testjitter_OBJECTS = $(am_testjitter_OBJECTS)
+@BUILD_EXAMPLES_TRUE@testjitter_DEPENDENCIES = libspeexdsp.la
+am__testresample_SOURCES_DIST = testresample.c
+@BUILD_EXAMPLES_TRUE@am_testresample_OBJECTS = testresample.$(OBJEXT)
+testresample_OBJECTS = $(am_testresample_OBJECTS)
+@BUILD_EXAMPLES_TRUE@testresample_DEPENDENCIES = libspeexdsp.la
+am__testresample2_SOURCES_DIST = testresample2.c
+@BUILD_EXAMPLES_TRUE@am_testresample2_OBJECTS = \
+@BUILD_EXAMPLES_TRUE@ testresample2.$(OBJEXT)
+testresample2_OBJECTS = $(am_testresample2_OBJECTS)
+@BUILD_EXAMPLES_TRUE@testresample2_DEPENDENCIES = libspeexdsp.la
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/buffer.Plo ./$(DEPDIR)/fftwrap.Plo \
+ ./$(DEPDIR)/filterbank.Plo ./$(DEPDIR)/jitter.Plo \
+ ./$(DEPDIR)/kiss_fft.Plo ./$(DEPDIR)/kiss_fftr.Plo \
+ ./$(DEPDIR)/mdf.Plo ./$(DEPDIR)/preprocess.Plo \
+ ./$(DEPDIR)/resample.Plo ./$(DEPDIR)/scal.Plo \
+ ./$(DEPDIR)/smallft.Plo ./$(DEPDIR)/testdenoise.Po \
+ ./$(DEPDIR)/testecho.Po ./$(DEPDIR)/testjitter.Po \
+ ./$(DEPDIR)/testresample.Po ./$(DEPDIR)/testresample2.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libspeexdsp_la_SOURCES) $(testdenoise_SOURCES) \
+ $(testecho_SOURCES) $(testjitter_SOURCES) \
+ $(testresample_SOURCES) $(testresample2_SOURCES)
+DIST_SOURCES = $(am__libspeexdsp_la_SOURCES_DIST) \
+ $(am__testdenoise_SOURCES_DIST) $(am__testecho_SOURCES_DIST) \
+ $(am__testjitter_SOURCES_DIST) \
+ $(am__testresample_SOURCES_DIST) \
+ $(am__testresample2_SOURCES_DIST)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+HEADERS = $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPPFLAGS = @CPPFLAGS@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ETAGS = @ETAGS@
+EXEEXT = @EXEEXT@
+FFT_CFLAGS = @FFT_CFLAGS@
+FFT_LIBS = @FFT_LIBS@
+FFT_PKGCONFIG = @FFT_PKGCONFIG@
+FGREP = @FGREP@
+GREP = @GREP@
+INCLUDE_STDINT = @INCLUDE_STDINT@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBM = @LIBM@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SIZE16 = @SIZE16@
+SIZE32 = @SIZE32@
+SPEEXDSP_LT_AGE = @SPEEXDSP_LT_AGE@
+SPEEXDSP_LT_CURRENT = @SPEEXDSP_LT_CURRENT@
+SPEEXDSP_LT_REVISION = @SPEEXDSP_LT_REVISION@
+STRIP = @STRIP@
+USIZE16 = @USIZE16@
+USIZE32 = @USIZE32@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+src = @src@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+EXTRA_DIST = echo_diagnostic.m
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include/speex -I$(top_builddir) @FFT_CFLAGS@
+lib_LTLIBRARIES = libspeexdsp.la
+@BUILD_KISS_FFT_FALSE@@BUILD_SMALLFT_FALSE@FFTSRC =
+@BUILD_KISS_FFT_FALSE@@BUILD_SMALLFT_TRUE@FFTSRC = smallft.c
+
+# Sources for compilation in the library
+@BUILD_KISS_FFT_TRUE@FFTSRC = kiss_fft.c _kiss_fft_guts.h kiss_fft.h kiss_fftr.c kiss_fftr.h
+libspeexdsp_la_SOURCES = preprocess.c jitter.c mdf.c fftwrap.c filterbank.c resample.c buffer.c scal.c $(FFTSRC)
+noinst_HEADERS = arch.h bfin.h \
+ fixed_arm4.h \
+ fixed_arm5e.h fixed_bfin.h fixed_debug.h \
+ math_approx.h misc_bfin.h \
+ fftwrap.h \
+ filterbank.h fixed_generic.h os_support.h \
+ pseudofloat.h smallft.h vorbis_psy.h resample_sse.h resample_neon.h
+
+libspeexdsp_la_LDFLAGS = -no-undefined -version-info @SPEEXDSP_LT_CURRENT@:@SPEEXDSP_LT_REVISION@:@SPEEXDSP_LT_AGE@
+libspeexdsp_la_LIBADD = $(LIBM)
+@BUILD_EXAMPLES_TRUE@testdenoise_SOURCES = testdenoise.c
+@BUILD_EXAMPLES_TRUE@testdenoise_LDADD = libspeexdsp.la @FFT_LIBS@
+@BUILD_EXAMPLES_TRUE@testecho_SOURCES = testecho.c
+@BUILD_EXAMPLES_TRUE@testecho_LDADD = libspeexdsp.la @FFT_LIBS@
+@BUILD_EXAMPLES_TRUE@testjitter_SOURCES = testjitter.c
+@BUILD_EXAMPLES_TRUE@testjitter_LDADD = libspeexdsp.la @FFT_LIBS@
+@BUILD_EXAMPLES_TRUE@testresample_SOURCES = testresample.c
+@BUILD_EXAMPLES_TRUE@testresample_LDADD = libspeexdsp.la @FFT_LIBS@ @LIBM@
+@BUILD_EXAMPLES_TRUE@testresample2_SOURCES = testresample2.c
+@BUILD_EXAMPLES_TRUE@testresample2_LDADD = libspeexdsp.la @FFT_LIBS@ @LIBM@
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign libspeexdsp/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign libspeexdsp/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstPROGRAMS:
+ @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libspeexdsp.la: $(libspeexdsp_la_OBJECTS) $(libspeexdsp_la_DEPENDENCIES) $(EXTRA_libspeexdsp_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libspeexdsp_la_LINK) -rpath $(libdir) $(libspeexdsp_la_OBJECTS) $(libspeexdsp_la_LIBADD) $(LIBS)
+
+testdenoise$(EXEEXT): $(testdenoise_OBJECTS) $(testdenoise_DEPENDENCIES) $(EXTRA_testdenoise_DEPENDENCIES)
+ @rm -f testdenoise$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(testdenoise_OBJECTS) $(testdenoise_LDADD) $(LIBS)
+
+testecho$(EXEEXT): $(testecho_OBJECTS) $(testecho_DEPENDENCIES) $(EXTRA_testecho_DEPENDENCIES)
+ @rm -f testecho$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(testecho_OBJECTS) $(testecho_LDADD) $(LIBS)
+
+testjitter$(EXEEXT): $(testjitter_OBJECTS) $(testjitter_DEPENDENCIES) $(EXTRA_testjitter_DEPENDENCIES)
+ @rm -f testjitter$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(testjitter_OBJECTS) $(testjitter_LDADD) $(LIBS)
+
+testresample$(EXEEXT): $(testresample_OBJECTS) $(testresample_DEPENDENCIES) $(EXTRA_testresample_DEPENDENCIES)
+ @rm -f testresample$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(testresample_OBJECTS) $(testresample_LDADD) $(LIBS)
+
+testresample2$(EXEEXT): $(testresample2_OBJECTS) $(testresample2_DEPENDENCIES) $(EXTRA_testresample2_DEPENDENCIES)
+ @rm -f testresample2$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(testresample2_OBJECTS) $(testresample2_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/buffer.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fftwrap.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filterbank.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/jitter.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kiss_fft.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kiss_fftr.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mdf.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/preprocess.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/resample.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scal.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/smallft.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testdenoise.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testecho.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testjitter.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testresample.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testresample2.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS) $(LTLIBRARIES) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ clean-noinstPROGRAMS mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/buffer.Plo
+ -rm -f ./$(DEPDIR)/fftwrap.Plo
+ -rm -f ./$(DEPDIR)/filterbank.Plo
+ -rm -f ./$(DEPDIR)/jitter.Plo
+ -rm -f ./$(DEPDIR)/kiss_fft.Plo
+ -rm -f ./$(DEPDIR)/kiss_fftr.Plo
+ -rm -f ./$(DEPDIR)/mdf.Plo
+ -rm -f ./$(DEPDIR)/preprocess.Plo
+ -rm -f ./$(DEPDIR)/resample.Plo
+ -rm -f ./$(DEPDIR)/scal.Plo
+ -rm -f ./$(DEPDIR)/smallft.Plo
+ -rm -f ./$(DEPDIR)/testdenoise.Po
+ -rm -f ./$(DEPDIR)/testecho.Po
+ -rm -f ./$(DEPDIR)/testjitter.Po
+ -rm -f ./$(DEPDIR)/testresample.Po
+ -rm -f ./$(DEPDIR)/testresample2.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/buffer.Plo
+ -rm -f ./$(DEPDIR)/fftwrap.Plo
+ -rm -f ./$(DEPDIR)/filterbank.Plo
+ -rm -f ./$(DEPDIR)/jitter.Plo
+ -rm -f ./$(DEPDIR)/kiss_fft.Plo
+ -rm -f ./$(DEPDIR)/kiss_fftr.Plo
+ -rm -f ./$(DEPDIR)/mdf.Plo
+ -rm -f ./$(DEPDIR)/preprocess.Plo
+ -rm -f ./$(DEPDIR)/resample.Plo
+ -rm -f ./$(DEPDIR)/scal.Plo
+ -rm -f ./$(DEPDIR)/smallft.Plo
+ -rm -f ./$(DEPDIR)/testdenoise.Po
+ -rm -f ./$(DEPDIR)/testecho.Po
+ -rm -f ./$(DEPDIR)/testjitter.Po
+ -rm -f ./$(DEPDIR)/testresample.Po
+ -rm -f ./$(DEPDIR)/testresample2.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libLTLIBRARIES
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libLTLIBRARIES clean-libtool \
+ clean-noinstPROGRAMS cscopelist-am ctags ctags-am distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am \
+ install-libLTLIBRARIES install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am uninstall-libLTLIBRARIES
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/lib/speexdsp/libspeexdsp/_kiss_fft_guts.h b/lib/speexdsp/libspeexdsp/_kiss_fft_guts.h
new file mode 100644
index 00000000..f5f2a5fd
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/_kiss_fft_guts.h
@@ -0,0 +1,160 @@
+/*
+Copyright (c) 2003-2004, Mark Borgerding
+
+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 MIN(a,b) ((a)<(b) ? (a):(b))
+#define MAX(a,b) ((a)>(b) ? (a):(b))
+
+/* kiss_fft.h
+ defines kiss_fft_scalar as either short or a float type
+ and defines
+ typedef struct { kiss_fft_scalar r; kiss_fft_scalar i; }kiss_fft_cpx; */
+#include "kiss_fft.h"
+#include "math_approx.h"
+
+#define MAXFACTORS 32
+/* e.g. an fft of length 128 has 4 factors
+ as far as kissfft is concerned
+ 4*4*4*2
+ */
+
+struct kiss_fft_state{
+ int nfft;
+ int inverse;
+ int factors[2*MAXFACTORS];
+ kiss_fft_cpx twiddles[1];
+};
+
+/*
+ Explanation of macros dealing with complex math:
+
+ C_MUL(m,a,b) : m = a*b
+ C_FIXDIV( c , div ) : if a fixed point impl., c /= div. noop otherwise
+ C_SUB( res, a,b) : res = a - b
+ C_SUBFROM( res , a) : res -= a
+ C_ADDTO( res , a) : res += a
+ * */
+#ifdef FIXED_POINT
+#include "arch.h"
+# define FRACBITS 15
+# define SAMPPROD spx_int32_t
+#define SAMP_MAX 32767
+
+#define SAMP_MIN -SAMP_MAX
+
+#if defined(CHECK_OVERFLOW)
+# define CHECK_OVERFLOW_OP(a,op,b) \
+ if ( (SAMPPROD)(a) op (SAMPPROD)(b) > SAMP_MAX || (SAMPPROD)(a) op (SAMPPROD)(b) < SAMP_MIN ) { \
+ fprintf(stderr,"WARNING:overflow @ " __FILE__ "(%d): (%d " #op" %d) = %ld\n",__LINE__,(a),(b),(SAMPPROD)(a) op (SAMPPROD)(b) ); }
+#endif
+
+
+# define smul(a,b) ( (SAMPPROD)(a)*(b) )
+# define sround( x ) (kiss_fft_scalar)( ( (x) + (1<<(FRACBITS-1)) ) >> FRACBITS )
+
+# define S_MUL(a,b) sround( smul(a,b) )
+
+# define C_MUL(m,a,b) \
+ do{ (m).r = sround( smul((a).r,(b).r) - smul((a).i,(b).i) ); \
+ (m).i = sround( smul((a).r,(b).i) + smul((a).i,(b).r) ); }while(0)
+
+# define C_MUL4(m,a,b) \
+ do{ (m).r = PSHR32( smul((a).r,(b).r) - smul((a).i,(b).i),17 ); \
+ (m).i = PSHR32( smul((a).r,(b).i) + smul((a).i,(b).r),17 ); }while(0)
+
+# define DIVSCALAR(x,k) \
+ (x) = sround( smul( x, SAMP_MAX/k ) )
+
+# define C_FIXDIV(c,div) \
+ do { DIVSCALAR( (c).r , div); \
+ DIVSCALAR( (c).i , div); }while (0)
+
+# define C_MULBYSCALAR( c, s ) \
+ do{ (c).r = sround( smul( (c).r , s ) ) ;\
+ (c).i = sround( smul( (c).i , s ) ) ; }while(0)
+
+#else /* not FIXED_POINT*/
+
+# define S_MUL(a,b) ( (a)*(b) )
+#define C_MUL(m,a,b) \
+ do{ (m).r = (a).r*(b).r - (a).i*(b).i;\
+ (m).i = (a).r*(b).i + (a).i*(b).r; }while(0)
+
+#define C_MUL4(m,a,b) C_MUL(m,a,b)
+
+# define C_FIXDIV(c,div) /* NOOP */
+# define C_MULBYSCALAR( c, s ) \
+ do{ (c).r *= (s);\
+ (c).i *= (s); }while(0)
+#endif
+
+#ifndef CHECK_OVERFLOW_OP
+# define CHECK_OVERFLOW_OP(a,op,b) /* noop */
+#endif
+
+#define C_ADD( res, a,b)\
+ do { \
+ CHECK_OVERFLOW_OP((a).r,+,(b).r)\
+ CHECK_OVERFLOW_OP((a).i,+,(b).i)\
+ (res).r=(a).r+(b).r; (res).i=(a).i+(b).i; \
+ }while(0)
+#define C_SUB( res, a,b)\
+ do { \
+ CHECK_OVERFLOW_OP((a).r,-,(b).r)\
+ CHECK_OVERFLOW_OP((a).i,-,(b).i)\
+ (res).r=(a).r-(b).r; (res).i=(a).i-(b).i; \
+ }while(0)
+#define C_ADDTO( res , a)\
+ do { \
+ CHECK_OVERFLOW_OP((res).r,+,(a).r)\
+ CHECK_OVERFLOW_OP((res).i,+,(a).i)\
+ (res).r += (a).r; (res).i += (a).i;\
+ }while(0)
+
+#define C_SUBFROM( res , a)\
+ do {\
+ CHECK_OVERFLOW_OP((res).r,-,(a).r)\
+ CHECK_OVERFLOW_OP((res).i,-,(a).i)\
+ (res).r -= (a).r; (res).i -= (a).i; \
+ }while(0)
+
+
+#ifdef FIXED_POINT
+# define KISS_FFT_COS(phase) floor(MIN(32767,MAX(-32767,.5+32768 * cos (phase))))
+# define KISS_FFT_SIN(phase) floor(MIN(32767,MAX(-32767,.5+32768 * sin (phase))))
+# define HALF_OF(x) ((x)>>1)
+#elif defined(USE_SIMD)
+# define KISS_FFT_COS(phase) _mm_set1_ps( cos(phase) )
+# define KISS_FFT_SIN(phase) _mm_set1_ps( sin(phase) )
+# define HALF_OF(x) ((x)*_mm_set1_ps(.5))
+#else
+# define KISS_FFT_COS(phase) (kiss_fft_scalar) cos(phase)
+# define KISS_FFT_SIN(phase) (kiss_fft_scalar) sin(phase)
+# define HALF_OF(x) ((x)*.5)
+#endif
+
+#define kf_cexp(x,phase) \
+ do{ \
+ (x)->r = KISS_FFT_COS(phase);\
+ (x)->i = KISS_FFT_SIN(phase);\
+ }while(0)
+#define kf_cexp2(x,phase) \
+ do{ \
+ (x)->r = spx_cos_norm((phase));\
+ (x)->i = spx_cos_norm((phase)-32768);\
+}while(0)
+
+
+/* a debugging function */
+#define pcpx(c)\
+ fprintf(stderr,"%g + %gi\n",(double)((c)->r),(double)((c)->i) )
diff --git a/lib/speexdsp/libspeexdsp/arch.h b/lib/speexdsp/libspeexdsp/arch.h
new file mode 100644
index 00000000..1cac3d9c
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/arch.h
@@ -0,0 +1,232 @@
+/* Copyright (C) 2003 Jean-Marc Valin */
+/**
+ @file arch.h
+ @brief Various architecture definitions Speex
+*/
+/*
+ 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.
+*/
+
+#ifndef ARCH_H
+#define ARCH_H
+
+/* A couple test to catch stupid option combinations */
+#ifdef FIXED_POINT
+
+#ifdef FLOATING_POINT
+#error You cannot compile as floating point and fixed point at the same time
+#endif
+#ifdef USE_SSE
+#error SSE is only for floating-point
+#endif
+#if defined(ARM4_ASM) + defined(ARM5E_ASM) + defined(BFIN_ASM) > 1
+#error Make up your mind. What CPU do you have?
+#endif
+#ifdef VORBIS_PSYCHO
+#error Vorbis-psy model currently not implemented in fixed-point
+#endif
+
+#else
+
+#ifndef FLOATING_POINT
+#error You now need to define either FIXED_POINT or FLOATING_POINT
+#endif
+#if defined(ARM4_ASM) || defined(ARM5E_ASM) || defined(BFIN_ASM)
+#error I suppose you can have a [ARM4/ARM5E/Blackfin] that has float instructions?
+#endif
+#ifdef FIXED_DEBUG
+#error "Don't you think enabling fixed-point is a good thing to do if you want to debug that?"
+#endif
+
+
+#endif
+
+#ifndef OUTSIDE_SPEEX
+#include "speex/speexdsp_types.h"
+#endif
+
+#define ABS(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute integer value. */
+#define ABS16(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute 16-bit value. */
+#define MIN16(a,b) ((a) < (b) ? (a) : (b)) /**< Maximum 16-bit value. */
+#define MAX16(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum 16-bit value. */
+#define ABS32(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute 32-bit value. */
+#define MIN32(a,b) ((a) < (b) ? (a) : (b)) /**< Maximum 32-bit value. */
+#define MAX32(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum 32-bit value. */
+
+#ifdef FIXED_POINT
+
+typedef spx_int16_t spx_word16_t;
+typedef spx_int32_t spx_word32_t;
+typedef spx_word32_t spx_mem_t;
+typedef spx_word16_t spx_coef_t;
+typedef spx_word16_t spx_lsp_t;
+typedef spx_word32_t spx_sig_t;
+
+#define Q15ONE 32767
+
+#define LPC_SCALING 8192
+#define SIG_SCALING 16384
+#define LSP_SCALING 8192.
+#define GAMMA_SCALING 32768.
+#define GAIN_SCALING 64
+#define GAIN_SCALING_1 0.015625
+
+#define LPC_SHIFT 13
+#define LSP_SHIFT 13
+#define SIG_SHIFT 14
+#define GAIN_SHIFT 6
+
+#define WORD2INT(x) ((x) < -32767 ? -32768 : ((x) > 32766 ? 32767 : (x)))
+
+#define VERY_SMALL 0
+#define VERY_LARGE32 ((spx_word32_t)2147483647)
+#define VERY_LARGE16 ((spx_word16_t)32767)
+#define Q15_ONE ((spx_word16_t)32767)
+
+
+#ifdef FIXED_DEBUG
+#include "fixed_debug.h"
+#else
+
+#include "fixed_generic.h"
+
+#ifdef ARM5E_ASM
+#include "fixed_arm5e.h"
+#elif defined(ARM4_ASM)
+#include "fixed_arm4.h"
+#elif defined(BFIN_ASM)
+#include "fixed_bfin.h"
+#endif
+
+#endif
+
+
+#else
+
+typedef float spx_mem_t;
+typedef float spx_coef_t;
+typedef float spx_lsp_t;
+typedef float spx_sig_t;
+typedef float spx_word16_t;
+typedef float spx_word32_t;
+
+#define Q15ONE 1.0f
+#define LPC_SCALING 1.f
+#define SIG_SCALING 1.f
+#define LSP_SCALING 1.f
+#define GAMMA_SCALING 1.f
+#define GAIN_SCALING 1.f
+#define GAIN_SCALING_1 1.f
+
+
+#define VERY_SMALL 1e-15f
+#define VERY_LARGE32 1e15f
+#define VERY_LARGE16 1e15f
+#define Q15_ONE ((spx_word16_t)1.f)
+
+#define QCONST16(x,bits) (x)
+#define QCONST32(x,bits) (x)
+
+#define NEG16(x) (-(x))
+#define NEG32(x) (-(x))
+#define EXTRACT16(x) (x)
+#define EXTEND32(x) (x)
+#define SHR16(a,shift) (a)
+#define SHL16(a,shift) (a)
+#define SHR32(a,shift) (a)
+#define SHL32(a,shift) (a)
+#define PSHR16(a,shift) (a)
+#define PSHR32(a,shift) (a)
+#define VSHR32(a,shift) (a)
+#define SATURATE16(x,a) (x)
+#define SATURATE32(x,a) (x)
+#define SATURATE32PSHR(x,shift,a) (x)
+
+#define PSHR(a,shift) (a)
+#define SHR(a,shift) (a)
+#define SHL(a,shift) (a)
+#define SATURATE(x,a) (x)
+
+#define ADD16(a,b) ((a)+(b))
+#define SUB16(a,b) ((a)-(b))
+#define ADD32(a,b) ((a)+(b))
+#define SUB32(a,b) ((a)-(b))
+#define MULT16_16_16(a,b) ((a)*(b))
+#define MULT16_32_32(a,b) ((a)*(b))
+#define MULT16_16(a,b) ((spx_word32_t)(a)*(spx_word32_t)(b))
+#define MAC16_16(c,a,b) ((c)+(spx_word32_t)(a)*(spx_word32_t)(b))
+
+#define MULT16_32_Q15(a,b) ((a)*(b))
+#define MULT16_32_P15(a,b) ((a)*(b))
+
+#define MAC16_32_Q15(c,a,b) ((c)+(a)*(b))
+
+#define MAC16_16_Q11(c,a,b) ((c)+(a)*(b))
+#define MAC16_16_Q13(c,a,b) ((c)+(a)*(b))
+#define MAC16_16_P13(c,a,b) ((c)+(a)*(b))
+#define MULT16_16_Q11_32(a,b) ((a)*(b))
+#define MULT16_16_Q13(a,b) ((a)*(b))
+#define MULT16_16_Q14(a,b) ((a)*(b))
+#define MULT16_16_Q15(a,b) ((a)*(b))
+#define MULT16_16_P15(a,b) ((a)*(b))
+#define MULT16_16_P13(a,b) ((a)*(b))
+#define MULT16_16_P14(a,b) ((a)*(b))
+
+#define DIV32_16(a,b) (((spx_word32_t)(a))/(spx_word16_t)(b))
+#define PDIV32_16(a,b) (((spx_word32_t)(a))/(spx_word16_t)(b))
+#define DIV32(a,b) (((spx_word32_t)(a))/(spx_word32_t)(b))
+#define PDIV32(a,b) (((spx_word32_t)(a))/(spx_word32_t)(b))
+
+#define WORD2INT(x) ((x) < -32767.5f ? -32768 : \
+ ((x) > 32766.5f ? 32767 : (spx_int16_t)floor(.5 + (x))))
+#endif
+
+
+#if defined(CONFIG_TI_C54X) || defined(CONFIG_TI_C55X)
+
+/* 2 on TI C5x DSP */
+#define BYTES_PER_CHAR 2
+#define BITS_PER_CHAR 16
+#define LOG2_BITS_PER_CHAR 4
+
+#else
+
+#define BYTES_PER_CHAR 1
+#define BITS_PER_CHAR 8
+#define LOG2_BITS_PER_CHAR 3
+
+#endif
+
+
+
+#ifdef FIXED_DEBUG
+extern long long spx_mips;
+#endif
+
+
+#endif
diff --git a/lib/speexdsp/libspeexdsp/bfin.h b/lib/speexdsp/libspeexdsp/bfin.h
new file mode 100644
index 00000000..b934cf2f
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/bfin.h
@@ -0,0 +1,15 @@
+/* Common Blackfin assembly defines
+ *
+ * Copyright (C) 2005-2009 Analog Devices
+ */
+
+#if __GNUC__ <= 3
+/* GCC-3.4 and older did not use hardware loops and thus did not have
+ * register constraints for declaring clobbers.
+ */
+# define BFIN_HWLOOP0_REGS
+# define BFIN_HWLOOP1_REGS
+#else
+# define BFIN_HWLOOP0_REGS , "LB0", "LT0", "LC0"
+# define BFIN_HWLOOP1_REGS , "LB1", "LT1", "LC1"
+#endif
diff --git a/lib/speexdsp/libspeexdsp/buffer.c b/lib/speexdsp/libspeexdsp/buffer.c
new file mode 100644
index 00000000..b06a1554
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/buffer.c
@@ -0,0 +1,176 @@
+/* Copyright (C) 2007 Jean-Marc Valin
+
+ File: buffer.c
+ This is a very simple ring buffer implementation. It is not thread-safe
+ so you need to do your own locking.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. 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.
+
+ 3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+#include "os_support.h"
+#include "arch.h"
+#include "speex/speex_buffer.h"
+
+struct SpeexBuffer_ {
+ char *data;
+ int size;
+ int read_ptr;
+ int write_ptr;
+ int available;
+};
+
+EXPORT SpeexBuffer *speex_buffer_init(int size)
+{
+ SpeexBuffer *st = speex_alloc(sizeof(SpeexBuffer));
+ st->data = speex_alloc(size);
+ st->size = size;
+ st->read_ptr = 0;
+ st->write_ptr = 0;
+ st->available = 0;
+ return st;
+}
+
+EXPORT void speex_buffer_destroy(SpeexBuffer *st)
+{
+ speex_free(st->data);
+ speex_free(st);
+}
+
+EXPORT int speex_buffer_write(SpeexBuffer *st, void *_data, int len)
+{
+ int end;
+ int end1;
+ char *data = _data;
+ if (len > st->size)
+ {
+ data += len-st->size;
+ len = st->size;
+ }
+ end = st->write_ptr + len;
+ end1 = end;
+ if (end1 > st->size)
+ end1 = st->size;
+ SPEEX_COPY(st->data + st->write_ptr, data, end1 - st->write_ptr);
+ if (end > st->size)
+ {
+ end -= st->size;
+ SPEEX_COPY(st->data, data+end1 - st->write_ptr, end);
+ }
+ st->available += len;
+ if (st->available > st->size)
+ {
+ st->available = st->size;
+ st->read_ptr = st->write_ptr;
+ }
+ st->write_ptr += len;
+ if (st->write_ptr > st->size)
+ st->write_ptr -= st->size;
+ return len;
+}
+
+EXPORT int speex_buffer_writezeros(SpeexBuffer *st, int len)
+{
+ /* This is almost the same as for speex_buffer_write() but using
+ SPEEX_MEMSET() instead of SPEEX_COPY(). Update accordingly. */
+ int end;
+ int end1;
+ if (len > st->size)
+ {
+ len = st->size;
+ }
+ end = st->write_ptr + len;
+ end1 = end;
+ if (end1 > st->size)
+ end1 = st->size;
+ SPEEX_MEMSET(st->data + st->write_ptr, 0, end1 - st->write_ptr);
+ if (end > st->size)
+ {
+ end -= st->size;
+ SPEEX_MEMSET(st->data, 0, end);
+ }
+ st->available += len;
+ if (st->available > st->size)
+ {
+ st->available = st->size;
+ st->read_ptr = st->write_ptr;
+ }
+ st->write_ptr += len;
+ if (st->write_ptr > st->size)
+ st->write_ptr -= st->size;
+ return len;
+}
+
+EXPORT int speex_buffer_read(SpeexBuffer *st, void *_data, int len)
+{
+ int end, end1;
+ char *data = _data;
+ if (len > st->available)
+ {
+ SPEEX_MEMSET(data+st->available, 0, len - st->available);
+ len = st->available;
+ }
+ end = st->read_ptr + len;
+ end1 = end;
+ if (end1 > st->size)
+ end1 = st->size;
+ SPEEX_COPY(data, st->data + st->read_ptr, end1 - st->read_ptr);
+
+ if (end > st->size)
+ {
+ end -= st->size;
+ SPEEX_COPY(data+end1 - st->read_ptr, st->data, end);
+ }
+ st->available -= len;
+ st->read_ptr += len;
+ if (st->read_ptr > st->size)
+ st->read_ptr -= st->size;
+ return len;
+}
+
+EXPORT int speex_buffer_get_available(SpeexBuffer *st)
+{
+ return st->available;
+}
+
+EXPORT int speex_buffer_resize(SpeexBuffer *st, int len)
+{
+ int old_len = st->size;
+ if (len > old_len)
+ {
+ st->data = speex_realloc(st->data, len);
+ /* FIXME: move data/pointers properly for growing the buffer */
+ } else {
+ /* FIXME: move data/pointers properly for shrinking the buffer */
+ st->data = speex_realloc(st->data, len);
+ }
+ return len;
+}
diff --git a/lib/speexdsp/libspeexdsp/config.h b/lib/speexdsp/libspeexdsp/config.h
new file mode 100644
index 00000000..060002b4
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/config.h
@@ -0,0 +1,13 @@
+// Copyright 2023 jacqueline <me@jacqueline.id.au>
+//
+// SPDX-License-Identifier: GPL-3.0-only
+
+#pragma once
+
+#define FIXED_POINT
+#define EXPORT
+#define STDC_HEADERS
+#define USE_SMALLFT
+
+#define DISABLE_ENCODER
+#define DISABLE_FLOAT_API
diff --git a/lib/speexdsp/libspeexdsp/echo_diagnostic.m b/lib/speexdsp/libspeexdsp/echo_diagnostic.m
new file mode 100644
index 00000000..aebf3906
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/echo_diagnostic.m
@@ -0,0 +1,72 @@
+% Attempts to diagnose AEC problems from recorded samples
+%
+% out = echo_diagnostic(rec_file, play_file, out_file, tail_length)
+%
+% Computes the full matrix inversion to cancel echo from the
+% recording 'rec_file' using the far end signal 'play_file' using
+% a filter length of 'tail_length'. The output is saved to 'out_file'.
+function out = echo_diagnostic(rec_file, play_file, out_file, tail_length)
+
+F=fopen(rec_file,'rb');
+rec=fread(F,Inf,'short');
+fclose (F);
+F=fopen(play_file,'rb');
+play=fread(F,Inf,'short');
+fclose (F);
+
+rec = [rec; zeros(1024,1)];
+play = [play; zeros(1024,1)];
+
+N = length(rec);
+corr = real(ifft(fft(rec).*conj(fft(play))));
+acorr = real(ifft(fft(play).*conj(fft(play))));
+
+[a,b] = max(corr);
+
+if b > N/2
+ b = b-N;
+end
+printf ("Far end to near end delay is %d samples\n", b);
+if (b > .3*tail_length)
+ printf ('This is too much delay, try delaying the far-end signal a bit\n');
+else if (b < 0)
+ printf ('You have a negative delay, the echo canceller has no chance to cancel anything!\n');
+ else
+ printf ('Delay looks OK.\n');
+ end
+ end
+end
+N2 = round(N/2);
+corr1 = real(ifft(fft(rec(1:N2)).*conj(fft(play(1:N2)))));
+corr2 = real(ifft(fft(rec(N2+1:end)).*conj(fft(play(N2+1:end)))));
+
+[a,b1] = max(corr1);
+if b1 > N2/2
+ b1 = b1-N2;
+end
+[a,b2] = max(corr2);
+if b2 > N2/2
+ b2 = b2-N2;
+end
+drift = (b1-b2)/N2;
+printf ('Drift estimate is %f%% (%d samples)\n', 100*drift, b1-b2);
+if abs(b1-b2) < 10
+ printf ('A drift of a few (+-10) samples is normal.\n');
+else
+ if abs(b1-b2) < 30
+ printf ('There may be (not sure) excessive clock drift. Is the capture and playback done on the same soundcard?\n');
+ else
+ printf ('Your clock is drifting! No way the AEC will be able to do anything with that. Most likely, you''re doing capture and playback from two different cards.\n');
+ end
+ end
+end
+acorr(1) = .001+1.00001*acorr(1);
+AtA = toeplitz(acorr(1:tail_length));
+bb = corr(1:tail_length);
+h = AtA\bb;
+
+out = (rec - filter(h, 1, play));
+
+F=fopen(out_file,'w');
+fwrite(F,out,'short');
+fclose (F);
diff --git a/lib/speexdsp/libspeexdsp/fftwrap.c b/lib/speexdsp/libspeexdsp/fftwrap.c
new file mode 100644
index 00000000..4573479d
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/fftwrap.c
@@ -0,0 +1,448 @@
+/* Copyright (C) 2005-2006 Jean-Marc Valin
+ File: fftwrap.c
+
+ Wrapper for various FFTs
+
+ 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.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "arch.h"
+#include "os_support.h"
+
+#define MAX_FFT_SIZE 2048
+
+#ifdef FIXED_POINT
+static int maximize_range(spx_word16_t *in, spx_word16_t *out, spx_word16_t bound, int len)
+{
+ int i, shift;
+ spx_word16_t max_val = 0;
+ for (i=0;i<len;i++)
+ {
+ if (in[i]>max_val)
+ max_val = in[i];
+ if (-in[i]>max_val)
+ max_val = -in[i];
+ }
+ shift=0;
+ while (max_val <= (bound>>1) && max_val != 0)
+ {
+ max_val <<= 1;
+ shift++;
+ }
+ for (i=0;i<len;i++)
+ {
+ out[i] = SHL16(in[i], shift);
+ }
+ return shift;
+}
+
+static void renorm_range(spx_word16_t *in, spx_word16_t *out, int shift, int len)
+{
+ int i;
+ for (i=0;i<len;i++)
+ {
+ out[i] = PSHR16(in[i], shift);
+ }
+}
+#endif
+
+#ifdef USE_SMALLFT
+
+#include "smallft.h"
+#include <math.h>
+
+void *spx_fft_init(int size)
+{
+ struct drft_lookup *table;
+ table = speex_alloc(sizeof(struct drft_lookup));
+ spx_drft_init((struct drft_lookup *)table, size);
+ return (void*)table;
+}
+
+void spx_fft_destroy(void *table)
+{
+ spx_drft_clear(table);
+ speex_free(table);
+}
+
+void spx_fft(void *table, float *in, float *out)
+{
+ if (in==out)
+ {
+ int i;
+ float scale = 1./((struct drft_lookup *)table)->n;
+ speex_warning("FFT should not be done in-place");
+ for (i=0;i<((struct drft_lookup *)table)->n;i++)
+ out[i] = scale*in[i];
+ } else {
+ int i;
+ float scale = 1./((struct drft_lookup *)table)->n;
+ for (i=0;i<((struct drft_lookup *)table)->n;i++)
+ out[i] = scale*in[i];
+ }
+ spx_drft_forward((struct drft_lookup *)table, out);
+}
+
+void spx_ifft(void *table, float *in, float *out)
+{
+ if (in==out)
+ {
+ speex_warning("FFT should not be done in-place");
+ } else {
+ int i;
+ for (i=0;i<((struct drft_lookup *)table)->n;i++)
+ out[i] = in[i];
+ }
+ spx_drft_backward((struct drft_lookup *)table, out);
+}
+
+#elif defined(USE_INTEL_MKL)
+#include <mkl.h>
+
+struct mkl_config {
+ DFTI_DESCRIPTOR_HANDLE desc;
+ int N;
+};
+
+void *spx_fft_init(int size)
+{
+ struct mkl_config *table = (struct mkl_config *) speex_alloc(sizeof(struct mkl_config));
+ table->N = size;
+ DftiCreateDescriptor(&table->desc, DFTI_SINGLE, DFTI_REAL, 1, size);
+ DftiSetValue(table->desc, DFTI_PACKED_FORMAT, DFTI_PACK_FORMAT);
+ DftiSetValue(table->desc, DFTI_PLACEMENT, DFTI_NOT_INPLACE);
+ DftiSetValue(table->desc, DFTI_FORWARD_SCALE, 1.0f / size);
+ DftiCommitDescriptor(table->desc);
+ return table;
+}
+
+void spx_fft_destroy(void *table)
+{
+ struct mkl_config *t = (struct mkl_config *) table;
+ DftiFreeDescriptor(t->desc);
+ speex_free(table);
+}
+
+void spx_fft(void *table, spx_word16_t *in, spx_word16_t *out)
+{
+ struct mkl_config *t = (struct mkl_config *) table;
+ DftiComputeForward(t->desc, in, out);
+}
+
+void spx_ifft(void *table, spx_word16_t *in, spx_word16_t *out)
+{
+ struct mkl_config *t = (struct mkl_config *) table;
+ DftiComputeBackward(t->desc, in, out);
+}
+
+#elif defined(USE_INTEL_IPP)
+
+#include <ipps.h>
+
+struct ipp_fft_config
+{
+ IppsDFTSpec_R_32f *dftSpec;
+ Ipp8u *buffer;
+};
+
+void *spx_fft_init(int size)
+{
+ int bufferSize = 0;
+ int hint;
+ struct ipp_fft_config *table;
+
+ table = (struct ipp_fft_config *)speex_alloc(sizeof(struct ipp_fft_config));
+
+ /* there appears to be no performance difference between ippAlgHintFast and
+ ippAlgHintAccurate when using the with the floating point version
+ of the fft. */
+ hint = ippAlgHintAccurate;
+
+ ippsDFTInitAlloc_R_32f(&table->dftSpec, size, IPP_FFT_DIV_FWD_BY_N, hint);
+
+ ippsDFTGetBufSize_R_32f(table->dftSpec, &bufferSize);
+ table->buffer = ippsMalloc_8u(bufferSize);
+
+ return table;
+}
+
+void spx_fft_destroy(void *table)
+{
+ struct ipp_fft_config *t = (struct ipp_fft_config *)table;
+ ippsFree(t->buffer);
+ ippsDFTFree_R_32f(t->dftSpec);
+ speex_free(t);
+}
+
+void spx_fft(void *table, spx_word16_t *in, spx_word16_t *out)
+{
+ struct ipp_fft_config *t = (struct ipp_fft_config *)table;
+ ippsDFTFwd_RToPack_32f(in, out, t->dftSpec, t->buffer);
+}
+
+void spx_ifft(void *table, spx_word16_t *in, spx_word16_t *out)
+{
+ struct ipp_fft_config *t = (struct ipp_fft_config *)table;
+ ippsDFTInv_PackToR_32f(in, out, t->dftSpec, t->buffer);
+}
+
+#elif defined(USE_GPL_FFTW3)
+
+#include <fftw3.h>
+
+struct fftw_config {
+ float *in;
+ float *out;
+ fftwf_plan fft;
+ fftwf_plan ifft;
+ int N;
+};
+
+void *spx_fft_init(int size)
+{
+ struct fftw_config *table = (struct fftw_config *) speex_alloc(sizeof(struct fftw_config));
+ table->in = fftwf_malloc(sizeof(float) * (size+2));
+ table->out = fftwf_malloc(sizeof(float) * (size+2));
+
+ table->fft = fftwf_plan_dft_r2c_1d(size, table->in, (fftwf_complex *) table->out, FFTW_PATIENT);
+ table->ifft = fftwf_plan_dft_c2r_1d(size, (fftwf_complex *) table->in, table->out, FFTW_PATIENT);
+
+ table->N = size;
+ return table;
+}
+
+void spx_fft_destroy(void *table)
+{
+ struct fftw_config *t = (struct fftw_config *) table;
+ fftwf_destroy_plan(t->fft);
+ fftwf_destroy_plan(t->ifft);
+ fftwf_free(t->in);
+ fftwf_free(t->out);
+ speex_free(table);
+}
+
+
+void spx_fft(void *table, spx_word16_t *in, spx_word16_t *out)
+{
+ int i;
+ struct fftw_config *t = (struct fftw_config *) table;
+ const int N = t->N;
+ float *iptr = t->in;
+ float *optr = t->out;
+ const float m = 1.0 / N;
+ for(i=0;i<N;++i)
+ iptr[i]=in[i] * m;
+
+ fftwf_execute(t->fft);
+
+ out[0] = optr[0];
+ for(i=1;i<N;++i)
+ out[i] = optr[i+1];
+}
+
+void spx_ifft(void *table, spx_word16_t *in, spx_word16_t *out)
+{
+ int i;
+ struct fftw_config *t = (struct fftw_config *) table;
+ const int N = t->N;
+ float *iptr = t->in;
+ float *optr = t->out;
+
+ iptr[0] = in[0];
+ iptr[1] = 0.0f;
+ for(i=1;i<N;++i)
+ iptr[i+1] = in[i];
+ iptr[N+1] = 0.0f;
+
+ fftwf_execute(t->ifft);
+
+ for(i=0;i<N;++i)
+ out[i] = optr[i];
+}
+
+#elif defined(USE_KISS_FFT)
+
+#include "kiss_fftr.h"
+#include "kiss_fft.h"
+
+struct kiss_config {
+ kiss_fftr_cfg forward;
+ kiss_fftr_cfg backward;
+ int N;
+};
+
+void *spx_fft_init(int size)
+{
+ struct kiss_config *table;
+ table = (struct kiss_config*)speex_alloc(sizeof(struct kiss_config));
+ table->forward = kiss_fftr_alloc(size,0,NULL,NULL);
+ table->backward = kiss_fftr_alloc(size,1,NULL,NULL);
+ table->N = size;
+ return table;
+}
+
+void spx_fft_destroy(void *table)
+{
+ struct kiss_config *t = (struct kiss_config *)table;
+ kiss_fftr_free(t->forward);
+ kiss_fftr_free(t->backward);
+ speex_free(table);
+}
+
+#ifdef FIXED_POINT
+
+void spx_fft(void *table, spx_word16_t *in, spx_word16_t *out)
+{
+ int shift;
+ struct kiss_config *t = (struct kiss_config *)table;
+ shift = maximize_range(in, in, 32000, t->N);
+ kiss_fftr2(t->forward, in, out);
+ renorm_range(in, in, shift, t->N);
+ renorm_range(out, out, shift, t->N);
+}
+
+#else
+
+void spx_fft(void *table, spx_word16_t *in, spx_word16_t *out)
+{
+ int i;
+ float scale;
+ struct kiss_config *t = (struct kiss_config *)table;
+ scale = 1./t->N;
+ kiss_fftr2(t->forward, in, out);
+ for (i=0;i<t->N;i++)
+ out[i] *= scale;
+}
+#endif
+
+void spx_ifft(void *table, spx_word16_t *in, spx_word16_t *out)
+{
+ struct kiss_config *t = (struct kiss_config *)table;
+ kiss_fftri2(t->backward, in, out);
+}
+
+
+#else
+
+#error No other FFT implemented
+
+#endif
+
+
+#ifdef FIXED_POINT
+/*#include "smallft.h"*/
+
+
+void spx_fft_float(void *table, float *in, float *out)
+{
+ int i;
+#ifdef USE_SMALLFT
+ int N = ((struct drft_lookup *)table)->n;
+#elif defined(USE_KISS_FFT)
+ int N = ((struct kiss_config *)table)->N;
+#else
+#endif
+#ifdef VAR_ARRAYS
+ spx_word16_t _in[N];
+ spx_word16_t _out[N];
+#else
+ spx_word16_t _in[MAX_FFT_SIZE];
+ spx_word16_t _out[MAX_FFT_SIZE];
+#endif
+ for (i=0;i<N;i++)
+ _in[i] = (int)floor(.5+in[i]);
+ spx_fft(table, _in, _out);
+ for (i=0;i<N;i++)
+ out[i] = _out[i];
+#if 0
+ if (!fixed_point)
+ {
+ float scale;
+ struct drft_lookup t;
+ spx_drft_init(&t, ((struct kiss_config *)table)->N);
+ scale = 1./((struct kiss_config *)table)->N;
+ for (i=0;i<((struct kiss_config *)table)->N;i++)
+ out[i] = scale*in[i];
+ spx_drft_forward(&t, out);
+ spx_drft_clear(&t);
+ }
+#endif
+}
+
+void spx_ifft_float(void *table, float *in, float *out)
+{
+ int i;
+#ifdef USE_SMALLFT
+ int N = ((struct drft_lookup *)table)->n;
+#elif defined(USE_KISS_FFT)
+ int N = ((struct kiss_config *)table)->N;
+#else
+#endif
+#ifdef VAR_ARRAYS
+ spx_word16_t _in[N];
+ spx_word16_t _out[N];
+#else
+ spx_word16_t _in[MAX_FFT_SIZE];
+ spx_word16_t _out[MAX_FFT_SIZE];
+#endif
+ for (i=0;i<N;i++)
+ _in[i] = (int)floor(.5+in[i]);
+ spx_ifft(table, _in, _out);
+ for (i=0;i<N;i++)
+ out[i] = _out[i];
+#if 0
+ if (!fixed_point)
+ {
+ int i;
+ struct drft_lookup t;
+ spx_drft_init(&t, ((struct kiss_config *)table)->N);
+ for (i=0;i<((struct kiss_config *)table)->N;i++)
+ out[i] = in[i];
+ spx_drft_backward(&t, out);
+ spx_drft_clear(&t);
+ }
+#endif
+}
+
+#else
+
+void spx_fft_float(void *table, float *in, float *out)
+{
+ spx_fft(table, in, out);
+}
+void spx_ifft_float(void *table, float *in, float *out)
+{
+ spx_ifft(table, in, out);
+}
+
+#endif
diff --git a/lib/speexdsp/libspeexdsp/fftwrap.h b/lib/speexdsp/libspeexdsp/fftwrap.h
new file mode 100644
index 00000000..2c074255
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/fftwrap.h
@@ -0,0 +1,58 @@
+/* Copyright (C) 2005 Jean-Marc Valin
+ File: fftwrap.h
+
+ Wrapper for various FFTs
+
+ 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.
+
+*/
+
+#ifndef FFTWRAP_H
+#define FFTWRAP_H
+
+#include "arch.h"
+
+/** Compute tables for an FFT */
+void *spx_fft_init(int size);
+
+/** Destroy tables for an FFT */
+void spx_fft_destroy(void *table);
+
+/** Forward (real to half-complex) transform */
+void spx_fft(void *table, spx_word16_t *in, spx_word16_t *out);
+
+/** Backward (half-complex to real) transform */
+void spx_ifft(void *table, spx_word16_t *in, spx_word16_t *out);
+
+/** Forward (real to half-complex) transform of float data */
+void spx_fft_float(void *table, float *in, float *out);
+
+/** Backward (half-complex to real) transform of float data */
+void spx_ifft_float(void *table, float *in, float *out);
+
+#endif
diff --git a/lib/speexdsp/libspeexdsp/filterbank.c b/lib/speexdsp/libspeexdsp/filterbank.c
new file mode 100644
index 00000000..c8fdd7a2
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/filterbank.c
@@ -0,0 +1,227 @@
+/* Copyright (C) 2006 Jean-Marc Valin */
+/**
+ @file filterbank.c
+ @brief Converting between psd and filterbank
+ */
+/*
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. 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.
+
+ 3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "filterbank.h"
+#include "arch.h"
+#include <math.h>
+#include "math_approx.h"
+#include "os_support.h"
+
+#ifdef FIXED_POINT
+
+#define toBARK(n) (MULT16_16(26829,spx_atan(SHR32(MULT16_16(97,n),2))) + MULT16_16(4588,spx_atan(MULT16_32_Q15(20,MULT16_16(n,n)))) + MULT16_16(3355,n))
+
+#else
+#define toBARK(n) (13.1f*atan(.00074f*(n))+2.24f*atan((n)*(n)*1.85e-8f)+1e-4f*(n))
+#endif
+
+#define toMEL(n) (2595.f*log10(1.f+(n)/700.f))
+
+FilterBank *filterbank_new(int banks, spx_word32_t sampling, int len, int type)
+{
+ FilterBank *bank;
+ spx_word32_t df;
+ spx_word32_t max_mel, mel_interval;
+ int i;
+ int id1;
+ int id2;
+ df = DIV32(SHL32(sampling,15),MULT16_16(2,len));
+ max_mel = toBARK(EXTRACT16(sampling/2));
+ mel_interval = PDIV32(max_mel,banks-1);
+
+ bank = (FilterBank*)speex_alloc(sizeof(FilterBank));
+ bank->nb_banks = banks;
+ bank->len = len;
+ bank->bank_left = (int*)speex_alloc(len*sizeof(int));
+ bank->bank_right = (int*)speex_alloc(len*sizeof(int));
+ bank->filter_left = (spx_word16_t*)speex_alloc(len*sizeof(spx_word16_t));
+ bank->filter_right = (spx_word16_t*)speex_alloc(len*sizeof(spx_word16_t));
+ /* Think I can safely disable normalisation that for fixed-point (and probably float as well) */
+#ifndef FIXED_POINT
+ bank->scaling = (float*)speex_alloc(banks*sizeof(float));
+#endif
+ for (i=0;i<len;i++)
+ {
+ spx_word16_t curr_freq;
+ spx_word32_t mel;
+ spx_word16_t val;
+ curr_freq = EXTRACT16(MULT16_32_P15(i,df));
+ mel = toBARK(curr_freq);
+ if (mel > max_mel)
+ break;
+#ifdef FIXED_POINT
+ id1 = DIV32(mel,mel_interval);
+#else
+ id1 = (int)(floor(mel/mel_interval));
+#endif
+ if (id1>banks-2)
+ {
+ id1 = banks-2;
+ val = Q15_ONE;
+ } else {
+ val = DIV32_16(mel - id1*mel_interval,EXTRACT16(PSHR32(mel_interval,15)));
+ }
+ id2 = id1+1;
+ bank->bank_left[i] = id1;
+ bank->filter_left[i] = SUB16(Q15_ONE,val);
+ bank->bank_right[i] = id2;
+ bank->filter_right[i] = val;
+ }
+
+ /* Think I can safely disable normalisation for fixed-point (and probably float as well) */
+#ifndef FIXED_POINT
+ for (i=0;i<bank->nb_banks;i++)
+ bank->scaling[i] = 0;
+ for (i=0;i<bank->len;i++)
+ {
+ int id = bank->bank_left[i];
+ bank->scaling[id] += bank->filter_left[i];
+ id = bank->bank_right[i];
+ bank->scaling[id] += bank->filter_right[i];
+ }
+ for (i=0;i<bank->nb_banks;i++)
+ bank->scaling[i] = Q15_ONE/(bank->scaling[i]);
+#endif
+ return bank;
+}
+
+void filterbank_destroy(FilterBank *bank)
+{
+ speex_free(bank->bank_left);
+ speex_free(bank->bank_right);
+ speex_free(bank->filter_left);
+ speex_free(bank->filter_right);
+#ifndef FIXED_POINT
+ speex_free(bank->scaling);
+#endif
+ speex_free(bank);
+}
+
+void filterbank_compute_bank32(FilterBank *bank, spx_word32_t *ps, spx_word32_t *mel)
+{
+ int i;
+ for (i=0;i<bank->nb_banks;i++)
+ mel[i] = 0;
+
+ for (i=0;i<bank->len;i++)
+ {
+ int id;
+ id = bank->bank_left[i];
+ mel[id] += MULT16_32_P15(bank->filter_left[i],ps[i]);
+ id = bank->bank_right[i];
+ mel[id] += MULT16_32_P15(bank->filter_right[i],ps[i]);
+ }
+ /* Think I can safely disable normalisation that for fixed-point (and probably float as well) */
+#ifndef FIXED_POINT
+ /*for (i=0;i<bank->nb_banks;i++)
+ mel[i] = MULT16_32_P15(Q15(bank->scaling[i]),mel[i]);
+ */
+#endif
+}
+
+void filterbank_compute_psd16(FilterBank *bank, spx_word16_t *mel, spx_word16_t *ps)
+{
+ int i;
+ for (i=0;i<bank->len;i++)
+ {
+ spx_word32_t tmp;
+ int id1, id2;
+ id1 = bank->bank_left[i];
+ id2 = bank->bank_right[i];
+ tmp = MULT16_16(mel[id1],bank->filter_left[i]);
+ tmp += MULT16_16(mel[id2],bank->filter_right[i]);
+ ps[i] = EXTRACT16(PSHR32(tmp,15));
+ }
+}
+
+
+#ifndef FIXED_POINT
+void filterbank_compute_bank(FilterBank *bank, float *ps, float *mel)
+{
+ int i;
+ for (i=0;i<bank->nb_banks;i++)
+ mel[i] = 0;
+
+ for (i=0;i<bank->len;i++)
+ {
+ int id = bank->bank_left[i];
+ mel[id] += bank->filter_left[i]*ps[i];
+ id = bank->bank_right[i];
+ mel[id] += bank->filter_right[i]*ps[i];
+ }
+ for (i=0;i<bank->nb_banks;i++)
+ mel[i] *= bank->scaling[i];
+}
+
+void filterbank_compute_psd(FilterBank *bank, float *mel, float *ps)
+{
+ int i;
+ for (i=0;i<bank->len;i++)
+ {
+ int id = bank->bank_left[i];
+ ps[i] = mel[id]*bank->filter_left[i];
+ id = bank->bank_right[i];
+ ps[i] += mel[id]*bank->filter_right[i];
+ }
+}
+
+void filterbank_psy_smooth(FilterBank *bank, float *ps, float *mask)
+{
+ /* Low freq slope: 14 dB/Bark*/
+ /* High freq slope: 9 dB/Bark*/
+ /* Noise vs tone: 5 dB difference */
+ /* FIXME: Temporary kludge */
+ float bark[100];
+ int i;
+ /* Assumes 1/3 Bark resolution */
+ float decay_low = 0.34145f;
+ float decay_high = 0.50119f;
+ filterbank_compute_bank(bank, ps, bark);
+ for (i=1;i<bank->nb_banks;i++)
+ {
+ /*float decay_high = 13-1.6*log10(bark[i-1]);
+ decay_high = pow(10,(-decay_high/30.f));*/
+ bark[i] = bark[i] + decay_high*bark[i-1];
+ }
+ for (i=bank->nb_banks-2;i>=0;i--)
+ {
+ bark[i] = bark[i] + decay_low*bark[i+1];
+ }
+ filterbank_compute_psd(bank, bark, mask);
+}
+
+#endif
diff --git a/lib/speexdsp/libspeexdsp/filterbank.h b/lib/speexdsp/libspeexdsp/filterbank.h
new file mode 100644
index 00000000..3e889a22
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/filterbank.h
@@ -0,0 +1,66 @@
+/* Copyright (C) 2006 Jean-Marc Valin */
+/**
+ @file filterbank.h
+ @brief Converting between psd and filterbank
+ */
+/*
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. 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.
+
+ 3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+*/
+
+#ifndef FILTERBANK_H
+#define FILTERBANK_H
+
+#include "arch.h"
+
+typedef struct {
+ int *bank_left;
+ int *bank_right;
+ spx_word16_t *filter_left;
+ spx_word16_t *filter_right;
+#ifndef FIXED_POINT
+ float *scaling;
+#endif
+ int nb_banks;
+ int len;
+} FilterBank;
+
+
+FilterBank *filterbank_new(int banks, spx_word32_t sampling, int len, int type);
+
+void filterbank_destroy(FilterBank *bank);
+
+void filterbank_compute_bank32(FilterBank *bank, spx_word32_t *ps, spx_word32_t *mel);
+
+void filterbank_compute_psd16(FilterBank *bank, spx_word16_t *mel, spx_word16_t *psd);
+
+#ifndef FIXED_POINT
+void filterbank_compute_bank(FilterBank *bank, float *psd, float *mel);
+void filterbank_compute_psd(FilterBank *bank, float *mel, float *psd);
+#endif
+
+
+#endif
diff --git a/lib/speexdsp/libspeexdsp/fixed_arm4.h b/lib/speexdsp/libspeexdsp/fixed_arm4.h
new file mode 100644
index 00000000..a7040b8a
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/fixed_arm4.h
@@ -0,0 +1,135 @@
+/* Copyright (C) 2004 Jean-Marc Valin */
+/**
+ @file fixed_arm4.h
+ @brief ARM4 fixed-point operations
+*/
+/*
+ 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.
+*/
+
+#ifndef FIXED_ARM4_H
+#define FIXED_ARM4_H
+
+#undef MULT16_32_Q15
+static inline spx_word32_t MULT16_32_Q15(spx_word16_t x, spx_word32_t y) {
+ int res;
+ int dummy;
+ asm (
+ "smull %0,%1,%2,%3 \n\t"
+ "mov %0, %0, lsr #15 \n\t"
+ "add %0, %0, %1, lsl #17 \n\t"
+ : "=&r"(res), "=&r" (dummy)
+ : "r"(y),"r"((int)x));
+ return(res);
+}
+
+#undef DIV32_16
+static inline short DIV32_16(int a, int b)
+{
+ int res=0;
+ int dead1, dead2, dead3, dead4, dead5;
+ __asm__ __volatile__ (
+ "\teor %5, %0, %1\n"
+ "\tmovs %4, %0\n"
+ "\trsbmi %0, %0, #0 \n"
+ "\tmovs %4, %1\n"
+ "\trsbmi %1, %1, #0 \n"
+ "\tmov %4, #1\n"
+
+ "\tsubs %3, %0, %1, asl #14 \n"
+ "\tmovpl %0, %3 \n"
+ "\torrpl %2, %2, %4, asl #14 \n"
+
+ "\tsubs %3, %0, %1, asl #13 \n"
+ "\tmovpl %0, %3 \n"
+ "\torrpl %2, %2, %4, asl #13 \n"
+
+ "\tsubs %3, %0, %1, asl #12 \n"
+ "\tmovpl %0, %3 \n"
+ "\torrpl %2, %2, %4, asl #12 \n"
+
+ "\tsubs %3, %0, %1, asl #11 \n"
+ "\tmovpl %0, %3 \n"
+ "\torrpl %2, %2, %4, asl #11 \n"
+
+ "\tsubs %3, %0, %1, asl #10 \n"
+ "\tmovpl %0, %3 \n"
+ "\torrpl %2, %2, %4, asl #10 \n"
+
+ "\tsubs %3, %0, %1, asl #9 \n"
+ "\tmovpl %0, %3 \n"
+ "\torrpl %2, %2, %4, asl #9 \n"
+
+ "\tsubs %3, %0, %1, asl #8 \n"
+ "\tmovpl %0, %3 \n"
+ "\torrpl %2, %2, %4, asl #8 \n"
+
+ "\tsubs %3, %0, %1, asl #7 \n"
+ "\tmovpl %0, %3 \n"
+ "\torrpl %2, %2, %4, asl #7 \n"
+
+ "\tsubs %3, %0, %1, asl #6 \n"
+ "\tmovpl %0, %3 \n"
+ "\torrpl %2, %2, %4, asl #6 \n"
+
+ "\tsubs %3, %0, %1, asl #5 \n"
+ "\tmovpl %0, %3 \n"
+ "\torrpl %2, %2, %4, asl #5 \n"
+
+ "\tsubs %3, %0, %1, asl #4 \n"
+ "\tmovpl %0, %3 \n"
+ "\torrpl %2, %2, %4, asl #4 \n"
+
+ "\tsubs %3, %0, %1, asl #3 \n"
+ "\tmovpl %0, %3 \n"
+ "\torrpl %2, %2, %4, asl #3 \n"
+
+ "\tsubs %3, %0, %1, asl #2 \n"
+ "\tmovpl %0, %3 \n"
+ "\torrpl %2, %2, %4, asl #2 \n"
+
+ "\tsubs %3, %0, %1, asl #1 \n"
+ "\tmovpl %0, %3 \n"
+ "\torrpl %2, %2, %4, asl #1 \n"
+
+ "\tsubs %3, %0, %1 \n"
+ "\tmovpl %0, %3 \n"
+ "\torrpl %2, %2, %4 \n"
+
+ "\tmovs %5, %5, lsr #31 \n"
+ "\trsbne %2, %2, #0 \n"
+ : "=r" (dead1), "=r" (dead2), "=r" (res),
+ "=r" (dead3), "=r" (dead4), "=r" (dead5)
+ : "0" (a), "1" (b), "2" (res)
+ : "cc"
+ );
+ return res;
+}
+
+
+#endif
diff --git a/lib/speexdsp/libspeexdsp/fixed_arm5e.h b/lib/speexdsp/libspeexdsp/fixed_arm5e.h
new file mode 100644
index 00000000..15c6d5de
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/fixed_arm5e.h
@@ -0,0 +1,160 @@
+/* Copyright (C) 2003 Jean-Marc Valin */
+/**
+ @file fixed_arm5e.h
+ @brief ARM-tuned fixed-point operations
+*/
+/*
+ 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.
+*/
+
+#ifndef FIXED_ARM5E_H
+#define FIXED_ARM5E_H
+
+#undef MULT16_16
+static inline spx_word32_t MULT16_16(spx_word16_t x, spx_word16_t y) {
+ int res;
+ asm ("smulbb %0,%1,%2;\n"
+ : "=&r"(res)
+ : "%r"(x),"r"(y));
+ return(res);
+}
+
+#undef MAC16_16
+static inline spx_word32_t MAC16_16(spx_word32_t a, spx_word16_t x, spx_word32_t y) {
+ int res;
+ asm ("smlabb %0,%1,%2,%3;\n"
+ : "=&r"(res)
+ : "%r"(x),"r"(y),"r"(a));
+ return(res);
+}
+
+#undef MULT16_32_Q15
+static inline spx_word32_t MULT16_32_Q15(spx_word16_t x, spx_word32_t y) {
+ int res;
+ asm ("smulwb %0,%1,%2;\n"
+ : "=&r"(res)
+ : "%r"(y<<1),"r"(x));
+ return(res);
+}
+
+#undef MAC16_32_Q15
+static inline spx_word32_t MAC16_32_Q15(spx_word32_t a, spx_word16_t x, spx_word32_t y) {
+ int res;
+ asm ("smlawb %0,%1,%2,%3;\n"
+ : "=&r"(res)
+ : "%r"(y<<1),"r"(x),"r"(a));
+ return(res);
+}
+
+#undef DIV32_16
+static inline short DIV32_16(int a, int b)
+{
+ int res=0;
+ int dead1, dead2, dead3, dead4, dead5;
+ __asm__ __volatile__ (
+ "\teor %5, %0, %1\n"
+ "\tmovs %4, %0\n"
+ "\trsbmi %0, %0, #0 \n"
+ "\tmovs %4, %1\n"
+ "\trsbmi %1, %1, #0 \n"
+ "\tmov %4, #1\n"
+
+ "\tsubs %3, %0, %1, asl #14 \n"
+ "\torrpl %2, %2, %4, asl #14 \n"
+ "\tmovpl %0, %3 \n"
+
+ "\tsubs %3, %0, %1, asl #13 \n"
+ "\torrpl %2, %2, %4, asl #13 \n"
+ "\tmovpl %0, %3 \n"
+
+ "\tsubs %3, %0, %1, asl #12 \n"
+ "\torrpl %2, %2, %4, asl #12 \n"
+ "\tmovpl %0, %3 \n"
+
+ "\tsubs %3, %0, %1, asl #11 \n"
+ "\torrpl %2, %2, %4, asl #11 \n"
+ "\tmovpl %0, %3 \n"
+
+ "\tsubs %3, %0, %1, asl #10 \n"
+ "\torrpl %2, %2, %4, asl #10 \n"
+ "\tmovpl %0, %3 \n"
+
+ "\tsubs %3, %0, %1, asl #9 \n"
+ "\torrpl %2, %2, %4, asl #9 \n"
+ "\tmovpl %0, %3 \n"
+
+ "\tsubs %3, %0, %1, asl #8 \n"
+ "\torrpl %2, %2, %4, asl #8 \n"
+ "\tmovpl %0, %3 \n"
+
+ "\tsubs %3, %0, %1, asl #7 \n"
+ "\torrpl %2, %2, %4, asl #7 \n"
+ "\tmovpl %0, %3 \n"
+
+ "\tsubs %3, %0, %1, asl #6 \n"
+ "\torrpl %2, %2, %4, asl #6 \n"
+ "\tmovpl %0, %3 \n"
+
+ "\tsubs %3, %0, %1, asl #5 \n"
+ "\torrpl %2, %2, %4, asl #5 \n"
+ "\tmovpl %0, %3 \n"
+
+ "\tsubs %3, %0, %1, asl #4 \n"
+ "\torrpl %2, %2, %4, asl #4 \n"
+ "\tmovpl %0, %3 \n"
+
+ "\tsubs %3, %0, %1, asl #3 \n"
+ "\torrpl %2, %2, %4, asl #3 \n"
+ "\tmovpl %0, %3 \n"
+
+ "\tsubs %3, %0, %1, asl #2 \n"
+ "\torrpl %2, %2, %4, asl #2 \n"
+ "\tmovpl %0, %3 \n"
+
+ "\tsubs %3, %0, %1, asl #1 \n"
+ "\torrpl %2, %2, %4, asl #1 \n"
+ "\tmovpl %0, %3 \n"
+
+ "\tsubs %3, %0, %1 \n"
+ "\torrpl %2, %2, %4 \n"
+ "\tmovpl %0, %3 \n"
+
+ "\tmovs %5, %5, lsr #31 \n"
+ "\trsbne %2, %2, #0 \n"
+ : "=r" (dead1), "=r" (dead2), "=r" (res),
+ "=r" (dead3), "=r" (dead4), "=r" (dead5)
+ : "0" (a), "1" (b), "2" (res)
+ : "memory", "cc"
+ );
+ return res;
+}
+
+
+
+
+#endif
diff --git a/lib/speexdsp/libspeexdsp/fixed_bfin.h b/lib/speexdsp/libspeexdsp/fixed_bfin.h
new file mode 100644
index 00000000..9a54d64e
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/fixed_bfin.h
@@ -0,0 +1,141 @@
+/* Copyright (C) 2005 Analog Devices
+ Author: Jean-Marc Valin */
+/**
+ @file fixed_bfin.h
+ @brief Blackfin fixed-point operations
+*/
+/*
+ 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.
+*/
+
+#ifndef FIXED_BFIN_H
+#define FIXED_BFIN_H
+
+#include "bfin.h"
+
+#undef PDIV32_16
+static inline spx_word16_t PDIV32_16(spx_word32_t a, spx_word16_t b)
+{
+ spx_word32_t res, bb;
+ bb = b;
+ a += b>>1;
+ __asm__ (
+ "P0 = 15;\n\t"
+ "R0 = %1;\n\t"
+ "R1 = %2;\n\t"
+ //"R0 = R0 + R1;\n\t"
+ "R0 <<= 1;\n\t"
+ "DIVS (R0, R1);\n\t"
+ "LOOP divide%= LC0 = P0;\n\t"
+ "LOOP_BEGIN divide%=;\n\t"
+ "DIVQ (R0, R1);\n\t"
+ "LOOP_END divide%=;\n\t"
+ "R0 = R0.L;\n\t"
+ "%0 = R0;\n\t"
+ : "=m" (res)
+ : "m" (a), "m" (bb)
+ : "P0", "R0", "R1", "ASTAT" BFIN_HWLOOP0_REGS);
+ return res;
+}
+
+#undef DIV32_16
+static inline spx_word16_t DIV32_16(spx_word32_t a, spx_word16_t b)
+{
+ spx_word32_t res, bb;
+ bb = b;
+ /* Make the roundinf consistent with the C version
+ (do we need to do that?)*/
+ if (a<0)
+ a += (b-1);
+ __asm__ (
+ "P0 = 15;\n\t"
+ "R0 = %1;\n\t"
+ "R1 = %2;\n\t"
+ "R0 <<= 1;\n\t"
+ "DIVS (R0, R1);\n\t"
+ "LOOP divide%= LC0 = P0;\n\t"
+ "LOOP_BEGIN divide%=;\n\t"
+ "DIVQ (R0, R1);\n\t"
+ "LOOP_END divide%=;\n\t"
+ "R0 = R0.L;\n\t"
+ "%0 = R0;\n\t"
+ : "=m" (res)
+ : "m" (a), "m" (bb)
+ : "P0", "R0", "R1", "ASTAT" BFIN_HWLOOP0_REGS);
+ return res;
+}
+
+#undef MAX16
+static inline spx_word16_t MAX16(spx_word16_t a, spx_word16_t b)
+{
+ spx_word32_t res;
+ __asm__ (
+ "%1 = %1.L (X);\n\t"
+ "%2 = %2.L (X);\n\t"
+ "%0 = MAX(%1,%2);"
+ : "=d" (res)
+ : "%d" (a), "d" (b)
+ : "ASTAT"
+ );
+ return res;
+}
+
+#undef MULT16_32_Q15
+static inline spx_word32_t MULT16_32_Q15(spx_word16_t a, spx_word32_t b)
+{
+ spx_word32_t res;
+ __asm__
+ (
+ "A1 = %2.L*%1.L (M);\n\t"
+ "A1 = A1 >>> 15;\n\t"
+ "%0 = (A1 += %2.L*%1.H) ;\n\t"
+ : "=&W" (res), "=&d" (b)
+ : "d" (a), "1" (b)
+ : "A1", "ASTAT"
+ );
+ return res;
+}
+
+#undef MAC16_32_Q15
+static inline spx_word32_t MAC16_32_Q15(spx_word32_t c, spx_word16_t a, spx_word32_t b)
+{
+ spx_word32_t res;
+ __asm__
+ (
+ "A1 = %2.L*%1.L (M);\n\t"
+ "A1 = A1 >>> 15;\n\t"
+ "%0 = (A1 += %2.L*%1.H);\n\t"
+ "%0 = %0 + %4;\n\t"
+ : "=&W" (res), "=&d" (b)
+ : "d" (a), "1" (b), "d" (c)
+ : "A1", "ASTAT"
+ );
+ return res;
+}
+
+#endif
diff --git a/lib/speexdsp/libspeexdsp/fixed_debug.h b/lib/speexdsp/libspeexdsp/fixed_debug.h
new file mode 100644
index 00000000..dbf02f19
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/fixed_debug.h
@@ -0,0 +1,497 @@
+/* Copyright (C) 2003 Jean-Marc Valin */
+/**
+ @file fixed_debug.h
+ @brief Fixed-point operations with debugging
+*/
+/*
+ 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.
+*/
+
+#ifndef FIXED_DEBUG_H
+#define FIXED_DEBUG_H
+
+#include <stdio.h>
+
+extern long long spx_mips;
+#define MIPS_INC spx_mips++,
+
+#define QCONST16(x,bits) ((spx_word16_t)(.5+(x)*(((spx_word32_t)1)<<(bits))))
+#define QCONST32(x,bits) ((spx_word32_t)(.5+(x)*(((spx_word32_t)1)<<(bits))))
+
+
+#define VERIFY_SHORT(x) ((x)<=32767&&(x)>=-32768)
+#define VERIFY_INT(x) ((x)<=2147483647LL&&(x)>=-2147483648LL)
+
+static inline short NEG16(int x)
+{
+ int res;
+ if (!VERIFY_SHORT(x))
+ {
+ fprintf (stderr, "NEG16: input is not short: %d\n", (int)x);
+ }
+ res = -x;
+ if (!VERIFY_SHORT(res))
+ fprintf (stderr, "NEG16: output is not short: %d\n", (int)res);
+ spx_mips++;
+ return res;
+}
+static inline int NEG32(long long x)
+{
+ long long res;
+ if (!VERIFY_INT(x))
+ {
+ fprintf (stderr, "NEG16: input is not int: %d\n", (int)x);
+ }
+ res = -x;
+ if (!VERIFY_INT(res))
+ fprintf (stderr, "NEG16: output is not int: %d\n", (int)res);
+ spx_mips++;
+ return res;
+}
+
+#define EXTRACT16(x) _EXTRACT16(x, __FILE__, __LINE__)
+static inline short _EXTRACT16(int x, char *file, int line)
+{
+ int res;
+ if (!VERIFY_SHORT(x))
+ {
+ fprintf (stderr, "EXTRACT16: input is not short: %d in %s: line %d\n", x, file, line);
+ }
+ res = x;
+ spx_mips++;
+ return res;
+}
+
+#define EXTEND32(x) _EXTEND32(x, __FILE__, __LINE__)
+static inline int _EXTEND32(int x, char *file, int line)
+{
+ int res;
+ if (!VERIFY_SHORT(x))
+ {
+ fprintf (stderr, "EXTEND32: input is not short: %d in %s: line %d\n", x, file, line);
+ }
+ res = x;
+ spx_mips++;
+ return res;
+}
+
+#define SHR16(a, shift) _SHR16(a, shift, __FILE__, __LINE__)
+static inline short _SHR16(int a, int shift, char *file, int line)
+{
+ int res;
+ if (!VERIFY_SHORT(a) || !VERIFY_SHORT(shift))
+ {
+ fprintf (stderr, "SHR16: inputs are not short: %d >> %d in %s: line %d\n", a, shift, file, line);
+ }
+ res = a>>shift;
+ if (!VERIFY_SHORT(res))
+ fprintf (stderr, "SHR16: output is not short: %d in %s: line %d\n", res, file, line);
+ spx_mips++;
+ return res;
+}
+#define SHL16(a, shift) _SHL16(a, shift, __FILE__, __LINE__)
+static inline short _SHL16(int a, int shift, char *file, int line)
+{
+ int res;
+ if (!VERIFY_SHORT(a) || !VERIFY_SHORT(shift))
+ {
+ fprintf (stderr, "SHL16: inputs are not short: %d %d in %s: line %d\n", a, shift, file, line);
+ }
+ res = a<<shift;
+ if (!VERIFY_SHORT(res))
+ fprintf (stderr, "SHL16: output is not short: %d in %s: line %d\n", res, file, line);
+ spx_mips++;
+ return res;
+}
+
+static inline int SHR32(long long a, int shift)
+{
+ long long res;
+ if (!VERIFY_INT(a) || !VERIFY_SHORT(shift))
+ {
+ fprintf (stderr, "SHR32: inputs are not int: %d %d\n", (int)a, shift);
+ }
+ res = a>>shift;
+ if (!VERIFY_INT(res))
+ {
+ fprintf (stderr, "SHR32: output is not int: %d\n", (int)res);
+ }
+ spx_mips++;
+ return res;
+}
+static inline int SHL32(long long a, int shift)
+{
+ long long res;
+ if (!VERIFY_INT(a) || !VERIFY_SHORT(shift))
+ {
+ fprintf (stderr, "SHL32: inputs are not int: %d %d\n", (int)a, shift);
+ }
+ res = a<<shift;
+ if (!VERIFY_INT(res))
+ {
+ fprintf (stderr, "SHL32: output is not int: %d\n", (int)res);
+ }
+ spx_mips++;
+ return res;
+}
+
+#define PSHR16(a,shift) (SHR16(ADD16((a),((1<<((shift))>>1))),shift))
+#define PSHR32(a,shift) (SHR32(ADD32((a),((EXTEND32(1)<<((shift))>>1))),shift))
+#define VSHR32(a, shift) (((shift)>0) ? SHR32(a, shift) : SHL32(a, -(shift)))
+
+#define SATURATE16(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x)))
+#define SATURATE32(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x)))
+
+//#define SHR(a,shift) ((a) >> (shift))
+//#define SHL(a,shift) ((a) << (shift))
+
+#define ADD16(a, b) _ADD16(a, b, __FILE__, __LINE__)
+static inline short _ADD16(int a, int b, char *file, int line)
+{
+ int res;
+ if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b))
+ {
+ fprintf (stderr, "ADD16: inputs are not short: %d %d in %s: line %d\n", a, b, file, line);
+ }
+ res = a+b;
+ if (!VERIFY_SHORT(res))
+ {
+ fprintf (stderr, "ADD16: output is not short: %d+%d=%d in %s: line %d\n", a,b,res, file, line);
+ }
+ spx_mips++;
+ return res;
+}
+
+#define SUB16(a, b) _SUB16(a, b, __FILE__, __LINE__)
+static inline short _SUB16(int a, int b, char *file, int line)
+{
+ int res;
+ if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b))
+ {
+ fprintf (stderr, "SUB16: inputs are not short: %d %d in %s: line %d\n", a, b, file, line);
+ }
+ res = a-b;
+ if (!VERIFY_SHORT(res))
+ fprintf (stderr, "SUB16: output is not short: %d in %s: line %d\n", res, file, line);
+ spx_mips++;
+ return res;
+}
+
+#define ADD32(a, b) _ADD32(a, b, __FILE__, __LINE__)
+static inline int _ADD32(long long a, long long b, char *file, int line)
+{
+ long long res;
+ if (!VERIFY_INT(a) || !VERIFY_INT(b))
+ {
+ fprintf (stderr, "ADD32: inputs are not int: %d %d in %s: line %d\n", (int)a, (int)b, file, line);
+ }
+ res = a+b;
+ if (!VERIFY_INT(res))
+ {
+ fprintf (stderr, "ADD32: output is not int: %d in %s: line %d\n", (int)res, file, line);
+ }
+ spx_mips++;
+ return res;
+}
+
+static inline int SUB32(long long a, long long b)
+{
+ long long res;
+ if (!VERIFY_INT(a) || !VERIFY_INT(b))
+ {
+ fprintf (stderr, "SUB32: inputs are not int: %d %d\n", (int)a, (int)b);
+ }
+ res = a-b;
+ if (!VERIFY_INT(res))
+ fprintf (stderr, "SUB32: output is not int: %d\n", (int)res);
+ spx_mips++;
+ return res;
+}
+
+#define ADD64(a,b) (MIPS_INC(a)+(b))
+
+/* result fits in 16 bits */
+static inline short MULT16_16_16(int a, int b)
+{
+ int res;
+ if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b))
+ {
+ fprintf (stderr, "MULT16_16_16: inputs are not short: %d %d\n", a, b);
+ }
+ res = a*b;
+ if (!VERIFY_SHORT(res))
+ fprintf (stderr, "MULT16_16_16: output is not short: %d\n", res);
+ spx_mips++;
+ return res;
+}
+
+/* result fits in 32 bits */
+static inline int MULT16_32_32(int a, long long b)
+{
+ long long res;
+ if (!VERIFY_SHORT(a) || !VERIFY_INT(b))
+ {
+ fprintf (stderr, "MULT16_32_32: inputs are not short+int: %d %d\n", a, (int)b);
+ }
+ res = a*b;
+ if (!VERIFY_INT(res))
+ fprintf (stderr, "MULT16_32_32: output is not int: %d\n", (int)res);
+ spx_mips++;
+ return res;
+}
+
+#define MULT16_16(a, b) _MULT16_16(a, b, __FILE__, __LINE__)
+static inline int _MULT16_16(int a, int b, char *file, int line)
+{
+ long long res;
+ if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b))
+ {
+ fprintf (stderr, "MULT16_16: inputs are not short: %d %d in %s: line %d\n", a, b, file, line);
+ }
+ res = ((long long)a)*b;
+ if (!VERIFY_INT(res))
+ fprintf (stderr, "MULT16_16: output is not int: %d in %s: line %d\n", (int)res, file, line);
+ spx_mips++;
+ return res;
+}
+
+#define MAC16_16(c,a,b) (spx_mips--,ADD32((c),MULT16_16((a),(b))))
+#define MAC16_16_Q11(c,a,b) (EXTRACT16(ADD16((c),EXTRACT16(SHR32(MULT16_16((a),(b)),11)))))
+#define MAC16_16_Q13(c,a,b) (EXTRACT16(ADD16((c),EXTRACT16(SHR32(MULT16_16((a),(b)),13)))))
+#define MAC16_16_P13(c,a,b) (EXTRACT16(ADD32((c),SHR32(ADD32(4096,MULT16_16((a),(b))),13))))
+
+
+#define MULT16_32_QX(a, b, Q) _MULT16_32_QX(a, b, Q, __FILE__, __LINE__)
+static inline int _MULT16_32_QX(int a, long long b, int Q, char *file, int line)
+{
+ long long res;
+ if (!VERIFY_SHORT(a) || !VERIFY_INT(b))
+ {
+ fprintf (stderr, "MULT16_32_Q%d: inputs are not short+int: %d %d in %s: line %d\n", Q, (int)a, (int)b, file, line);
+ }
+ if (ABS(b)>>(16+Q))
+ fprintf (stderr, "MULT16_32_Q%d: second operand too large: %d %d in %s: line %d\n", Q, (int)a, (int)b, file, line);
+ res = (((long long)a)*(long long)b) >> Q;
+ if (!VERIFY_INT(res))
+ fprintf (stderr, "MULT16_32_Q%d: output is not int: %d*%d=%d in %s: line %d\n", Q, (int)a, (int)b,(int)res, file, line);
+ spx_mips+=5;
+ return res;
+}
+
+static inline int MULT16_32_PX(int a, long long b, int Q)
+{
+ long long res;
+ if (!VERIFY_SHORT(a) || !VERIFY_INT(b))
+ {
+ fprintf (stderr, "MULT16_32_P%d: inputs are not short+int: %d %d\n", Q, (int)a, (int)b);
+ }
+ if (ABS(b)>>(16+Q))
+ fprintf (stderr, "MULT16_32_Q%d: second operand too large: %d %d\n", Q, (int)a, (int)b);
+ res = ((((long long)a)*(long long)b) + ((EXTEND32(1)<<Q)>>1))>> Q;
+ if (!VERIFY_INT(res))
+ fprintf (stderr, "MULT16_32_P%d: output is not int: %d*%d=%d\n", Q, (int)a, (int)b,(int)res);
+ spx_mips+=5;
+ return res;
+}
+
+
+#define MULT16_32_Q15(a,b) MULT16_32_QX(a,b,15)
+#define MULT16_32_P15(a,b) MULT16_32_PX(a,b,15)
+#define MAC16_32_Q15(c,a,b) ADD32((c),MULT16_32_Q15((a),(b)))
+
+static inline int SATURATE(int a, int b)
+{
+ if (a>b)
+ a=b;
+ if (a<-b)
+ a = -b;
+ return a;
+}
+
+static inline int MULT16_16_Q11_32(int a, int b)
+{
+ long long res;
+ if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b))
+ {
+ fprintf (stderr, "MULT16_16_Q11: inputs are not short: %d %d\n", a, b);
+ }
+ res = ((long long)a)*b;
+ res >>= 11;
+ if (!VERIFY_INT(res))
+ fprintf (stderr, "MULT16_16_Q11: output is not short: %d*%d=%d\n", (int)a, (int)b, (int)res);
+ spx_mips+=3;
+ return res;
+}
+static inline short MULT16_16_Q13(int a, int b)
+{
+ long long res;
+ if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b))
+ {
+ fprintf (stderr, "MULT16_16_Q13: inputs are not short: %d %d\n", a, b);
+ }
+ res = ((long long)a)*b;
+ res >>= 13;
+ if (!VERIFY_SHORT(res))
+ fprintf (stderr, "MULT16_16_Q13: output is not short: %d*%d=%d\n", a, b, (int)res);
+ spx_mips+=3;
+ return res;
+}
+static inline short MULT16_16_Q14(int a, int b)
+{
+ long long res;
+ if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b))
+ {
+ fprintf (stderr, "MULT16_16_Q14: inputs are not short: %d %d\n", a, b);
+ }
+ res = ((long long)a)*b;
+ res >>= 14;
+ if (!VERIFY_SHORT(res))
+ fprintf (stderr, "MULT16_16_Q14: output is not short: %d\n", (int)res);
+ spx_mips+=3;
+ return res;
+}
+static inline short MULT16_16_Q15(int a, int b)
+{
+ long long res;
+ if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b))
+ {
+ fprintf (stderr, "MULT16_16_Q15: inputs are not short: %d %d\n", a, b);
+ }
+ res = ((long long)a)*b;
+ res >>= 15;
+ if (!VERIFY_SHORT(res))
+ {
+ fprintf (stderr, "MULT16_16_Q15: output is not short: %d\n", (int)res);
+ }
+ spx_mips+=3;
+ return res;
+}
+
+static inline short MULT16_16_P13(int a, int b)
+{
+ long long res;
+ if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b))
+ {
+ fprintf (stderr, "MULT16_16_P13: inputs are not short: %d %d\n", a, b);
+ }
+ res = ((long long)a)*b;
+ res += 4096;
+ if (!VERIFY_INT(res))
+ fprintf (stderr, "MULT16_16_P13: overflow: %d*%d=%d\n", a, b, (int)res);
+ res >>= 13;
+ if (!VERIFY_SHORT(res))
+ fprintf (stderr, "MULT16_16_P13: output is not short: %d*%d=%d\n", a, b, (int)res);
+ spx_mips+=4;
+ return res;
+}
+static inline short MULT16_16_P14(int a, int b)
+{
+ long long res;
+ if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b))
+ {
+ fprintf (stderr, "MULT16_16_P14: inputs are not short: %d %d\n", a, b);
+ }
+ res = ((long long)a)*b;
+ res += 8192;
+ if (!VERIFY_INT(res))
+ fprintf (stderr, "MULT16_16_P14: overflow: %d*%d=%d\n", a, b, (int)res);
+ res >>= 14;
+ if (!VERIFY_SHORT(res))
+ fprintf (stderr, "MULT16_16_P14: output is not short: %d*%d=%d\n", a, b, (int)res);
+ spx_mips+=4;
+ return res;
+}
+static inline short MULT16_16_P15(int a, int b)
+{
+ long long res;
+ if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b))
+ {
+ fprintf (stderr, "MULT16_16_P15: inputs are not short: %d %d\n", a, b);
+ }
+ res = ((long long)a)*b;
+ res += 16384;
+ if (!VERIFY_INT(res))
+ fprintf (stderr, "MULT16_16_P15: overflow: %d*%d=%d\n", a, b, (int)res);
+ res >>= 15;
+ if (!VERIFY_SHORT(res))
+ fprintf (stderr, "MULT16_16_P15: output is not short: %d*%d=%d\n", a, b, (int)res);
+ spx_mips+=4;
+ return res;
+}
+
+#define DIV32_16(a, b) _DIV32_16(a, b, __FILE__, __LINE__)
+
+static inline int _DIV32_16(long long a, long long b, char *file, int line)
+{
+ long long res;
+ if (b==0)
+ {
+ fprintf(stderr, "DIV32_16: divide by zero: %d/%d in %s: line %d\n", (int)a, (int)b, file, line);
+ return 0;
+ }
+ if (!VERIFY_INT(a) || !VERIFY_SHORT(b))
+ {
+ fprintf (stderr, "DIV32_16: inputs are not int/short: %d %d in %s: line %d\n", (int)a, (int)b, file, line);
+ }
+ res = a/b;
+ if (!VERIFY_SHORT(res))
+ {
+ fprintf (stderr, "DIV32_16: output is not short: %d / %d = %d in %s: line %d\n", (int)a,(int)b,(int)res, file, line);
+ if (res>32767)
+ res = 32767;
+ if (res<-32768)
+ res = -32768;
+ }
+ spx_mips+=20;
+ return res;
+}
+
+#define DIV32(a, b) _DIV32(a, b, __FILE__, __LINE__)
+static inline int _DIV32(long long a, long long b, char *file, int line)
+{
+ long long res;
+ if (b==0)
+ {
+ fprintf(stderr, "DIV32: divide by zero: %d/%d in %s: line %d\n", (int)a, (int)b, file, line);
+ return 0;
+ }
+
+ if (!VERIFY_INT(a) || !VERIFY_INT(b))
+ {
+ fprintf (stderr, "DIV32: inputs are not int/short: %d %d in %s: line %d\n", (int)a, (int)b, file, line);
+ }
+ res = a/b;
+ if (!VERIFY_INT(res))
+ fprintf (stderr, "DIV32: output is not int: %d in %s: line %d\n", (int)res, file, line);
+ spx_mips+=36;
+ return res;
+}
+#define PDIV32(a,b) DIV32(ADD32((a),(b)>>1),b)
+#define PDIV32_16(a,b) DIV32_16(ADD32((a),(b)>>1),b)
+
+#endif
diff --git a/lib/speexdsp/libspeexdsp/fixed_generic.h b/lib/speexdsp/libspeexdsp/fixed_generic.h
new file mode 100644
index 00000000..09366c36
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/fixed_generic.h
@@ -0,0 +1,106 @@
+/* Copyright (C) 2003 Jean-Marc Valin */
+/**
+ @file fixed_generic.h
+ @brief Generic fixed-point operations
+*/
+/*
+ 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.
+*/
+
+#ifndef FIXED_GENERIC_H
+#define FIXED_GENERIC_H
+
+#define QCONST16(x,bits) ((spx_word16_t)(.5+(x)*(((spx_word32_t)1)<<(bits))))
+#define QCONST32(x,bits) ((spx_word32_t)(.5+(x)*(((spx_word32_t)1)<<(bits))))
+
+#define NEG16(x) (-(x))
+#define NEG32(x) (-(x))
+#define EXTRACT16(x) ((spx_word16_t)(x))
+#define EXTEND32(x) ((spx_word32_t)(x))
+#define SHR16(a,shift) ((a) >> (shift))
+#define SHL16(a,shift) ((a) << (shift))
+#define SHR32(a,shift) ((a) >> (shift))
+#define SHL32(a,shift) ((a) << (shift))
+#define PSHR16(a,shift) (SHR16((a)+((1<<((shift))>>1)),shift))
+#define PSHR32(a,shift) (SHR32((a)+((EXTEND32(1)<<((shift))>>1)),shift))
+#define VSHR32(a, shift) (((shift)>0) ? SHR32(a, shift) : SHL32(a, -(shift)))
+#define SATURATE16(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x)))
+#define SATURATE32(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x)))
+
+#define SATURATE32PSHR(x,shift,a) (((x)>=(SHL32(a,shift))) ? (a) : \
+ (x)<=-(SHL32(a,shift)) ? -(a) : \
+ (PSHR32(x, shift)))
+
+#define SHR(a,shift) ((a) >> (shift))
+#define SHL(a,shift) ((spx_word32_t)(a) << (shift))
+#define PSHR(a,shift) (SHR((a)+((EXTEND32(1)<<((shift))>>1)),shift))
+#define SATURATE(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x)))
+
+
+#define ADD16(a,b) ((spx_word16_t)((spx_word16_t)(a)+(spx_word16_t)(b)))
+#define SUB16(a,b) ((spx_word16_t)(a)-(spx_word16_t)(b))
+#define ADD32(a,b) ((spx_word32_t)(a)+(spx_word32_t)(b))
+#define SUB32(a,b) ((spx_word32_t)(a)-(spx_word32_t)(b))
+
+
+/* result fits in 16 bits */
+#define MULT16_16_16(a,b) (((spx_word16_t)(a))*((spx_word16_t)(b)))
+/* result fits in 32 bits */
+#define MULT16_32_32(a,b) (((spx_word16_t)(a))*((spx_word32_t)(b)))
+
+/* (spx_word32_t)(spx_word16_t) gives TI compiler a hint that it's 16x16->32 multiply */
+#define MULT16_16(a,b) (((spx_word32_t)(spx_word16_t)(a))*((spx_word32_t)(spx_word16_t)(b)))
+
+#define MAC16_16(c,a,b) (ADD32((c),MULT16_16((a),(b))))
+
+#define MULT16_32_P15(a,b) ADD32(MULT16_32_32(a,SHR((b),15)), PSHR(MULT16_16((a),((b)&0x00007fff)),15))
+#define MULT16_32_Q15(a,b) ADD32(MULT16_32_32(a,SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15))
+#define MAC16_32_Q15(c,a,b) ADD32(c,MULT16_32_Q15(a,b))
+
+
+#define MAC16_16_Q11(c,a,b) (ADD32((c),SHR(MULT16_16((a),(b)),11)))
+#define MAC16_16_Q13(c,a,b) (ADD32((c),SHR(MULT16_16((a),(b)),13)))
+#define MAC16_16_P13(c,a,b) (ADD32((c),SHR(ADD32(4096,MULT16_16((a),(b))),13)))
+
+#define MULT16_16_Q11_32(a,b) (SHR(MULT16_16((a),(b)),11))
+#define MULT16_16_Q13(a,b) (SHR(MULT16_16((a),(b)),13))
+#define MULT16_16_Q14(a,b) (SHR(MULT16_16((a),(b)),14))
+#define MULT16_16_Q15(a,b) (SHR(MULT16_16((a),(b)),15))
+
+#define MULT16_16_P13(a,b) (SHR(ADD32(4096,MULT16_16((a),(b))),13))
+#define MULT16_16_P14(a,b) (SHR(ADD32(8192,MULT16_16((a),(b))),14))
+#define MULT16_16_P15(a,b) (SHR(ADD32(16384,MULT16_16((a),(b))),15))
+
+#define MUL_16_32_R15(a,bh,bl) ADD32(MULT16_16((a),(bh)), SHR(MULT16_16((a),(bl)),15))
+
+#define DIV32_16(a,b) ((spx_word16_t)(((spx_word32_t)(a))/((spx_word16_t)(b))))
+#define PDIV32_16(a,b) ((spx_word16_t)(((spx_word32_t)(a)+((spx_word16_t)(b)>>1))/((spx_word16_t)(b))))
+#define DIV32(a,b) (((spx_word32_t)(a))/((spx_word32_t)(b)))
+#define PDIV32(a,b) (((spx_word32_t)(a)+((spx_word16_t)(b)>>1))/((spx_word32_t)(b)))
+
+#endif
diff --git a/lib/speexdsp/libspeexdsp/jitter.c b/lib/speexdsp/libspeexdsp/jitter.c
new file mode 100644
index 00000000..a9c0dd31
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/jitter.c
@@ -0,0 +1,839 @@
+/* Copyright (C) 2002 Jean-Marc Valin
+ File: speex_jitter.h
+
+ Adaptive jitter buffer for Speex
+
+ 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:
+- Add short-term estimate
+- Defensive programming
+ + warn when last returned < last desired (begative buffering)
+ + warn if update_delay not called between get() and tick() or is called twice in a row
+- Linked list structure for holding the packets instead of the current fixed-size array
+ + return memory to a pool
+ + allow pre-allocation of the pool
+ + optional max number of elements
+- Statistics
+ + drift
+ + loss
+ + late
+ + jitter
+ + buffering delay
+*/
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+#include "arch.h"
+#include "speex/speex_jitter.h"
+#include "os_support.h"
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#define SPEEX_JITTER_MAX_BUFFER_SIZE 200 /**< Maximum number of packets in jitter buffer */
+
+#define TSUB(a,b) ((spx_int32_t)((a)-(b)))
+
+#define GT32(a,b) (((spx_int32_t)((a)-(b)))>0)
+#define GE32(a,b) (((spx_int32_t)((a)-(b)))>=0)
+#define LT32(a,b) (((spx_int32_t)((a)-(b)))<0)
+#define LE32(a,b) (((spx_int32_t)((a)-(b)))<=0)
+
+#define ROUND_DOWN(x, step) ((x)<0 ? ((x)-(step)+1)/(step)*(step) : (x)/(step)*(step))
+
+#define MAX_TIMINGS 40
+#define MAX_BUFFERS 3
+#define TOP_DELAY 40
+
+/** Buffer that keeps the time of arrival of the latest packets */
+struct TimingBuffer {
+ int filled; /**< Number of entries occupied in "timing" and "counts"*/
+ int curr_count; /**< Number of packet timings we got (including those we discarded) */
+ spx_int32_t timing[MAX_TIMINGS]; /**< Sorted list of all timings ("latest" packets first) */
+ spx_int16_t counts[MAX_TIMINGS]; /**< Order the packets were put in (will be used for short-term estimate) */
+};
+
+static void tb_init(struct TimingBuffer *tb)
+{
+ tb->filled = 0;
+ tb->curr_count = 0;
+}
+
+/* Add the timing of a new packet to the TimingBuffer */
+static void tb_add(struct TimingBuffer *tb, spx_int16_t timing)
+{
+ int pos;
+ /* Discard packet that won't make it into the list because they're too early */
+ if (tb->filled >= MAX_TIMINGS && timing >= tb->timing[tb->filled-1])
+ {
+ tb->curr_count++;
+ return;
+ }
+
+ /* Find where the timing info goes in the sorted list */
+ pos = 0;
+ /* FIXME: Do bisection instead of linear search */
+ while (pos<tb->filled && timing >= tb->timing[pos])
+ {
+ pos++;
+ }
+
+ speex_assert(pos <= tb->filled && pos < MAX_TIMINGS);
+
+ /* Shift everything so we can perform the insertion */
+ if (pos < tb->filled)
+ {
+ int move_size = tb->filled-pos;
+ if (tb->filled == MAX_TIMINGS)
+ move_size -= 1;
+ SPEEX_MOVE(&tb->timing[pos+1], &tb->timing[pos], move_size);
+ SPEEX_MOVE(&tb->counts[pos+1], &tb->counts[pos], move_size);
+ }
+ /* Insert */
+ tb->timing[pos] = timing;
+ tb->counts[pos] = tb->curr_count;
+
+ tb->curr_count++;
+ if (tb->filled<MAX_TIMINGS)
+ tb->filled++;
+}
+
+
+
+/** Jitter buffer structure */
+struct JitterBuffer_ {
+ spx_uint32_t pointer_timestamp; /**< Timestamp of what we will *get* next */
+ spx_uint32_t last_returned_timestamp; /**< Useful for getting the next packet with the same timestamp (for fragmented media) */
+ spx_uint32_t next_stop; /**< Estimated time the next get() will be called */
+
+ spx_int32_t buffered; /**< Amount of data we think is still buffered by the application (timestamp units)*/
+
+ JitterBufferPacket packets[SPEEX_JITTER_MAX_BUFFER_SIZE]; /**< Packets stored in the buffer */
+ spx_uint32_t arrival[SPEEX_JITTER_MAX_BUFFER_SIZE]; /**< Packet arrival time (0 means it was late, even though it's a valid timestamp) */
+
+ void (*destroy) (void *); /**< Callback for destroying a packet */
+
+ spx_int32_t delay_step; /**< Size of the steps when adjusting buffering (timestamp units) */
+ spx_int32_t concealment_size; /**< Size of the packet loss concealment "units" */
+ int reset_state; /**< True if state was just reset */
+ int buffer_margin; /**< How many frames we want to keep in the buffer (lower bound) */
+ int late_cutoff; /**< How late must a packet be for it not to be considered at all */
+ int interp_requested; /**< An interpolation is requested by speex_jitter_update_delay() */
+ int auto_adjust; /**< Whether to automatically adjust the delay at any time */
+
+ struct TimingBuffer _tb[MAX_BUFFERS]; /**< Don't use those directly */
+ struct TimingBuffer *timeBuffers[MAX_BUFFERS]; /**< Storing arrival time of latest frames so we can compute some stats */
+ int window_size; /**< Total window over which the late frames are counted */
+ int subwindow_size; /**< Sub-window size for faster computation */
+ int max_late_rate; /**< Absolute maximum amount of late packets tolerable (in percent) */
+ int latency_tradeoff; /**< Latency equivalent of losing one percent of packets */
+ int auto_tradeoff; /**< Latency equivalent of losing one percent of packets (automatic default) */
+
+ int lost_count; /**< Number of consecutive lost packets */
+};
+
+/** Based on available data, this computes the optimal delay for the jitter buffer.
+ The optimised function is in timestamp units and is:
+ cost = delay + late_factor*[number of frames that would be late if we used that delay]
+ @param tb Array of buffers
+ @param late_factor Equivalent cost of a late frame (in timestamp units)
+ */
+static spx_int16_t compute_opt_delay(JitterBuffer *jitter)
+{
+ int i;
+ spx_int16_t opt=0;
+ spx_int32_t best_cost=0x7fffffff;
+ int late = 0;
+ int pos[MAX_BUFFERS];
+ int tot_count;
+ float late_factor;
+ int penalty_taken = 0;
+ int best = 0;
+ int worst = 0;
+ spx_int32_t deltaT;
+ struct TimingBuffer *tb;
+
+ tb = jitter->_tb;
+
+ /* Number of packet timings we have received (including those we didn't keep) */
+ tot_count = 0;
+ for (i=0;i<MAX_BUFFERS;i++)
+ tot_count += tb[i].curr_count;
+ if (tot_count==0)
+ return 0;
+
+ /* Compute cost for one lost packet */
+ if (jitter->latency_tradeoff != 0)
+ late_factor = jitter->latency_tradeoff * 100.0f / tot_count;
+ else
+ late_factor = jitter->auto_tradeoff * jitter->window_size/tot_count;
+
+ /*fprintf(stderr, "late_factor = %f\n", late_factor);*/
+ for (i=0;i<MAX_BUFFERS;i++)
+ pos[i] = 0;
+
+ /* Pick the TOP_DELAY "latest" packets (doesn't need to actually be late
+ for the current settings) */
+ for (i=0;i<TOP_DELAY;i++)
+ {
+ int j;
+ int next=-1;
+ int latest = 32767;
+ /* Pick latest amoung all sub-windows */
+ for (j=0;j<MAX_BUFFERS;j++)
+ {
+ if (pos[j] < tb[j].filled && tb[j].timing[pos[j]] < latest)
+ {
+ next = j;
+ latest = tb[j].timing[pos[j]];
+ }
+ }
+ if (next != -1)
+ {
+ spx_int32_t cost;
+
+ if (i==0)
+ worst = latest;
+ best = latest;
+ latest = ROUND_DOWN(latest, jitter->delay_step);
+ pos[next]++;
+
+ /* Actual cost function that tells us how bad using this delay would be */
+ cost = -latest + late_factor*late;
+ /*fprintf(stderr, "cost %d = %d + %f * %d\n", cost, -latest, late_factor, late);*/
+ if (cost < best_cost)
+ {
+ best_cost = cost;
+ opt = latest;
+ }
+ } else {
+ break;
+ }
+
+ /* For the next timing we will consider, there will be one more late packet to count */
+ late++;
+ /* Two-frame penalty if we're going to increase the amount of late frames (hysteresis) */
+ if (latest >= 0 && !penalty_taken)
+ {
+ penalty_taken = 1;
+ late+=4;
+ }
+ }
+
+ deltaT = best-worst;
+ /* This is a default "automatic latency tradeoff" when none is provided */
+ jitter->auto_tradeoff = 1 + deltaT/TOP_DELAY;
+ /*fprintf(stderr, "auto_tradeoff = %d (%d %d %d)\n", jitter->auto_tradeoff, best, worst, i);*/
+
+ /* FIXME: Compute a short-term estimate too and combine with the long-term one */
+
+ /* Prevents reducing the buffer size when we haven't really had much data */
+ if (tot_count < TOP_DELAY && opt > 0)
+ return 0;
+ return opt;
+}
+
+
+/** Initialise jitter buffer */
+EXPORT JitterBuffer *jitter_buffer_init(int step_size)
+{
+ JitterBuffer *jitter = (JitterBuffer*)speex_alloc(sizeof(JitterBuffer));
+ if (jitter)
+ {
+ int i;
+ spx_int32_t tmp;
+ for (i=0;i<SPEEX_JITTER_MAX_BUFFER_SIZE;i++)
+ jitter->packets[i].data=NULL;
+ jitter->delay_step = step_size;
+ jitter->concealment_size = step_size;
+ /*FIXME: Should this be 0 or 1?*/
+ jitter->buffer_margin = 0;
+ jitter->late_cutoff = 50;
+ jitter->destroy = NULL;
+ jitter->latency_tradeoff = 0;
+ jitter->auto_adjust = 1;
+ tmp = 4;
+ jitter_buffer_ctl(jitter, JITTER_BUFFER_SET_MAX_LATE_RATE, &tmp);
+ jitter_buffer_reset(jitter);
+ }
+ return jitter;
+}
+
+/** Reset jitter buffer */
+EXPORT void jitter_buffer_reset(JitterBuffer *jitter)
+{
+ int i;
+ for (i=0;i<SPEEX_JITTER_MAX_BUFFER_SIZE;i++)
+ {
+ if (jitter->packets[i].data)
+ {
+ if (jitter->destroy)
+ jitter->destroy(jitter->packets[i].data);
+ else
+ speex_free(jitter->packets[i].data);
+ jitter->packets[i].data = NULL;
+ }
+ }
+ /* Timestamp is actually undefined at this point */
+ jitter->pointer_timestamp = 0;
+ jitter->next_stop = 0;
+ jitter->reset_state = 1;
+ jitter->lost_count = 0;
+ jitter->buffered = 0;
+ jitter->auto_tradeoff = 32000;
+
+ for (i=0;i<MAX_BUFFERS;i++)
+ {
+ tb_init(&jitter->_tb[i]);
+ jitter->timeBuffers[i] = &jitter->_tb[i];
+ }
+ /*fprintf (stderr, "reset\n");*/
+}
+
+/** Destroy jitter buffer */
+EXPORT void jitter_buffer_destroy(JitterBuffer *jitter)
+{
+ jitter_buffer_reset(jitter);
+ speex_free(jitter);
+}
+
+/** Take the following timing into consideration for future calculations */
+static void update_timings(JitterBuffer *jitter, spx_int32_t timing)
+{
+ if (timing < -32767)
+ timing = -32767;
+ if (timing > 32767)
+ timing = 32767;
+ /* If the current sub-window is full, perform a rotation and discard oldest sub-widow */
+ if (jitter->timeBuffers[0]->curr_count >= jitter->subwindow_size)
+ {
+ int i;
+ /*fprintf(stderr, "Rotate buffer\n");*/
+ struct TimingBuffer *tmp = jitter->timeBuffers[MAX_BUFFERS-1];
+ for (i=MAX_BUFFERS-1;i>=1;i--)
+ jitter->timeBuffers[i] = jitter->timeBuffers[i-1];
+ jitter->timeBuffers[0] = tmp;
+ tb_init(jitter->timeBuffers[0]);
+ }
+ tb_add(jitter->timeBuffers[0], timing);
+}
+
+/** Compensate all timings when we do an adjustment of the buffering */
+static void shift_timings(JitterBuffer *jitter, spx_int16_t amount)
+{
+ int i, j;
+ for (i=0;i<MAX_BUFFERS;i++)
+ {
+ for (j=0;j<jitter->timeBuffers[i]->filled;j++)
+ jitter->timeBuffers[i]->timing[j] += amount;
+ }
+}
+
+
+/** Put one packet into the jitter buffer */
+EXPORT void jitter_buffer_put(JitterBuffer *jitter, const JitterBufferPacket *packet)
+{
+ int i,j;
+ int late;
+ /*fprintf (stderr, "put packet %d %d\n", timestamp, span);*/
+
+ /* Cleanup buffer (remove old packets that weren't played) */
+ if (!jitter->reset_state)
+ {
+ for (i=0;i<SPEEX_JITTER_MAX_BUFFER_SIZE;i++)
+ {
+ /* Make sure we don't discard a "just-late" packet in case we want to play it next (if we interpolate). */
+ if (jitter->packets[i].data && LE32(jitter->packets[i].timestamp + jitter->packets[i].span, jitter->pointer_timestamp))
+ {
+ /*fprintf (stderr, "cleaned (not played)\n");*/
+ if (jitter->destroy)
+ jitter->destroy(jitter->packets[i].data);
+ else
+ speex_free(jitter->packets[i].data);
+ jitter->packets[i].data = NULL;
+ }
+ }
+ }
+
+ /*fprintf(stderr, "arrival: %d %d %d\n", packet->timestamp, jitter->next_stop, jitter->pointer_timestamp);*/
+ /* Check if packet is late (could still be useful though) */
+ if (!jitter->reset_state && LT32(packet->timestamp, jitter->next_stop))
+ {
+ update_timings(jitter, ((spx_int32_t)packet->timestamp) - ((spx_int32_t)jitter->next_stop) - jitter->buffer_margin);
+ late = 1;
+ } else {
+ late = 0;
+ }
+
+ /* For some reason, the consumer has failed the last 20 fetches. Make sure this packet is
+ * used to resync. */
+ if (jitter->lost_count>20)
+ {
+ jitter_buffer_reset(jitter);
+ }
+
+ /* Only insert the packet if it's not hopelessly late (i.e. totally useless) */
+ if (jitter->reset_state || GE32(packet->timestamp+packet->span+jitter->delay_step, jitter->pointer_timestamp))
+ {
+
+ /*Find an empty slot in the buffer*/
+ for (i=0;i<SPEEX_JITTER_MAX_BUFFER_SIZE;i++)
+ {
+ if (jitter->packets[i].data==NULL)
+ break;
+ }
+
+ /*No place left in the buffer, need to make room for it by discarding the oldest packet */
+ if (i==SPEEX_JITTER_MAX_BUFFER_SIZE)
+ {
+ int earliest=jitter->packets[0].timestamp;
+ i=0;
+ for (j=1;j<SPEEX_JITTER_MAX_BUFFER_SIZE;j++)
+ {
+ if (!jitter->packets[i].data || LT32(jitter->packets[j].timestamp,earliest))
+ {
+ earliest = jitter->packets[j].timestamp;
+ i=j;
+ }
+ }
+ if (jitter->destroy)
+ jitter->destroy(jitter->packets[i].data);
+ else
+ speex_free(jitter->packets[i].data);
+ jitter->packets[i].data=NULL;
+ /*fprintf (stderr, "Buffer is full, discarding earliest frame %d (currently at %d)\n", timestamp, jitter->pointer_timestamp);*/
+ }
+
+ /* Copy packet in buffer */
+ if (jitter->destroy)
+ {
+ jitter->packets[i].data = packet->data;
+ } else {
+ jitter->packets[i].data=(char*)speex_alloc(packet->len);
+ for (j=0;j<packet->len;j++)
+ jitter->packets[i].data[j]=packet->data[j];
+ }
+ jitter->packets[i].timestamp=packet->timestamp;
+ jitter->packets[i].span=packet->span;
+ jitter->packets[i].len=packet->len;
+ jitter->packets[i].sequence=packet->sequence;
+ jitter->packets[i].user_data=packet->user_data;
+ if (jitter->reset_state || late)
+ jitter->arrival[i] = 0;
+ else
+ jitter->arrival[i] = jitter->next_stop;
+ }
+
+
+}
+
+/** Get one packet from the jitter buffer */
+EXPORT int jitter_buffer_get(JitterBuffer *jitter, JitterBufferPacket *packet, spx_int32_t desired_span, spx_int32_t *start_offset)
+{
+ int i;
+ unsigned int j;
+ spx_int16_t opt;
+
+ if (start_offset != NULL)
+ *start_offset = 0;
+
+ /* Syncing on the first call */
+ if (jitter->reset_state)
+ {
+ int found = 0;
+ /* Find the oldest packet */
+ spx_uint32_t oldest=0;
+ for (i=0;i<SPEEX_JITTER_MAX_BUFFER_SIZE;i++)
+ {
+ if (jitter->packets[i].data && (!found || LT32(jitter->packets[i].timestamp,oldest)))
+ {
+ oldest = jitter->packets[i].timestamp;
+ found = 1;
+ }
+ }
+ if (found)
+ {
+ jitter->reset_state=0;
+ jitter->pointer_timestamp = oldest;
+ jitter->next_stop = oldest;
+ } else {
+ packet->timestamp = 0;
+ packet->span = jitter->interp_requested;
+ return JITTER_BUFFER_MISSING;
+ }
+ }
+
+
+ jitter->last_returned_timestamp = jitter->pointer_timestamp;
+
+ if (jitter->interp_requested != 0)
+ {
+ packet->timestamp = jitter->pointer_timestamp;
+ packet->span = jitter->interp_requested;
+
+ /* Increment the pointer because it got decremented in the delay update */
+ jitter->pointer_timestamp += jitter->interp_requested;
+ packet->len = 0;
+ /*fprintf (stderr, "Deferred interpolate\n");*/
+
+ jitter->interp_requested = 0;
+
+ jitter->buffered = packet->span - desired_span;
+
+ return JITTER_BUFFER_INSERTION;
+ }
+
+ /* Searching for the packet that fits best */
+
+ /* Search the buffer for a packet with the right timestamp and spanning the whole current chunk */
+ for (i=0;i<SPEEX_JITTER_MAX_BUFFER_SIZE;i++)
+ {
+ if (jitter->packets[i].data && jitter->packets[i].timestamp==jitter->pointer_timestamp && GE32(jitter->packets[i].timestamp+jitter->packets[i].span,jitter->pointer_timestamp+desired_span))
+ break;
+ }
+
+ /* If no match, try for an "older" packet that still spans (fully) the current chunk */
+ if (i==SPEEX_JITTER_MAX_BUFFER_SIZE)
+ {
+ for (i=0;i<SPEEX_JITTER_MAX_BUFFER_SIZE;i++)
+ {
+ if (jitter->packets[i].data && LE32(jitter->packets[i].timestamp, jitter->pointer_timestamp) && GE32(jitter->packets[i].timestamp+jitter->packets[i].span,jitter->pointer_timestamp+desired_span))
+ break;
+ }
+ }
+
+ /* If still no match, try for an "older" packet that spans part of the current chunk */
+ if (i==SPEEX_JITTER_MAX_BUFFER_SIZE)
+ {
+ for (i=0;i<SPEEX_JITTER_MAX_BUFFER_SIZE;i++)
+ {
+ if (jitter->packets[i].data && LE32(jitter->packets[i].timestamp, jitter->pointer_timestamp) && GT32(jitter->packets[i].timestamp+jitter->packets[i].span,jitter->pointer_timestamp))
+ break;
+ }
+ }
+
+ /* If still no match, try for earliest packet possible */
+ if (i==SPEEX_JITTER_MAX_BUFFER_SIZE)
+ {
+ int found = 0;
+ spx_uint32_t best_time=0;
+ int best_span=0;
+ int besti=0;
+ for (i=0;i<SPEEX_JITTER_MAX_BUFFER_SIZE;i++)
+ {
+ /* check if packet starts within current chunk */
+ if (jitter->packets[i].data && LT32(jitter->packets[i].timestamp,jitter->pointer_timestamp+desired_span) && GE32(jitter->packets[i].timestamp,jitter->pointer_timestamp))
+ {
+ if (!found || LT32(jitter->packets[i].timestamp,best_time) || (jitter->packets[i].timestamp==best_time && GT32(jitter->packets[i].span,best_span)))
+ {
+ best_time = jitter->packets[i].timestamp;
+ best_span = jitter->packets[i].span;
+ besti = i;
+ found = 1;
+ }
+ }
+ }
+ if (found)
+ {
+ i=besti;
+ /*fprintf (stderr, "incomplete: %d %d %d %d\n", jitter->packets[i].timestamp, jitter->pointer_timestamp, chunk_size, jitter->packets[i].span);*/
+ }
+ }
+
+ /* If we find something */
+ if (i!=SPEEX_JITTER_MAX_BUFFER_SIZE)
+ {
+ spx_int32_t offset;
+
+ /* We (obviously) haven't lost this packet */
+ jitter->lost_count = 0;
+
+ /* In this case, 0 isn't as a valid timestamp */
+ if (jitter->arrival[i] != 0)
+ {
+ update_timings(jitter, ((spx_int32_t)jitter->packets[i].timestamp) - ((spx_int32_t)jitter->arrival[i]) - jitter->buffer_margin);
+ }
+
+
+ /* Copy packet */
+ if (jitter->destroy)
+ {
+ packet->data = jitter->packets[i].data;
+ packet->len = jitter->packets[i].len;
+ } else {
+ if (jitter->packets[i].len > packet->len)
+ {
+ speex_warning_int("jitter_buffer_get(): packet too large to fit. Size is", jitter->packets[i].len);
+ } else {
+ packet->len = jitter->packets[i].len;
+ }
+ for (j=0;j<packet->len;j++)
+ packet->data[j] = jitter->packets[i].data[j];
+ /* Remove packet */
+ speex_free(jitter->packets[i].data);
+ }
+ jitter->packets[i].data = NULL;
+ /* Set timestamp and span (if requested) */
+ offset = (spx_int32_t)jitter->packets[i].timestamp-(spx_int32_t)jitter->pointer_timestamp;
+ if (start_offset != NULL)
+ *start_offset = offset;
+ else if (offset != 0)
+ speex_warning_int("jitter_buffer_get() discarding non-zero start_offset", offset);
+
+ packet->timestamp = jitter->packets[i].timestamp;
+ jitter->last_returned_timestamp = packet->timestamp;
+
+ packet->span = jitter->packets[i].span;
+ packet->sequence = jitter->packets[i].sequence;
+ packet->user_data = jitter->packets[i].user_data;
+ /* Point to the end of the current packet */
+ jitter->pointer_timestamp = jitter->packets[i].timestamp+jitter->packets[i].span;
+
+ jitter->buffered = packet->span - desired_span;
+
+ if (start_offset != NULL)
+ jitter->buffered += *start_offset;
+
+ return JITTER_BUFFER_OK;
+ }
+
+
+ /* If we haven't found anything worth returning */
+
+ /*fprintf (stderr, "not found\n");*/
+ jitter->lost_count++;
+ /*fprintf (stderr, "m");*/
+ /*fprintf (stderr, "lost_count = %d\n", jitter->lost_count);*/
+
+ opt = compute_opt_delay(jitter);
+
+ /* Should we force an increase in the buffer or just do normal interpolation? */
+ if (opt < 0)
+ {
+ /* Need to increase buffering */
+
+ /* Shift histogram to compensate */
+ shift_timings(jitter, -opt);
+
+ packet->timestamp = jitter->pointer_timestamp;
+ packet->span = -opt;
+ /* Don't move the pointer_timestamp forward */
+ packet->len = 0;
+
+ jitter->buffered = packet->span - desired_span;
+ return JITTER_BUFFER_INSERTION;
+ /*jitter->pointer_timestamp -= jitter->delay_step;*/
+ /*fprintf (stderr, "Forced to interpolate\n");*/
+ } else {
+ /* Normal packet loss */
+ packet->timestamp = jitter->pointer_timestamp;
+
+ desired_span = ROUND_DOWN(desired_span, jitter->concealment_size);
+ packet->span = desired_span;
+ jitter->pointer_timestamp += desired_span;
+ packet->len = 0;
+
+ jitter->buffered = packet->span - desired_span;
+ return JITTER_BUFFER_MISSING;
+ /*fprintf (stderr, "Normal loss\n");*/
+ }
+
+
+}
+
+EXPORT int jitter_buffer_get_another(JitterBuffer *jitter, JitterBufferPacket *packet)
+{
+ int i, j;
+ for (i=0;i<SPEEX_JITTER_MAX_BUFFER_SIZE;i++)
+ {
+ if (jitter->packets[i].data && jitter->packets[i].timestamp==jitter->last_returned_timestamp)
+ break;
+ }
+ if (i!=SPEEX_JITTER_MAX_BUFFER_SIZE)
+ {
+ /* Copy packet */
+ packet->len = jitter->packets[i].len;
+ if (jitter->destroy)
+ {
+ packet->data = jitter->packets[i].data;
+ } else {
+ for (j=0;j<packet->len;j++)
+ packet->data[j] = jitter->packets[i].data[j];
+ /* Remove packet */
+ speex_free(jitter->packets[i].data);
+ }
+ jitter->packets[i].data = NULL;
+ packet->timestamp = jitter->packets[i].timestamp;
+ packet->span = jitter->packets[i].span;
+ packet->sequence = jitter->packets[i].sequence;
+ packet->user_data = jitter->packets[i].user_data;
+ return JITTER_BUFFER_OK;
+ } else {
+ packet->data = NULL;
+ packet->len = 0;
+ packet->span = 0;
+ return JITTER_BUFFER_MISSING;
+ }
+}
+
+/* Let the jitter buffer know it's the right time to adjust the buffering delay to the network conditions */
+static int _jitter_buffer_update_delay(JitterBuffer *jitter, JitterBufferPacket *packet, spx_int32_t *start_offset)
+{
+ spx_int16_t opt = compute_opt_delay(jitter);
+ /*fprintf(stderr, "opt adjustment is %d ", opt);*/
+
+ if (opt < 0)
+ {
+ shift_timings(jitter, -opt);
+
+ jitter->pointer_timestamp += opt;
+ jitter->interp_requested = -opt;
+ /*fprintf (stderr, "Decision to interpolate %d samples\n", -opt);*/
+ } else if (opt > 0)
+ {
+ shift_timings(jitter, -opt);
+ jitter->pointer_timestamp += opt;
+ /*fprintf (stderr, "Decision to drop %d samples\n", opt);*/
+ }
+
+ return opt;
+}
+
+/* Let the jitter buffer know it's the right time to adjust the buffering delay to the network conditions */
+EXPORT int jitter_buffer_update_delay(JitterBuffer *jitter, JitterBufferPacket *packet, spx_int32_t *start_offset)
+{
+ /* If the programmer calls jitter_buffer_update_delay() directly,
+ automatically disable auto-adjustment */
+ jitter->auto_adjust = 0;
+
+ return _jitter_buffer_update_delay(jitter, packet, start_offset);
+}
+
+/** Get pointer timestamp of jitter buffer */
+EXPORT int jitter_buffer_get_pointer_timestamp(JitterBuffer *jitter)
+{
+ return jitter->pointer_timestamp;
+}
+
+EXPORT void jitter_buffer_tick(JitterBuffer *jitter)
+{
+ /* Automatically-adjust the buffering delay if requested */
+ if (jitter->auto_adjust)
+ _jitter_buffer_update_delay(jitter, NULL, NULL);
+
+ if (jitter->buffered >= 0)
+ {
+ jitter->next_stop = jitter->pointer_timestamp - jitter->buffered;
+ } else {
+ jitter->next_stop = jitter->pointer_timestamp;
+ speex_warning_int("jitter buffer sees negative buffering, your code might be broken. Value is ", jitter->buffered);
+ }
+ jitter->buffered = 0;
+}
+
+EXPORT void jitter_buffer_remaining_span(JitterBuffer *jitter, spx_uint32_t rem)
+{
+ /* Automatically-adjust the buffering delay if requested */
+ if (jitter->auto_adjust)
+ _jitter_buffer_update_delay(jitter, NULL, NULL);
+
+ if (jitter->buffered < 0)
+ speex_warning_int("jitter buffer sees negative buffering, your code might be broken. Value is ", jitter->buffered);
+ jitter->next_stop = jitter->pointer_timestamp - rem;
+}
+
+
+/* Used like the ioctl function to control the jitter buffer parameters */
+EXPORT int jitter_buffer_ctl(JitterBuffer *jitter, int request, void *ptr)
+{
+ int count, i;
+ switch(request)
+ {
+ case JITTER_BUFFER_SET_MARGIN:
+ jitter->buffer_margin = *(spx_int32_t*)ptr;
+ break;
+ case JITTER_BUFFER_GET_MARGIN:
+ *(spx_int32_t*)ptr = jitter->buffer_margin;
+ break;
+ case JITTER_BUFFER_GET_AVALIABLE_COUNT:
+ count = 0;
+ for (i=0;i<SPEEX_JITTER_MAX_BUFFER_SIZE;i++)
+ {
+ if (jitter->packets[i].data && LE32(jitter->pointer_timestamp, jitter->packets[i].timestamp))
+ {
+ count++;
+ }
+ }
+ *(spx_int32_t*)ptr = count;
+ break;
+ case JITTER_BUFFER_SET_DESTROY_CALLBACK:
+ jitter->destroy = (void (*) (void *))ptr;
+ break;
+ case JITTER_BUFFER_GET_DESTROY_CALLBACK:
+ *(void (**) (void *))ptr = jitter->destroy;
+ break;
+ case JITTER_BUFFER_SET_DELAY_STEP:
+ jitter->delay_step = *(spx_int32_t*)ptr;
+ break;
+ case JITTER_BUFFER_GET_DELAY_STEP:
+ *(spx_int32_t*)ptr = jitter->delay_step;
+ break;
+ case JITTER_BUFFER_SET_CONCEALMENT_SIZE:
+ jitter->concealment_size = *(spx_int32_t*)ptr;
+ break;
+ case JITTER_BUFFER_GET_CONCEALMENT_SIZE:
+ *(spx_int32_t*)ptr = jitter->concealment_size;
+ break;
+ case JITTER_BUFFER_SET_MAX_LATE_RATE:
+ jitter->max_late_rate = *(spx_int32_t*)ptr;
+ jitter->window_size = 100*TOP_DELAY/jitter->max_late_rate;
+ jitter->subwindow_size = jitter->window_size/MAX_BUFFERS;
+ break;
+ case JITTER_BUFFER_GET_MAX_LATE_RATE:
+ *(spx_int32_t*)ptr = jitter->max_late_rate;
+ break;
+ case JITTER_BUFFER_SET_LATE_COST:
+ jitter->latency_tradeoff = *(spx_int32_t*)ptr;
+ break;
+ case JITTER_BUFFER_GET_LATE_COST:
+ *(spx_int32_t*)ptr = jitter->latency_tradeoff;
+ break;
+ default:
+ speex_warning_int("Unknown jitter_buffer_ctl request: ", request);
+ return -1;
+ }
+ return 0;
+}
+
diff --git a/lib/speexdsp/libspeexdsp/kiss_fft.c b/lib/speexdsp/libspeexdsp/kiss_fft.c
new file mode 100644
index 00000000..285e5579
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/kiss_fft.c
@@ -0,0 +1,523 @@
+/*
+Copyright (c) 2003-2004, Mark Borgerding
+Copyright (c) 2005-2007, Jean-Marc Valin
+
+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.
+*/
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "_kiss_fft_guts.h"
+#include "arch.h"
+#include "os_support.h"
+
+/* The guts header contains all the multiplication and addition macros that are defined for
+ fixed or floating point complex numbers. It also delares the kf_ internal functions.
+ */
+
+static void kf_bfly2(
+ kiss_fft_cpx * Fout,
+ const size_t fstride,
+ const kiss_fft_cfg st,
+ int m,
+ int N,
+ int mm
+ )
+{
+ kiss_fft_cpx * Fout2;
+ kiss_fft_cpx * tw1;
+ kiss_fft_cpx t;
+ if (!st->inverse) {
+ int i,j;
+ kiss_fft_cpx * Fout_beg = Fout;
+ for (i=0;i<N;i++)
+ {
+ Fout = Fout_beg + i*mm;
+ Fout2 = Fout + m;
+ tw1 = st->twiddles;
+ for(j=0;j<m;j++)
+ {
+ /* Almost the same as the code path below, except that we divide the input by two
+ (while keeping the best accuracy possible) */
+ spx_word32_t tr, ti;
+ tr = SHR32(SUB32(MULT16_16(Fout2->r , tw1->r),MULT16_16(Fout2->i , tw1->i)), 1);
+ ti = SHR32(ADD32(MULT16_16(Fout2->i , tw1->r),MULT16_16(Fout2->r , tw1->i)), 1);
+ tw1 += fstride;
+ Fout2->r = PSHR32(SUB32(SHL32(EXTEND32(Fout->r), 14), tr), 15);
+ Fout2->i = PSHR32(SUB32(SHL32(EXTEND32(Fout->i), 14), ti), 15);
+ Fout->r = PSHR32(ADD32(SHL32(EXTEND32(Fout->r), 14), tr), 15);
+ Fout->i = PSHR32(ADD32(SHL32(EXTEND32(Fout->i), 14), ti), 15);
+ ++Fout2;
+ ++Fout;
+ }
+ }
+ } else {
+ int i,j;
+ kiss_fft_cpx * Fout_beg = Fout;
+ for (i=0;i<N;i++)
+ {
+ Fout = Fout_beg + i*mm;
+ Fout2 = Fout + m;
+ tw1 = st->twiddles;
+ for(j=0;j<m;j++)
+ {
+ C_MUL (t, *Fout2 , *tw1);
+ tw1 += fstride;
+ C_SUB( *Fout2 , *Fout , t );
+ C_ADDTO( *Fout , t );
+ ++Fout2;
+ ++Fout;
+ }
+ }
+ }
+}
+
+static void kf_bfly4(
+ kiss_fft_cpx * Fout,
+ const size_t fstride,
+ const kiss_fft_cfg st,
+ int m,
+ int N,
+ int mm
+ )
+{
+ kiss_fft_cpx *tw1,*tw2,*tw3;
+ kiss_fft_cpx scratch[6];
+ const size_t m2=2*m;
+ const size_t m3=3*m;
+ int i, j;
+
+ if (st->inverse)
+ {
+ kiss_fft_cpx * Fout_beg = Fout;
+ for (i=0;i<N;i++)
+ {
+ Fout = Fout_beg + i*mm;
+ tw3 = tw2 = tw1 = st->twiddles;
+ for (j=0;j<m;j++)
+ {
+ C_MUL(scratch[0],Fout[m] , *tw1 );
+ C_MUL(scratch[1],Fout[m2] , *tw2 );
+ C_MUL(scratch[2],Fout[m3] , *tw3 );
+
+ C_SUB( scratch[5] , *Fout, scratch[1] );
+ C_ADDTO(*Fout, scratch[1]);
+ C_ADD( scratch[3] , scratch[0] , scratch[2] );
+ C_SUB( scratch[4] , scratch[0] , scratch[2] );
+ C_SUB( Fout[m2], *Fout, scratch[3] );
+ tw1 += fstride;
+ tw2 += fstride*2;
+ tw3 += fstride*3;
+ C_ADDTO( *Fout , scratch[3] );
+
+ Fout[m].r = scratch[5].r - scratch[4].i;
+ Fout[m].i = scratch[5].i + scratch[4].r;
+ Fout[m3].r = scratch[5].r + scratch[4].i;
+ Fout[m3].i = scratch[5].i - scratch[4].r;
+ ++Fout;
+ }
+ }
+ } else
+ {
+ kiss_fft_cpx * Fout_beg = Fout;
+ for (i=0;i<N;i++)
+ {
+ Fout = Fout_beg + i*mm;
+ tw3 = tw2 = tw1 = st->twiddles;
+ for (j=0;j<m;j++)
+ {
+ C_MUL4(scratch[0],Fout[m] , *tw1 );
+ C_MUL4(scratch[1],Fout[m2] , *tw2 );
+ C_MUL4(scratch[2],Fout[m3] , *tw3 );
+
+ Fout->r = PSHR16(Fout->r, 2);
+ Fout->i = PSHR16(Fout->i, 2);
+ C_SUB( scratch[5] , *Fout, scratch[1] );
+ C_ADDTO(*Fout, scratch[1]);
+ C_ADD( scratch[3] , scratch[0] , scratch[2] );
+ C_SUB( scratch[4] , scratch[0] , scratch[2] );
+ Fout[m2].r = PSHR16(Fout[m2].r, 2);
+ Fout[m2].i = PSHR16(Fout[m2].i, 2);
+ C_SUB( Fout[m2], *Fout, scratch[3] );
+ tw1 += fstride;
+ tw2 += fstride*2;
+ tw3 += fstride*3;
+ C_ADDTO( *Fout , scratch[3] );
+
+ Fout[m].r = scratch[5].r + scratch[4].i;
+ Fout[m].i = scratch[5].i - scratch[4].r;
+ Fout[m3].r = scratch[5].r - scratch[4].i;
+ Fout[m3].i = scratch[5].i + scratch[4].r;
+ ++Fout;
+ }
+ }
+ }
+}
+
+static void kf_bfly3(
+ kiss_fft_cpx * Fout,
+ const size_t fstride,
+ const kiss_fft_cfg st,
+ size_t m
+ )
+{
+ size_t k=m;
+ const size_t m2 = 2*m;
+ kiss_fft_cpx *tw1,*tw2;
+ kiss_fft_cpx scratch[5];
+ kiss_fft_cpx epi3;
+ epi3 = st->twiddles[fstride*m];
+
+ tw1=tw2=st->twiddles;
+
+ do{
+ if (!st->inverse) {
+ C_FIXDIV(*Fout,3); C_FIXDIV(Fout[m],3); C_FIXDIV(Fout[m2],3);
+ }
+
+ C_MUL(scratch[1],Fout[m] , *tw1);
+ C_MUL(scratch[2],Fout[m2] , *tw2);
+
+ C_ADD(scratch[3],scratch[1],scratch[2]);
+ C_SUB(scratch[0],scratch[1],scratch[2]);
+ tw1 += fstride;
+ tw2 += fstride*2;
+
+ Fout[m].r = Fout->r - HALF_OF(scratch[3].r);
+ Fout[m].i = Fout->i - HALF_OF(scratch[3].i);
+
+ C_MULBYSCALAR( scratch[0] , epi3.i );
+
+ C_ADDTO(*Fout,scratch[3]);
+
+ Fout[m2].r = Fout[m].r + scratch[0].i;
+ Fout[m2].i = Fout[m].i - scratch[0].r;
+
+ Fout[m].r -= scratch[0].i;
+ Fout[m].i += scratch[0].r;
+
+ ++Fout;
+ }while(--k);
+}
+
+static void kf_bfly5(
+ kiss_fft_cpx * Fout,
+ const size_t fstride,
+ const kiss_fft_cfg st,
+ int m
+ )
+{
+ kiss_fft_cpx *Fout0,*Fout1,*Fout2,*Fout3,*Fout4;
+ int u;
+ kiss_fft_cpx scratch[13];
+ kiss_fft_cpx * twiddles = st->twiddles;
+ kiss_fft_cpx *tw;
+ kiss_fft_cpx ya,yb;
+ ya = twiddles[fstride*m];
+ yb = twiddles[fstride*2*m];
+
+ Fout0=Fout;
+ Fout1=Fout0+m;
+ Fout2=Fout0+2*m;
+ Fout3=Fout0+3*m;
+ Fout4=Fout0+4*m;
+
+ tw=st->twiddles;
+ for ( u=0; u<m; ++u ) {
+ if (!st->inverse) {
+ C_FIXDIV( *Fout0,5); C_FIXDIV( *Fout1,5); C_FIXDIV( *Fout2,5); C_FIXDIV( *Fout3,5); C_FIXDIV( *Fout4,5);
+ }
+ scratch[0] = *Fout0;
+
+ C_MUL(scratch[1] ,*Fout1, tw[u*fstride]);
+ C_MUL(scratch[2] ,*Fout2, tw[2*u*fstride]);
+ C_MUL(scratch[3] ,*Fout3, tw[3*u*fstride]);
+ C_MUL(scratch[4] ,*Fout4, tw[4*u*fstride]);
+
+ C_ADD( scratch[7],scratch[1],scratch[4]);
+ C_SUB( scratch[10],scratch[1],scratch[4]);
+ C_ADD( scratch[8],scratch[2],scratch[3]);
+ C_SUB( scratch[9],scratch[2],scratch[3]);
+
+ Fout0->r += scratch[7].r + scratch[8].r;
+ Fout0->i += scratch[7].i + scratch[8].i;
+
+ scratch[5].r = scratch[0].r + S_MUL(scratch[7].r,ya.r) + S_MUL(scratch[8].r,yb.r);
+ scratch[5].i = scratch[0].i + S_MUL(scratch[7].i,ya.r) + S_MUL(scratch[8].i,yb.r);
+
+ scratch[6].r = S_MUL(scratch[10].i,ya.i) + S_MUL(scratch[9].i,yb.i);
+ scratch[6].i = -S_MUL(scratch[10].r,ya.i) - S_MUL(scratch[9].r,yb.i);
+
+ C_SUB(*Fout1,scratch[5],scratch[6]);
+ C_ADD(*Fout4,scratch[5],scratch[6]);
+
+ scratch[11].r = scratch[0].r + S_MUL(scratch[7].r,yb.r) + S_MUL(scratch[8].r,ya.r);
+ scratch[11].i = scratch[0].i + S_MUL(scratch[7].i,yb.r) + S_MUL(scratch[8].i,ya.r);
+ scratch[12].r = - S_MUL(scratch[10].i,yb.i) + S_MUL(scratch[9].i,ya.i);
+ scratch[12].i = S_MUL(scratch[10].r,yb.i) - S_MUL(scratch[9].r,ya.i);
+
+ C_ADD(*Fout2,scratch[11],scratch[12]);
+ C_SUB(*Fout3,scratch[11],scratch[12]);
+
+ ++Fout0;++Fout1;++Fout2;++Fout3;++Fout4;
+ }
+}
+
+/* perform the butterfly for one stage of a mixed radix FFT */
+static void kf_bfly_generic(
+ kiss_fft_cpx * Fout,
+ const size_t fstride,
+ const kiss_fft_cfg st,
+ int m,
+ int p
+ )
+{
+ int u,k,q1,q;
+ kiss_fft_cpx * twiddles = st->twiddles;
+ kiss_fft_cpx t;
+ kiss_fft_cpx scratchbuf[17];
+ int Norig = st->nfft;
+
+ /*CHECKBUF(scratchbuf,nscratchbuf,p);*/
+ if (p>17)
+ speex_fatal("KissFFT: max radix supported is 17");
+
+ for ( u=0; u<m; ++u ) {
+ k=u;
+ for ( q1=0 ; q1<p ; ++q1 ) {
+ scratchbuf[q1] = Fout[ k ];
+ if (!st->inverse) {
+ C_FIXDIV(scratchbuf[q1],p);
+ }
+ k += m;
+ }
+
+ k=u;
+ for ( q1=0 ; q1<p ; ++q1 ) {
+ int twidx=0;
+ Fout[ k ] = scratchbuf[0];
+ for (q=1;q<p;++q ) {
+ twidx += fstride * k;
+ if (twidx>=Norig) twidx-=Norig;
+ C_MUL(t,scratchbuf[q] , twiddles[twidx] );
+ C_ADDTO( Fout[ k ] ,t);
+ }
+ k += m;
+ }
+ }
+}
+
+static
+void kf_shuffle(
+ kiss_fft_cpx * Fout,
+ const kiss_fft_cpx * f,
+ const size_t fstride,
+ int in_stride,
+ int * factors,
+ const kiss_fft_cfg st
+ )
+{
+ const int p=*factors++; /* the radix */
+ const int m=*factors++; /* stage's fft length/p */
+
+ /*printf ("fft %d %d %d %d %d %d\n", p*m, m, p, s2, fstride*in_stride, N);*/
+ if (m==1)
+ {
+ int j;
+ for (j=0;j<p;j++)
+ {
+ Fout[j] = *f;
+ f += fstride*in_stride;
+ }
+ } else {
+ int j;
+ for (j=0;j<p;j++)
+ {
+ kf_shuffle( Fout , f, fstride*p, in_stride, factors,st);
+ f += fstride*in_stride;
+ Fout += m;
+ }
+ }
+}
+
+static
+void kf_work(
+ kiss_fft_cpx * Fout,
+ const kiss_fft_cpx * f,
+ const size_t fstride,
+ int in_stride,
+ int * factors,
+ const kiss_fft_cfg st,
+ int N,
+ int s2,
+ int m2
+ )
+{
+ int i;
+ kiss_fft_cpx * Fout_beg=Fout;
+ const int p=*factors++; /* the radix */
+ const int m=*factors++; /* stage's fft length/p */
+#if 0
+ /*printf ("fft %d %d %d %d %d %d\n", p*m, m, p, s2, fstride*in_stride, N);*/
+ if (m==1)
+ {
+ /* int j;
+ for (j=0;j<p;j++)
+ {
+ Fout[j] = *f;
+ f += fstride*in_stride;
+ }*/
+ } else {
+ int j;
+ for (j=0;j<p;j++)
+ {
+ kf_work( Fout , f, fstride*p, in_stride, factors,st, N*p, fstride*in_stride, m);
+ f += fstride*in_stride;
+ Fout += m;
+ }
+ }
+
+ Fout=Fout_beg;
+
+ switch (p) {
+ case 2: kf_bfly2(Fout,fstride,st,m); break;
+ case 3: kf_bfly3(Fout,fstride,st,m); break;
+ case 4: kf_bfly4(Fout,fstride,st,m); break;
+ case 5: kf_bfly5(Fout,fstride,st,m); break;
+ default: kf_bfly_generic(Fout,fstride,st,m,p); break;
+ }
+#else
+ /*printf ("fft %d %d %d %d %d %d %d\n", p*m, m, p, s2, fstride*in_stride, N, m2);*/
+ if (m==1)
+ {
+ /*for (i=0;i<N;i++)
+ {
+ int j;
+ Fout = Fout_beg+i*m2;
+ const kiss_fft_cpx * f2 = f+i*s2;
+ for (j=0;j<p;j++)
+ {
+ *Fout++ = *f2;
+ f2 += fstride*in_stride;
+ }
+ }*/
+ }else{
+ kf_work( Fout , f, fstride*p, in_stride, factors,st, N*p, fstride*in_stride, m);
+ }
+
+
+
+
+ switch (p) {
+ case 2: kf_bfly2(Fout,fstride,st,m, N, m2); break;
+ case 3: for (i=0;i<N;i++){Fout=Fout_beg+i*m2; kf_bfly3(Fout,fstride,st,m);} break;
+ case 4: kf_bfly4(Fout,fstride,st,m, N, m2); break;
+ case 5: for (i=0;i<N;i++){Fout=Fout_beg+i*m2; kf_bfly5(Fout,fstride,st,m);} break;
+ default: for (i=0;i<N;i++){Fout=Fout_beg+i*m2; kf_bfly_generic(Fout,fstride,st,m,p);} break;
+ }
+#endif
+}
+
+/* facbuf is populated by p1,m1,p2,m2, ...
+ where
+ p[i] * m[i] = m[i-1]
+ m0 = n */
+static
+void kf_factor(int n,int * facbuf)
+{
+ int p=4;
+
+ /*factor out powers of 4, powers of 2, then any remaining primes */
+ do {
+ while (n % p) {
+ switch (p) {
+ case 4: p = 2; break;
+ case 2: p = 3; break;
+ default: p += 2; break;
+ }
+ if (p>32000 || (spx_int32_t)p*(spx_int32_t)p > n)
+ p = n; /* no more factors, skip to end */
+ }
+ n /= p;
+ *facbuf++ = p;
+ *facbuf++ = n;
+ } while (n > 1);
+}
+/*
+ *
+ * User-callable function to allocate all necessary storage space for the fft.
+ *
+ * The return value is a contiguous block of memory, allocated with malloc. As such,
+ * It can be freed with free(), rather than a kiss_fft-specific function.
+ * */
+kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem )
+{
+ kiss_fft_cfg st=NULL;
+ size_t memneeded = sizeof(struct kiss_fft_state)
+ + sizeof(kiss_fft_cpx)*(nfft-1); /* twiddle factors*/
+
+ if ( lenmem==NULL ) {
+ st = ( kiss_fft_cfg)KISS_FFT_MALLOC( memneeded );
+ }else{
+ if (mem != NULL && *lenmem >= memneeded)
+ st = (kiss_fft_cfg)mem;
+ *lenmem = memneeded;
+ }
+ if (st) {
+ int i;
+ st->nfft=nfft;
+ st->inverse = inverse_fft;
+#ifdef FIXED_POINT
+ for (i=0;i<nfft;++i) {
+ spx_word32_t phase = i;
+ if (!st->inverse)
+ phase = -phase;
+ kf_cexp2(st->twiddles+i, DIV32(SHL32(phase,17),nfft));
+ }
+#else
+ for (i=0;i<nfft;++i) {
+ const double pi=3.14159265358979323846264338327;
+ double phase = ( -2*pi /nfft ) * i;
+ if (st->inverse)
+ phase *= -1;
+ kf_cexp(st->twiddles+i, phase );
+ }
+#endif
+ kf_factor(nfft,st->factors);
+ }
+ return st;
+}
+
+
+
+
+void kiss_fft_stride(kiss_fft_cfg st,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int in_stride)
+{
+ if (fin == fout)
+ {
+ speex_fatal("In-place FFT not supported");
+ /*CHECKBUF(tmpbuf,ntmpbuf,st->nfft);
+ kf_work(tmpbuf,fin,1,in_stride, st->factors,st);
+ SPEEX_MOVE(fout,tmpbuf,st->nfft);*/
+ } else {
+ kf_shuffle( fout, fin, 1,in_stride, st->factors,st);
+ kf_work( fout, fin, 1,in_stride, st->factors,st, 1, in_stride, 1);
+ }
+}
+
+void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout)
+{
+ kiss_fft_stride(cfg,fin,fout,1);
+}
+
diff --git a/lib/speexdsp/libspeexdsp/kiss_fft.h b/lib/speexdsp/libspeexdsp/kiss_fft.h
new file mode 100644
index 00000000..bb19eadb
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/kiss_fft.h
@@ -0,0 +1,108 @@
+#ifndef KISS_FFT_H
+#define KISS_FFT_H
+
+#include <stdlib.h>
+#include <math.h>
+#include "arch.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ ATTENTION!
+ If you would like a :
+ -- a utility that will handle the caching of fft objects
+ -- real-only (no imaginary time component ) FFT
+ -- a multi-dimensional FFT
+ -- a command-line utility to perform ffts
+ -- a command-line utility to perform fast-convolution filtering
+
+ Then see kfc.h kiss_fftr.h kiss_fftnd.h fftutil.c kiss_fastfir.c
+ in the tools/ directory.
+*/
+
+#ifdef USE_SIMD
+# include <xmmintrin.h>
+# define kiss_fft_scalar __m128
+#define KISS_FFT_MALLOC(nbytes) memalign(16,nbytes)
+#else
+#define KISS_FFT_MALLOC speex_alloc
+#endif
+
+
+#ifdef FIXED_POINT
+#include "arch.h"
+# define kiss_fft_scalar spx_int16_t
+#else
+# ifndef kiss_fft_scalar
+/* default is float */
+# define kiss_fft_scalar float
+# endif
+#endif
+
+typedef struct {
+ kiss_fft_scalar r;
+ kiss_fft_scalar i;
+}kiss_fft_cpx;
+
+typedef struct kiss_fft_state* kiss_fft_cfg;
+
+/*
+ * kiss_fft_alloc
+ *
+ * Initialize a FFT (or IFFT) algorithm's cfg/state buffer.
+ *
+ * typical usage: kiss_fft_cfg mycfg=kiss_fft_alloc(1024,0,NULL,NULL);
+ *
+ * The return value from fft_alloc is a cfg buffer used internally
+ * by the fft routine or NULL.
+ *
+ * If lenmem is NULL, then kiss_fft_alloc will allocate a cfg buffer using malloc.
+ * The returned value should be free()d when done to avoid memory leaks.
+ *
+ * The state can be placed in a user supplied buffer 'mem':
+ * If lenmem is not NULL and mem is not NULL and *lenmem is large enough,
+ * then the function places the cfg in mem and the size used in *lenmem
+ * and returns mem.
+ *
+ * If lenmem is not NULL and ( mem is NULL or *lenmem is not large enough),
+ * then the function returns NULL and places the minimum cfg
+ * buffer size in *lenmem.
+ * */
+
+kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem);
+
+/*
+ * kiss_fft(cfg,in_out_buf)
+ *
+ * Perform an FFT on a complex input buffer.
+ * for a forward FFT,
+ * fin should be f[0] , f[1] , ... ,f[nfft-1]
+ * fout will be F[0] , F[1] , ... ,F[nfft-1]
+ * Note that each element is complex and can be accessed like
+ f[k].r and f[k].i
+ * */
+void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout);
+
+/*
+ A more generic version of the above function. It reads its input from every Nth sample.
+ * */
+void kiss_fft_stride(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int fin_stride);
+
+/* If kiss_fft_alloc allocated a buffer, it is one contiguous
+ buffer and can be simply free()d when no longer needed*/
+#define kiss_fft_free speex_free
+
+/*
+ Cleans up some memory that gets managed internally. Not necessary to call, but it might clean up
+ your compiler output to call this before you exit.
+*/
+void kiss_fft_cleanup(void);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/speexdsp/libspeexdsp/kiss_fftr.c b/lib/speexdsp/libspeexdsp/kiss_fftr.c
new file mode 100644
index 00000000..827e0b14
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/kiss_fftr.c
@@ -0,0 +1,297 @@
+/*
+Copyright (c) 2003-2004, Mark Borgerding
+
+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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "os_support.h"
+#include "kiss_fftr.h"
+#include "_kiss_fft_guts.h"
+
+struct kiss_fftr_state{
+ kiss_fft_cfg substate;
+ kiss_fft_cpx * tmpbuf;
+ kiss_fft_cpx * super_twiddles;
+#ifdef USE_SIMD
+ long pad;
+#endif
+};
+
+kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem)
+{
+ int i;
+ kiss_fftr_cfg st = NULL;
+ size_t subsize, memneeded;
+
+ if (nfft & 1) {
+ speex_warning("Real FFT optimization must be even.\n");
+ return NULL;
+ }
+ nfft >>= 1;
+
+ kiss_fft_alloc (nfft, inverse_fft, NULL, &subsize);
+ memneeded = sizeof(struct kiss_fftr_state) + subsize + sizeof(kiss_fft_cpx) * ( nfft * 2);
+
+ if (lenmem == NULL) {
+ st = (kiss_fftr_cfg) KISS_FFT_MALLOC (memneeded);
+ } else {
+ if (*lenmem >= memneeded)
+ st = (kiss_fftr_cfg) mem;
+ *lenmem = memneeded;
+ }
+ if (!st)
+ return NULL;
+
+ st->substate = (kiss_fft_cfg) (st + 1); /*just beyond kiss_fftr_state struct */
+ st->tmpbuf = (kiss_fft_cpx *) (((char *) st->substate) + subsize);
+ st->super_twiddles = st->tmpbuf + nfft;
+ kiss_fft_alloc(nfft, inverse_fft, st->substate, &subsize);
+
+#ifdef FIXED_POINT
+ for (i=0;i<nfft;++i) {
+ spx_word32_t phase = i+(nfft>>1);
+ if (!inverse_fft)
+ phase = -phase;
+ kf_cexp2(st->super_twiddles+i, DIV32(SHL32(phase,16),nfft));
+ }
+#else
+ for (i=0;i<nfft;++i) {
+ const double pi=3.14159265358979323846264338327;
+ double phase = pi*(((double)i) /nfft + .5);
+ if (!inverse_fft)
+ phase = -phase;
+ kf_cexp(st->super_twiddles+i, phase );
+ }
+#endif
+ return st;
+}
+
+void kiss_fftr(kiss_fftr_cfg st,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata)
+{
+ /* input buffer timedata is stored row-wise */
+ int k,ncfft;
+ kiss_fft_cpx fpnk,fpk,f1k,f2k,tw,tdc;
+
+ if ( st->substate->inverse) {
+ speex_fatal("kiss fft usage error: improper alloc\n");
+ }
+
+ ncfft = st->substate->nfft;
+
+ /*perform the parallel fft of two real signals packed in real,imag*/
+ kiss_fft( st->substate , (const kiss_fft_cpx*)timedata, st->tmpbuf );
+ /* The real part of the DC element of the frequency spectrum in st->tmpbuf
+ * contains the sum of the even-numbered elements of the input time sequence
+ * The imag part is the sum of the odd-numbered elements
+ *
+ * The sum of tdc.r and tdc.i is the sum of the input time sequence.
+ * yielding DC of input time sequence
+ * The difference of tdc.r - tdc.i is the sum of the input (dot product) [1,-1,1,-1...
+ * yielding Nyquist bin of input time sequence
+ */
+
+ tdc.r = st->tmpbuf[0].r;
+ tdc.i = st->tmpbuf[0].i;
+ C_FIXDIV(tdc,2);
+ CHECK_OVERFLOW_OP(tdc.r ,+, tdc.i);
+ CHECK_OVERFLOW_OP(tdc.r ,-, tdc.i);
+ freqdata[0].r = tdc.r + tdc.i;
+ freqdata[ncfft].r = tdc.r - tdc.i;
+#ifdef USE_SIMD
+ freqdata[ncfft].i = freqdata[0].i = _mm_set1_ps(0);
+#else
+ freqdata[ncfft].i = freqdata[0].i = 0;
+#endif
+
+ for ( k=1;k <= ncfft/2 ; ++k ) {
+ fpk = st->tmpbuf[k];
+ fpnk.r = st->tmpbuf[ncfft-k].r;
+ fpnk.i = - st->tmpbuf[ncfft-k].i;
+ C_FIXDIV(fpk,2);
+ C_FIXDIV(fpnk,2);
+
+ C_ADD( f1k, fpk , fpnk );
+ C_SUB( f2k, fpk , fpnk );
+ C_MUL( tw , f2k , st->super_twiddles[k]);
+
+ freqdata[k].r = HALF_OF(f1k.r + tw.r);
+ freqdata[k].i = HALF_OF(f1k.i + tw.i);
+ freqdata[ncfft-k].r = HALF_OF(f1k.r - tw.r);
+ freqdata[ncfft-k].i = HALF_OF(tw.i - f1k.i);
+ }
+}
+
+void kiss_fftri(kiss_fftr_cfg st,const kiss_fft_cpx *freqdata, kiss_fft_scalar *timedata)
+{
+ /* input buffer timedata is stored row-wise */
+ int k, ncfft;
+
+ if (st->substate->inverse == 0) {
+ speex_fatal("kiss fft usage error: improper alloc\n");
+ }
+
+ ncfft = st->substate->nfft;
+
+ st->tmpbuf[0].r = freqdata[0].r + freqdata[ncfft].r;
+ st->tmpbuf[0].i = freqdata[0].r - freqdata[ncfft].r;
+ /*C_FIXDIV(st->tmpbuf[0],2);*/
+
+ for (k = 1; k <= ncfft / 2; ++k) {
+ kiss_fft_cpx fk, fnkc, fek, fok, tmp;
+ fk = freqdata[k];
+ fnkc.r = freqdata[ncfft - k].r;
+ fnkc.i = -freqdata[ncfft - k].i;
+ /*C_FIXDIV( fk , 2 );
+ C_FIXDIV( fnkc , 2 );*/
+
+ C_ADD (fek, fk, fnkc);
+ C_SUB (tmp, fk, fnkc);
+ C_MUL (fok, tmp, st->super_twiddles[k]);
+ C_ADD (st->tmpbuf[k], fek, fok);
+ C_SUB (st->tmpbuf[ncfft - k], fek, fok);
+#ifdef USE_SIMD
+ st->tmpbuf[ncfft - k].i *= _mm_set1_ps(-1.0);
+#else
+ st->tmpbuf[ncfft - k].i *= -1;
+#endif
+ }
+ kiss_fft (st->substate, st->tmpbuf, (kiss_fft_cpx *) timedata);
+}
+
+void kiss_fftr2(kiss_fftr_cfg st,const kiss_fft_scalar *timedata,kiss_fft_scalar *freqdata)
+{
+ /* input buffer timedata is stored row-wise */
+ int k,ncfft;
+ kiss_fft_cpx f2k,tdc;
+ spx_word32_t f1kr, f1ki, twr, twi;
+
+ if ( st->substate->inverse) {
+ speex_fatal("kiss fft usage error: improper alloc\n");
+ }
+
+ ncfft = st->substate->nfft;
+
+ /*perform the parallel fft of two real signals packed in real,imag*/
+ kiss_fft( st->substate , (const kiss_fft_cpx*)timedata, st->tmpbuf );
+ /* The real part of the DC element of the frequency spectrum in st->tmpbuf
+ * contains the sum of the even-numbered elements of the input time sequence
+ * The imag part is the sum of the odd-numbered elements
+ *
+ * The sum of tdc.r and tdc.i is the sum of the input time sequence.
+ * yielding DC of input time sequence
+ * The difference of tdc.r - tdc.i is the sum of the input (dot product) [1,-1,1,-1...
+ * yielding Nyquist bin of input time sequence
+ */
+
+ tdc.r = st->tmpbuf[0].r;
+ tdc.i = st->tmpbuf[0].i;
+ C_FIXDIV(tdc,2);
+ CHECK_OVERFLOW_OP(tdc.r ,+, tdc.i);
+ CHECK_OVERFLOW_OP(tdc.r ,-, tdc.i);
+ freqdata[0] = tdc.r + tdc.i;
+ freqdata[2*ncfft-1] = tdc.r - tdc.i;
+
+ for ( k=1;k <= ncfft/2 ; ++k )
+ {
+ /*fpk = st->tmpbuf[k];
+ fpnk.r = st->tmpbuf[ncfft-k].r;
+ fpnk.i = - st->tmpbuf[ncfft-k].i;
+ C_FIXDIV(fpk,2);
+ C_FIXDIV(fpnk,2);
+
+ C_ADD( f1k, fpk , fpnk );
+ C_SUB( f2k, fpk , fpnk );
+
+ C_MUL( tw , f2k , st->super_twiddles[k]);
+
+ freqdata[2*k-1] = HALF_OF(f1k.r + tw.r);
+ freqdata[2*k] = HALF_OF(f1k.i + tw.i);
+ freqdata[2*(ncfft-k)-1] = HALF_OF(f1k.r - tw.r);
+ freqdata[2*(ncfft-k)] = HALF_OF(tw.i - f1k.i);
+ */
+
+ /*f1k.r = PSHR32(ADD32(EXTEND32(st->tmpbuf[k].r), EXTEND32(st->tmpbuf[ncfft-k].r)),1);
+ f1k.i = PSHR32(SUB32(EXTEND32(st->tmpbuf[k].i), EXTEND32(st->tmpbuf[ncfft-k].i)),1);
+ f2k.r = PSHR32(SUB32(EXTEND32(st->tmpbuf[k].r), EXTEND32(st->tmpbuf[ncfft-k].r)),1);
+ f2k.i = SHR32(ADD32(EXTEND32(st->tmpbuf[k].i), EXTEND32(st->tmpbuf[ncfft-k].i)),1);
+
+ C_MUL( tw , f2k , st->super_twiddles[k]);
+
+ freqdata[2*k-1] = HALF_OF(f1k.r + tw.r);
+ freqdata[2*k] = HALF_OF(f1k.i + tw.i);
+ freqdata[2*(ncfft-k)-1] = HALF_OF(f1k.r - tw.r);
+ freqdata[2*(ncfft-k)] = HALF_OF(tw.i - f1k.i);
+ */
+ f2k.r = SHR32(SUB32(EXTEND32(st->tmpbuf[k].r), EXTEND32(st->tmpbuf[ncfft-k].r)),1);
+ f2k.i = PSHR32(ADD32(EXTEND32(st->tmpbuf[k].i), EXTEND32(st->tmpbuf[ncfft-k].i)),1);
+
+ f1kr = SHL32(ADD32(EXTEND32(st->tmpbuf[k].r), EXTEND32(st->tmpbuf[ncfft-k].r)),13);
+ f1ki = SHL32(SUB32(EXTEND32(st->tmpbuf[k].i), EXTEND32(st->tmpbuf[ncfft-k].i)),13);
+
+ twr = SHR32(SUB32(MULT16_16(f2k.r,st->super_twiddles[k].r),MULT16_16(f2k.i,st->super_twiddles[k].i)), 1);
+ twi = SHR32(ADD32(MULT16_16(f2k.i,st->super_twiddles[k].r),MULT16_16(f2k.r,st->super_twiddles[k].i)), 1);
+
+#ifdef FIXED_POINT
+ freqdata[2*k-1] = PSHR32(f1kr + twr, 15);
+ freqdata[2*k] = PSHR32(f1ki + twi, 15);
+ freqdata[2*(ncfft-k)-1] = PSHR32(f1kr - twr, 15);
+ freqdata[2*(ncfft-k)] = PSHR32(twi - f1ki, 15);
+#else
+ freqdata[2*k-1] = .5f*(f1kr + twr);
+ freqdata[2*k] = .5f*(f1ki + twi);
+ freqdata[2*(ncfft-k)-1] = .5f*(f1kr - twr);
+ freqdata[2*(ncfft-k)] = .5f*(twi - f1ki);
+
+#endif
+ }
+}
+
+void kiss_fftri2(kiss_fftr_cfg st,const kiss_fft_scalar *freqdata,kiss_fft_scalar *timedata)
+{
+ /* input buffer timedata is stored row-wise */
+ int k, ncfft;
+
+ if (st->substate->inverse == 0) {
+ speex_fatal ("kiss fft usage error: improper alloc\n");
+ }
+
+ ncfft = st->substate->nfft;
+
+ st->tmpbuf[0].r = freqdata[0] + freqdata[2*ncfft-1];
+ st->tmpbuf[0].i = freqdata[0] - freqdata[2*ncfft-1];
+ /*C_FIXDIV(st->tmpbuf[0],2);*/
+
+ for (k = 1; k <= ncfft / 2; ++k) {
+ kiss_fft_cpx fk, fnkc, fek, fok, tmp;
+ fk.r = freqdata[2*k-1];
+ fk.i = freqdata[2*k];
+ fnkc.r = freqdata[2*(ncfft - k)-1];
+ fnkc.i = -freqdata[2*(ncfft - k)];
+ /*C_FIXDIV( fk , 2 );
+ C_FIXDIV( fnkc , 2 );*/
+
+ C_ADD (fek, fk, fnkc);
+ C_SUB (tmp, fk, fnkc);
+ C_MUL (fok, tmp, st->super_twiddles[k]);
+ C_ADD (st->tmpbuf[k], fek, fok);
+ C_SUB (st->tmpbuf[ncfft - k], fek, fok);
+#ifdef USE_SIMD
+ st->tmpbuf[ncfft - k].i *= _mm_set1_ps(-1.0);
+#else
+ st->tmpbuf[ncfft - k].i *= -1;
+#endif
+ }
+ kiss_fft (st->substate, st->tmpbuf, (kiss_fft_cpx *) timedata);
+}
diff --git a/lib/speexdsp/libspeexdsp/kiss_fftr.h b/lib/speexdsp/libspeexdsp/kiss_fftr.h
new file mode 100644
index 00000000..8cfeda13
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/kiss_fftr.h
@@ -0,0 +1,51 @@
+#ifndef KISS_FTR_H
+#define KISS_FTR_H
+
+#include "kiss_fft.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+
+ Real optimized version can save about 45% cpu time vs. complex fft of a real seq.
+
+
+
+ */
+
+typedef struct kiss_fftr_state *kiss_fftr_cfg;
+
+
+kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem, size_t * lenmem);
+/*
+ nfft must be even
+
+ If you don't care to allocate space, use mem = lenmem = NULL
+*/
+
+
+void kiss_fftr(kiss_fftr_cfg cfg,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata);
+/*
+ input timedata has nfft scalar points
+ output freqdata has nfft/2+1 complex points
+*/
+
+void kiss_fftr2(kiss_fftr_cfg st,const kiss_fft_scalar *timedata,kiss_fft_scalar *freqdata);
+
+void kiss_fftri(kiss_fftr_cfg cfg,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata);
+
+void kiss_fftri2(kiss_fftr_cfg st,const kiss_fft_scalar *freqdata, kiss_fft_scalar *timedata);
+
+/*
+ input freqdata has nfft/2+1 complex points
+ output timedata has nfft scalar points
+*/
+
+#define kiss_fftr_free speex_free
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/lib/speexdsp/libspeexdsp/math_approx.h b/lib/speexdsp/libspeexdsp/math_approx.h
new file mode 100644
index 00000000..596dfdc1
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/math_approx.h
@@ -0,0 +1,332 @@
+/* Copyright (C) 2002 Jean-Marc Valin */
+/**
+ @file math_approx.h
+ @brief Various math approximation functions for Speex
+*/
+/*
+ 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.
+*/
+
+#ifndef MATH_APPROX_H
+#define MATH_APPROX_H
+
+#include "arch.h"
+
+#ifndef FIXED_POINT
+
+#define spx_sqrt sqrt
+#define spx_acos acos
+#define spx_exp exp
+#define spx_cos_norm(x) (cos((.5f*M_PI)*(x)))
+#define spx_atan atan
+
+/** Generate a pseudo-random number */
+static inline spx_word16_t speex_rand(spx_word16_t std, spx_int32_t *seed)
+{
+ const unsigned int jflone = 0x3f800000;
+ const unsigned int jflmsk = 0x007fffff;
+ union {int i; float f;} ran;
+ *seed = 1664525 * *seed + 1013904223;
+ ran.i = jflone | (jflmsk & *seed);
+ ran.f -= 1.5;
+ return 3.4642*std*ran.f;
+}
+
+
+#endif
+
+
+static inline spx_int16_t spx_ilog2(spx_uint32_t x)
+{
+ int r=0;
+ if (x>=(spx_int32_t)65536)
+ {
+ x >>= 16;
+ r += 16;
+ }
+ if (x>=256)
+ {
+ x >>= 8;
+ r += 8;
+ }
+ if (x>=16)
+ {
+ x >>= 4;
+ r += 4;
+ }
+ if (x>=4)
+ {
+ x >>= 2;
+ r += 2;
+ }
+ if (x>=2)
+ {
+ r += 1;
+ }
+ return r;
+}
+
+static inline spx_int16_t spx_ilog4(spx_uint32_t x)
+{
+ int r=0;
+ if (x>=(spx_int32_t)65536)
+ {
+ x >>= 16;
+ r += 8;
+ }
+ if (x>=256)
+ {
+ x >>= 8;
+ r += 4;
+ }
+ if (x>=16)
+ {
+ x >>= 4;
+ r += 2;
+ }
+ if (x>=4)
+ {
+ r += 1;
+ }
+ return r;
+}
+
+#ifdef FIXED_POINT
+
+/** Generate a pseudo-random number */
+static inline spx_word16_t speex_rand(spx_word16_t std, spx_int32_t *seed)
+{
+ spx_word32_t res;
+ *seed = 1664525 * *seed + 1013904223;
+ res = MULT16_16(EXTRACT16(SHR32(*seed,16)),std);
+ return EXTRACT16(PSHR32(SUB32(res, SHR32(res, 3)),14));
+}
+
+/* sqrt(x) ~= 0.22178 + 1.29227*x - 0.77070*x^2 + 0.25723*x^3 (for .25 < x < 1) */
+/*#define C0 3634
+#define C1 21173
+#define C2 -12627
+#define C3 4215*/
+
+/* sqrt(x) ~= 0.22178 + 1.29227*x - 0.77070*x^2 + 0.25659*x^3 (for .25 < x < 1) */
+#define C0 3634
+#define C1 21173
+#define C2 -12627
+#define C3 4204
+
+static inline spx_word16_t spx_sqrt(spx_word32_t x)
+{
+ int k;
+ spx_word32_t rt;
+ k = spx_ilog4(x)-6;
+ x = VSHR32(x, (k<<1));
+ rt = ADD16(C0, MULT16_16_Q14(x, ADD16(C1, MULT16_16_Q14(x, ADD16(C2, MULT16_16_Q14(x, (C3)))))));
+ rt = VSHR32(rt,7-k);
+ return rt;
+}
+
+/* log(x) ~= -2.18151 + 4.20592*x - 2.88938*x^2 + 0.86535*x^3 (for .5 < x < 1) */
+
+
+#define A1 16469
+#define A2 2242
+#define A3 1486
+
+static inline spx_word16_t spx_acos(spx_word16_t x)
+{
+ int s=0;
+ spx_word16_t ret;
+ spx_word16_t sq;
+ if (x<0)
+ {
+ s=1;
+ x = NEG16(x);
+ }
+ x = SUB16(16384,x);
+
+ x = x >> 1;
+ sq = MULT16_16_Q13(x, ADD16(A1, MULT16_16_Q13(x, ADD16(A2, MULT16_16_Q13(x, (A3))))));
+ ret = spx_sqrt(SHL32(EXTEND32(sq),13));
+
+ /*ret = spx_sqrt(67108864*(-1.6129e-04 + 2.0104e+00*f + 2.7373e-01*f*f + 1.8136e-01*f*f*f));*/
+ if (s)
+ ret = SUB16(25736,ret);
+ return ret;
+}
+
+
+#define K1 8192
+#define K2 -4096
+#define K3 340
+#define K4 -10
+
+static inline spx_word16_t spx_cos(spx_word16_t x)
+{
+ spx_word16_t x2;
+
+ if (x<12868)
+ {
+ x2 = MULT16_16_P13(x,x);
+ return ADD32(K1, MULT16_16_P13(x2, ADD32(K2, MULT16_16_P13(x2, ADD32(K3, MULT16_16_P13(K4, x2))))));
+ } else {
+ x = SUB16(25736,x);
+ x2 = MULT16_16_P13(x,x);
+ return SUB32(-K1, MULT16_16_P13(x2, ADD32(K2, MULT16_16_P13(x2, ADD32(K3, MULT16_16_P13(K4, x2))))));
+ }
+}
+
+#define L1 32767
+#define L2 -7651
+#define L3 8277
+#define L4 -626
+
+static inline spx_word16_t _spx_cos_pi_2(spx_word16_t x)
+{
+ spx_word16_t x2;
+
+ x2 = MULT16_16_P15(x,x);
+ return ADD16(1,MIN16(32766,ADD32(SUB16(L1,x2), MULT16_16_P15(x2, ADD32(L2, MULT16_16_P15(x2, ADD32(L3, MULT16_16_P15(L4, x2))))))));
+}
+
+static inline spx_word16_t spx_cos_norm(spx_word32_t x)
+{
+ x = x&0x0001ffff;
+ if (x>SHL32(EXTEND32(1), 16))
+ x = SUB32(SHL32(EXTEND32(1), 17),x);
+ if (x&0x00007fff)
+ {
+ if (x<SHL32(EXTEND32(1), 15))
+ {
+ return _spx_cos_pi_2(EXTRACT16(x));
+ } else {
+ return NEG32(_spx_cos_pi_2(EXTRACT16(65536-x)));
+ }
+ } else {
+ if (x&0x0000ffff)
+ return 0;
+ else if (x&0x0001ffff)
+ return -32767;
+ else
+ return 32767;
+ }
+}
+
+/*
+ K0 = 1
+ K1 = log(2)
+ K2 = 3-4*log(2)
+ K3 = 3*log(2) - 2
+*/
+#define D0 16384
+#define D1 11356
+#define D2 3726
+#define D3 1301
+/* Input in Q11 format, output in Q16 */
+static inline spx_word32_t spx_exp2(spx_word16_t x)
+{
+ int integer;
+ spx_word16_t frac;
+ integer = SHR16(x,11);
+ if (integer>14)
+ return 0x7fffffff;
+ else if (integer < -15)
+ return 0;
+ frac = SHL16(x-SHL16(integer,11),3);
+ frac = ADD16(D0, MULT16_16_Q14(frac, ADD16(D1, MULT16_16_Q14(frac, ADD16(D2 , MULT16_16_Q14(D3,frac))))));
+ return VSHR32(EXTEND32(frac), -integer-2);
+}
+
+/* Input in Q11 format, output in Q16 */
+static inline spx_word32_t spx_exp(spx_word16_t x)
+{
+ if (x>21290)
+ return 0x7fffffff;
+ else if (x<-21290)
+ return 0;
+ else
+ return spx_exp2(MULT16_16_P14(23637,x));
+}
+#define M1 32767
+#define M2 -21
+#define M3 -11943
+#define M4 4936
+
+static inline spx_word16_t spx_atan01(spx_word16_t x)
+{
+ return MULT16_16_P15(x, ADD32(M1, MULT16_16_P15(x, ADD32(M2, MULT16_16_P15(x, ADD32(M3, MULT16_16_P15(M4, x)))))));
+}
+
+#undef M1
+#undef M2
+#undef M3
+#undef M4
+
+/* Input in Q15, output in Q14 */
+static inline spx_word16_t spx_atan(spx_word32_t x)
+{
+ if (x <= 32767)
+ {
+ return SHR16(spx_atan01(x),1);
+ } else {
+ int e = spx_ilog2(x);
+ if (e>=29)
+ return 25736;
+ x = DIV32_16(SHL32(EXTEND32(32767),29-e), EXTRACT16(SHR32(x, e-14)));
+ return SUB16(25736, SHR16(spx_atan01(x),1));
+ }
+}
+#else
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846 /* pi */
+#endif
+
+#define C1 0.9999932946f
+#define C2 -0.4999124376f
+#define C3 0.0414877472f
+#define C4 -0.0012712095f
+
+
+#define SPX_PI_2 1.5707963268
+static inline spx_word16_t spx_cos(spx_word16_t x)
+{
+ if (x<SPX_PI_2)
+ {
+ x *= x;
+ return C1 + x*(C2+x*(C3+C4*x));
+ } else {
+ x = M_PI-x;
+ x *= x;
+ return NEG16(C1 + x*(C2+x*(C3+C4*x)));
+ }
+}
+
+#endif
+
+
+#endif
diff --git a/lib/speexdsp/libspeexdsp/mdf.c b/lib/speexdsp/libspeexdsp/mdf.c
new file mode 100644
index 00000000..7b367f9a
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/mdf.c
@@ -0,0 +1,1279 @@
+/* Copyright (C) 2003-2008 Jean-Marc Valin
+
+ File: mdf.c
+ Echo canceller based on the MDF algorithm (see below)
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. 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.
+
+ 3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 echo canceller is based on the MDF algorithm described in:
+
+ J. S. Soo, K. K. Pang Multidelay block frequency adaptive filter,
+ IEEE Trans. Acoust. Speech Signal Process., Vol. ASSP-38, No. 2,
+ February 1990.
+
+ We use the Alternatively Updated MDF (AUMDF) variant. Robustness to
+ double-talk is achieved using a variable learning rate as described in:
+
+ Valin, J.-M., On Adjusting the Learning Rate in Frequency Domain Echo
+ Cancellation With Double-Talk. IEEE Transactions on Audio,
+ Speech and Language Processing, Vol. 15, No. 3, pp. 1030-1034, 2007.
+ http://people.xiph.org/~jm/papers/valin_taslp2006.pdf
+
+ There is no explicit double-talk detection, but a continuous variation
+ in the learning rate based on residual echo, double-talk and background
+ noise.
+
+ About the fixed-point version:
+ All the signals are represented with 16-bit words. The filter weights
+ are represented with 32-bit words, but only the top 16 bits are used
+ in most cases. The lower 16 bits are completely unreliable (due to the
+ fact that the update is done only on the top bits), but help in the
+ adaptation -- probably by removing a "threshold effect" due to
+ quantization (rounding going to zero) when the gradient is small.
+
+ Another kludge that seems to work good: when performing the weight
+ update, we only move half the way toward the "goal" this seems to
+ reduce the effect of quantization noise in the update phase. This
+ can be seen as applying a gradient descent on a "soft constraint"
+ instead of having a hard constraint.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "arch.h"
+#include "speex/speex_echo.h"
+#include "fftwrap.h"
+#include "pseudofloat.h"
+#include "math_approx.h"
+#include "os_support.h"
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+#ifdef FIXED_POINT
+#define WEIGHT_SHIFT 11
+#define NORMALIZE_SCALEDOWN 5
+#define NORMALIZE_SCALEUP 3
+#else
+#define WEIGHT_SHIFT 0
+#endif
+
+/* If enabled, the AEC will use a foreground filter and a background filter to be more robust to double-talk
+ and difficult signals in general. The cost is an extra FFT and a matrix-vector multiply */
+#define TWO_PATH
+
+#ifdef FIXED_POINT
+static const spx_float_t MIN_LEAK = {20972, -22};
+
+/* Constants for the two-path filter */
+static const spx_float_t VAR1_SMOOTH = {23593, -16};
+static const spx_float_t VAR2_SMOOTH = {23675, -15};
+static const spx_float_t VAR1_UPDATE = {16384, -15};
+static const spx_float_t VAR2_UPDATE = {16384, -16};
+static const spx_float_t VAR_BACKTRACK = {16384, -12};
+#define TOP16(x) ((x)>>16)
+
+#else
+
+static const spx_float_t MIN_LEAK = .005f;
+
+/* Constants for the two-path filter */
+static const spx_float_t VAR1_SMOOTH = .36f;
+static const spx_float_t VAR2_SMOOTH = .7225f;
+static const spx_float_t VAR1_UPDATE = .5f;
+static const spx_float_t VAR2_UPDATE = .25f;
+static const spx_float_t VAR_BACKTRACK = 4.f;
+#define TOP16(x) (x)
+#endif
+
+
+#define PLAYBACK_DELAY 2
+
+void speex_echo_get_residual(SpeexEchoState *st, spx_word32_t *Yout, int len);
+
+
+/** Speex echo cancellation state. */
+struct SpeexEchoState_ {
+ int frame_size; /**< Number of samples processed each time */
+ int window_size;
+ int M;
+ int cancel_count;
+ int adapted;
+ int saturated;
+ int screwed_up;
+ int C; /** Number of input channels (microphones) */
+ int K; /** Number of output channels (loudspeakers) */
+ spx_int32_t sampling_rate;
+ spx_word16_t spec_average;
+ spx_word16_t beta0;
+ spx_word16_t beta_max;
+ spx_word32_t sum_adapt;
+ spx_word16_t leak_estimate;
+
+ spx_word16_t *e; /* scratch */
+ spx_word16_t *x; /* Far-end input buffer (2N) */
+ spx_word16_t *X; /* Far-end buffer (M+1 frames) in frequency domain */
+ spx_word16_t *input; /* scratch */
+ spx_word16_t *y; /* scratch */
+ spx_word16_t *last_y;
+ spx_word16_t *Y; /* scratch */
+ spx_word16_t *E;
+ spx_word32_t *PHI; /* scratch */
+ spx_word32_t *W; /* (Background) filter weights */
+#ifdef TWO_PATH
+ spx_word16_t *foreground; /* Foreground filter weights */
+ spx_word32_t Davg1; /* 1st recursive average of the residual power difference */
+ spx_word32_t Davg2; /* 2nd recursive average of the residual power difference */
+ spx_float_t Dvar1; /* Estimated variance of 1st estimator */
+ spx_float_t Dvar2; /* Estimated variance of 2nd estimator */
+#endif
+ spx_word32_t *power; /* Power of the far-end signal */
+ spx_float_t *power_1;/* Inverse power of far-end */
+ spx_word16_t *wtmp; /* scratch */
+#ifdef FIXED_POINT
+ spx_word16_t *wtmp2; /* scratch */
+#endif
+ spx_word32_t *Rf; /* scratch */
+ spx_word32_t *Yf; /* scratch */
+ spx_word32_t *Xf; /* scratch */
+ spx_word32_t *Eh;
+ spx_word32_t *Yh;
+ spx_float_t Pey;
+ spx_float_t Pyy;
+ spx_word16_t *window;
+ spx_word16_t *prop;
+ void *fft_table;
+ spx_word16_t *memX, *memD, *memE;
+ spx_word16_t preemph;
+ spx_word16_t notch_radius;
+ spx_mem_t *notch_mem;
+
+ /* NOTE: If you only use speex_echo_cancel() and want to save some memory, remove this */
+ spx_int16_t *play_buf;
+ int play_buf_pos;
+ int play_buf_started;
+};
+
+static inline void filter_dc_notch16(const spx_int16_t *in, spx_word16_t radius, spx_word16_t *out, int len, spx_mem_t *mem, int stride)
+{
+ int i;
+ spx_word16_t den2;
+#ifdef FIXED_POINT
+ den2 = MULT16_16_Q15(radius,radius) + MULT16_16_Q15(QCONST16(.7,15),MULT16_16_Q15(32767-radius,32767-radius));
+#else
+ den2 = radius*radius + .7*(1-radius)*(1-radius);
+#endif
+ /*printf ("%d %d %d %d %d %d\n", num[0], num[1], num[2], den[0], den[1], den[2]);*/
+ for (i=0;i<len;i++)
+ {
+ spx_word16_t vin = in[i*stride];
+ spx_word32_t vout = mem[0] + SHL32(EXTEND32(vin),15);
+#ifdef FIXED_POINT
+ mem[0] = mem[1] + SHL32(SHL32(-EXTEND32(vin),15) + MULT16_32_Q15(radius,vout),1);
+#else
+ mem[0] = mem[1] + 2*(-vin + radius*vout);
+#endif
+ mem[1] = SHL32(EXTEND32(vin),15) - MULT16_32_Q15(den2,vout);
+ out[i] = SATURATE32(PSHR32(MULT16_32_Q15(radius,vout),15),32767);
+ }
+}
+
+/* This inner product is slightly different from the codec version because of fixed-point */
+static inline spx_word32_t mdf_inner_prod(const spx_word16_t *x, const spx_word16_t *y, int len)
+{
+ spx_word32_t sum=0;
+ len >>= 1;
+ while(len--)
+ {
+ spx_word32_t part=0;
+ part = MAC16_16(part,*x++,*y++);
+ part = MAC16_16(part,*x++,*y++);
+ /* HINT: If you had a 40-bit accumulator, you could shift only at the end */
+ sum = ADD32(sum,SHR32(part,6));
+ }
+ return sum;
+}
+
+/** Compute power spectrum of a half-complex (packed) vector */
+static inline void power_spectrum(const spx_word16_t *X, spx_word32_t *ps, int N)
+{
+ int i, j;
+ ps[0]=MULT16_16(X[0],X[0]);
+ for (i=1,j=1;i<N-1;i+=2,j++)
+ {
+ ps[j] = MULT16_16(X[i],X[i]) + MULT16_16(X[i+1],X[i+1]);
+ }
+ ps[j]=MULT16_16(X[i],X[i]);
+}
+
+/** Compute power spectrum of a half-complex (packed) vector and accumulate */
+static inline void power_spectrum_accum(const spx_word16_t *X, spx_word32_t *ps, int N)
+{
+ int i, j;
+ ps[0]+=MULT16_16(X[0],X[0]);
+ for (i=1,j=1;i<N-1;i+=2,j++)
+ {
+ ps[j] += MULT16_16(X[i],X[i]) + MULT16_16(X[i+1],X[i+1]);
+ }
+ ps[j]+=MULT16_16(X[i],X[i]);
+}
+
+/** Compute cross-power spectrum of a half-complex (packed) vectors and add to acc */
+#ifdef FIXED_POINT
+static inline void spectral_mul_accum(const spx_word16_t *X, const spx_word32_t *Y, spx_word16_t *acc, int N, int M)
+{
+ int i,j;
+ spx_word32_t tmp1=0,tmp2=0;
+ for (j=0;j<M;j++)
+ {
+ tmp1 = MAC16_16(tmp1, X[j*N],TOP16(Y[j*N]));
+ }
+ acc[0] = PSHR32(tmp1,WEIGHT_SHIFT);
+ for (i=1;i<N-1;i+=2)
+ {
+ tmp1 = tmp2 = 0;
+ for (j=0;j<M;j++)
+ {
+ tmp1 = SUB32(MAC16_16(tmp1, X[j*N+i],TOP16(Y[j*N+i])), MULT16_16(X[j*N+i+1],TOP16(Y[j*N+i+1])));
+ tmp2 = MAC16_16(MAC16_16(tmp2, X[j*N+i+1],TOP16(Y[j*N+i])), X[j*N+i], TOP16(Y[j*N+i+1]));
+ }
+ acc[i] = PSHR32(tmp1,WEIGHT_SHIFT);
+ acc[i+1] = PSHR32(tmp2,WEIGHT_SHIFT);
+ }
+ tmp1 = tmp2 = 0;
+ for (j=0;j<M;j++)
+ {
+ tmp1 = MAC16_16(tmp1, X[(j+1)*N-1],TOP16(Y[(j+1)*N-1]));
+ }
+ acc[N-1] = PSHR32(tmp1,WEIGHT_SHIFT);
+}
+static inline void spectral_mul_accum16(const spx_word16_t *X, const spx_word16_t *Y, spx_word16_t *acc, int N, int M)
+{
+ int i,j;
+ spx_word32_t tmp1=0,tmp2=0;
+ for (j=0;j<M;j++)
+ {
+ tmp1 = MAC16_16(tmp1, X[j*N],Y[j*N]);
+ }
+ acc[0] = PSHR32(tmp1,WEIGHT_SHIFT);
+ for (i=1;i<N-1;i+=2)
+ {
+ tmp1 = tmp2 = 0;
+ for (j=0;j<M;j++)
+ {
+ tmp1 = SUB32(MAC16_16(tmp1, X[j*N+i],Y[j*N+i]), MULT16_16(X[j*N+i+1],Y[j*N+i+1]));
+ tmp2 = MAC16_16(MAC16_16(tmp2, X[j*N+i+1],Y[j*N+i]), X[j*N+i], Y[j*N+i+1]);
+ }
+ acc[i] = PSHR32(tmp1,WEIGHT_SHIFT);
+ acc[i+1] = PSHR32(tmp2,WEIGHT_SHIFT);
+ }
+ tmp1 = tmp2 = 0;
+ for (j=0;j<M;j++)
+ {
+ tmp1 = MAC16_16(tmp1, X[(j+1)*N-1],Y[(j+1)*N-1]);
+ }
+ acc[N-1] = PSHR32(tmp1,WEIGHT_SHIFT);
+}
+
+#else
+static inline void spectral_mul_accum(const spx_word16_t *X, const spx_word32_t *Y, spx_word16_t *acc, int N, int M)
+{
+ int i,j;
+ for (i=0;i<N;i++)
+ acc[i] = 0;
+ for (j=0;j<M;j++)
+ {
+ acc[0] += X[0]*Y[0];
+ for (i=1;i<N-1;i+=2)
+ {
+ acc[i] += (X[i]*Y[i] - X[i+1]*Y[i+1]);
+ acc[i+1] += (X[i+1]*Y[i] + X[i]*Y[i+1]);
+ }
+ acc[i] += X[i]*Y[i];
+ X += N;
+ Y += N;
+ }
+}
+#define spectral_mul_accum16 spectral_mul_accum
+#endif
+
+/** Compute weighted cross-power spectrum of a half-complex (packed) vector with conjugate */
+static inline void weighted_spectral_mul_conj(const spx_float_t *w, const spx_float_t p, const spx_word16_t *X, const spx_word16_t *Y, spx_word32_t *prod, int N)
+{
+ int i, j;
+ spx_float_t W;
+ W = FLOAT_AMULT(p, w[0]);
+ prod[0] = FLOAT_MUL32(W,MULT16_16(X[0],Y[0]));
+ for (i=1,j=1;i<N-1;i+=2,j++)
+ {
+ W = FLOAT_AMULT(p, w[j]);
+ prod[i] = FLOAT_MUL32(W,MAC16_16(MULT16_16(X[i],Y[i]), X[i+1],Y[i+1]));
+ prod[i+1] = FLOAT_MUL32(W,MAC16_16(MULT16_16(-X[i+1],Y[i]), X[i],Y[i+1]));
+ }
+ W = FLOAT_AMULT(p, w[j]);
+ prod[i] = FLOAT_MUL32(W,MULT16_16(X[i],Y[i]));
+}
+
+static inline void mdf_adjust_prop(const spx_word32_t *W, int N, int M, int P, spx_word16_t *prop)
+{
+ int i, j, p;
+ spx_word16_t max_sum = 1;
+ spx_word32_t prop_sum = 1;
+ for (i=0;i<M;i++)
+ {
+ spx_word32_t tmp = 1;
+ for (p=0;p<P;p++)
+ for (j=0;j<N;j++)
+ tmp += MULT16_16(EXTRACT16(SHR32(W[p*N*M + i*N+j],18)), EXTRACT16(SHR32(W[p*N*M + i*N+j],18)));
+#ifdef FIXED_POINT
+ /* Just a security in case an overflow were to occur */
+ tmp = MIN32(ABS32(tmp), 536870912);
+#endif
+ prop[i] = spx_sqrt(tmp);
+ if (prop[i] > max_sum)
+ max_sum = prop[i];
+ }
+ for (i=0;i<M;i++)
+ {
+ prop[i] += MULT16_16_Q15(QCONST16(.1f,15),max_sum);
+ prop_sum += EXTEND32(prop[i]);
+ }
+ for (i=0;i<M;i++)
+ {
+ prop[i] = DIV32(MULT16_16(QCONST16(.99f,15), prop[i]),prop_sum);
+ /*printf ("%f ", prop[i]);*/
+ }
+ /*printf ("\n");*/
+}
+
+#ifdef DUMP_ECHO_CANCEL_DATA
+#include <stdio.h>
+static FILE *rFile=NULL, *pFile=NULL, *oFile=NULL;
+
+static void dump_audio(const spx_int16_t *rec, const spx_int16_t *play, const spx_int16_t *out, int len)
+{
+ if (!(rFile && pFile && oFile))
+ {
+ speex_fatal("Dump files not open");
+ }
+ fwrite(rec, sizeof(spx_int16_t), len, rFile);
+ fwrite(play, sizeof(spx_int16_t), len, pFile);
+ fwrite(out, sizeof(spx_int16_t), len, oFile);
+}
+#endif
+
+/** Creates a new echo canceller state */
+EXPORT SpeexEchoState *speex_echo_state_init(int frame_size, int filter_length)
+{
+ return speex_echo_state_init_mc(frame_size, filter_length, 1, 1);
+}
+
+EXPORT SpeexEchoState *speex_echo_state_init_mc(int frame_size, int filter_length, int nb_mic, int nb_speakers)
+{
+ int i,N,M, C, K;
+ SpeexEchoState *st = (SpeexEchoState *)speex_alloc(sizeof(SpeexEchoState));
+
+ st->K = nb_speakers;
+ st->C = nb_mic;
+ C=st->C;
+ K=st->K;
+#ifdef DUMP_ECHO_CANCEL_DATA
+ if (rFile || pFile || oFile)
+ speex_fatal("Opening dump files twice");
+ rFile = fopen("aec_rec.sw", "wb");
+ pFile = fopen("aec_play.sw", "wb");
+ oFile = fopen("aec_out.sw", "wb");
+#endif
+
+ st->frame_size = frame_size;
+ st->window_size = 2*frame_size;
+ N = st->window_size;
+ M = st->M = (filter_length+st->frame_size-1)/frame_size;
+ st->cancel_count=0;
+ st->sum_adapt = 0;
+ st->saturated = 0;
+ st->screwed_up = 0;
+ /* This is the default sampling rate */
+ st->sampling_rate = 8000;
+ st->spec_average = DIV32_16(SHL32(EXTEND32(st->frame_size), 15), st->sampling_rate);
+#ifdef FIXED_POINT
+ st->beta0 = DIV32_16(SHL32(EXTEND32(st->frame_size), 16), st->sampling_rate);
+ st->beta_max = DIV32_16(SHL32(EXTEND32(st->frame_size), 14), st->sampling_rate);
+#else
+ st->beta0 = (2.0f*st->frame_size)/st->sampling_rate;
+ st->beta_max = (.5f*st->frame_size)/st->sampling_rate;
+#endif
+ st->leak_estimate = 0;
+
+ st->fft_table = spx_fft_init(N);
+
+ st->e = (spx_word16_t*)speex_alloc(C*N*sizeof(spx_word16_t));
+ st->x = (spx_word16_t*)speex_alloc(K*N*sizeof(spx_word16_t));
+ st->input = (spx_word16_t*)speex_alloc(C*st->frame_size*sizeof(spx_word16_t));
+ st->y = (spx_word16_t*)speex_alloc(C*N*sizeof(spx_word16_t));
+ st->last_y = (spx_word16_t*)speex_alloc(C*N*sizeof(spx_word16_t));
+ st->Yf = (spx_word32_t*)speex_alloc((st->frame_size+1)*sizeof(spx_word32_t));
+ st->Rf = (spx_word32_t*)speex_alloc((st->frame_size+1)*sizeof(spx_word32_t));
+ st->Xf = (spx_word32_t*)speex_alloc((st->frame_size+1)*sizeof(spx_word32_t));
+ st->Yh = (spx_word32_t*)speex_alloc((st->frame_size+1)*sizeof(spx_word32_t));
+ st->Eh = (spx_word32_t*)speex_alloc((st->frame_size+1)*sizeof(spx_word32_t));
+
+ st->X = (spx_word16_t*)speex_alloc(K*(M+1)*N*sizeof(spx_word16_t));
+ st->Y = (spx_word16_t*)speex_alloc(C*N*sizeof(spx_word16_t));
+ st->E = (spx_word16_t*)speex_alloc(C*N*sizeof(spx_word16_t));
+ st->W = (spx_word32_t*)speex_alloc(C*K*M*N*sizeof(spx_word32_t));
+#ifdef TWO_PATH
+ st->foreground = (spx_word16_t*)speex_alloc(M*N*C*K*sizeof(spx_word16_t));
+#endif
+ st->PHI = (spx_word32_t*)speex_alloc(N*sizeof(spx_word32_t));
+ st->power = (spx_word32_t*)speex_alloc((frame_size+1)*sizeof(spx_word32_t));
+ st->power_1 = (spx_float_t*)speex_alloc((frame_size+1)*sizeof(spx_float_t));
+ st->window = (spx_word16_t*)speex_alloc(N*sizeof(spx_word16_t));
+ st->prop = (spx_word16_t*)speex_alloc(M*sizeof(spx_word16_t));
+ st->wtmp = (spx_word16_t*)speex_alloc(N*sizeof(spx_word16_t));
+#ifdef FIXED_POINT
+ st->wtmp2 = (spx_word16_t*)speex_alloc(N*sizeof(spx_word16_t));
+ for (i=0;i<N>>1;i++)
+ {
+ st->window[i] = (16383-SHL16(spx_cos(DIV32_16(MULT16_16(25736,i<<1),N)),1));
+ st->window[N-i-1] = st->window[i];
+ }
+#else
+ for (i=0;i<N;i++)
+ st->window[i] = .5-.5*cos(2*M_PI*i/N);
+#endif
+ for (i=0;i<=st->frame_size;i++)
+ st->power_1[i] = FLOAT_ONE;
+ for (i=0;i<N*M*K*C;i++)
+ st->W[i] = 0;
+ {
+ spx_word32_t sum = 0;
+ /* Ratio of ~10 between adaptation rate of first and last block */
+ spx_word16_t decay = SHR32(spx_exp(NEG16(DIV32_16(QCONST16(2.4,11),M))),1);
+ st->prop[0] = QCONST16(.7, 15);
+ sum = EXTEND32(st->prop[0]);
+ for (i=1;i<M;i++)
+ {
+ st->prop[i] = MULT16_16_Q15(st->prop[i-1], decay);
+ sum = ADD32(sum, EXTEND32(st->prop[i]));
+ }
+ for (i=M-1;i>=0;i--)
+ {
+ st->prop[i] = DIV32(MULT16_16(QCONST16(.8f,15), st->prop[i]),sum);
+ }
+ }
+
+ st->memX = (spx_word16_t*)speex_alloc(K*sizeof(spx_word16_t));
+ st->memD = (spx_word16_t*)speex_alloc(C*sizeof(spx_word16_t));
+ st->memE = (spx_word16_t*)speex_alloc(C*sizeof(spx_word16_t));
+ st->preemph = QCONST16(.9,15);
+ if (st->sampling_rate<12000)
+ st->notch_radius = QCONST16(.9, 15);
+ else if (st->sampling_rate<24000)
+ st->notch_radius = QCONST16(.982, 15);
+ else
+ st->notch_radius = QCONST16(.992, 15);
+
+ st->notch_mem = (spx_mem_t*)speex_alloc(2*C*sizeof(spx_mem_t));
+ st->adapted = 0;
+ st->Pey = st->Pyy = FLOAT_ONE;
+
+#ifdef TWO_PATH
+ st->Davg1 = st->Davg2 = 0;
+ st->Dvar1 = st->Dvar2 = FLOAT_ZERO;
+#endif
+
+ st->play_buf = (spx_int16_t*)speex_alloc(K*(PLAYBACK_DELAY+1)*st->frame_size*sizeof(spx_int16_t));
+ st->play_buf_pos = PLAYBACK_DELAY*st->frame_size;
+ st->play_buf_started = 0;
+
+ return st;
+}
+
+/** Resets echo canceller state */
+EXPORT void speex_echo_state_reset(SpeexEchoState *st)
+{
+ int i, M, N, C, K;
+ st->cancel_count=0;
+ st->screwed_up = 0;
+ N = st->window_size;
+ M = st->M;
+ C=st->C;
+ K=st->K;
+ for (i=0;i<N*M;i++)
+ st->W[i] = 0;
+#ifdef TWO_PATH
+ for (i=0;i<N*M;i++)
+ st->foreground[i] = 0;
+#endif
+ for (i=0;i<N*(M+1);i++)
+ st->X[i] = 0;
+ for (i=0;i<=st->frame_size;i++)
+ {
+ st->power[i] = 0;
+ st->power_1[i] = FLOAT_ONE;
+ st->Eh[i] = 0;
+ st->Yh[i] = 0;
+ }
+ for (i=0;i<st->frame_size;i++)
+ {
+ st->last_y[i] = 0;
+ }
+ for (i=0;i<N*C;i++)
+ {
+ st->E[i] = 0;
+ }
+ for (i=0;i<N*K;i++)
+ {
+ st->x[i] = 0;
+ }
+ for (i=0;i<2*C;i++)
+ st->notch_mem[i] = 0;
+ for (i=0;i<C;i++)
+ st->memD[i]=st->memE[i]=0;
+ for (i=0;i<K;i++)
+ st->memX[i]=0;
+
+ st->saturated = 0;
+ st->adapted = 0;
+ st->sum_adapt = 0;
+ st->Pey = st->Pyy = FLOAT_ONE;
+#ifdef TWO_PATH
+ st->Davg1 = st->Davg2 = 0;
+ st->Dvar1 = st->Dvar2 = FLOAT_ZERO;
+#endif
+ for (i=0;i<3*st->frame_size;i++)
+ st->play_buf[i] = 0;
+ st->play_buf_pos = PLAYBACK_DELAY*st->frame_size;
+ st->play_buf_started = 0;
+
+}
+
+/** Destroys an echo canceller state */
+EXPORT void speex_echo_state_destroy(SpeexEchoState *st)
+{
+ spx_fft_destroy(st->fft_table);
+
+ speex_free(st->e);
+ speex_free(st->x);
+ speex_free(st->input);
+ speex_free(st->y);
+ speex_free(st->last_y);
+ speex_free(st->Yf);
+ speex_free(st->Rf);
+ speex_free(st->Xf);
+ speex_free(st->Yh);
+ speex_free(st->Eh);
+
+ speex_free(st->X);
+ speex_free(st->Y);
+ speex_free(st->E);
+ speex_free(st->W);
+#ifdef TWO_PATH
+ speex_free(st->foreground);
+#endif
+ speex_free(st->PHI);
+ speex_free(st->power);
+ speex_free(st->power_1);
+ speex_free(st->window);
+ speex_free(st->prop);
+ speex_free(st->wtmp);
+#ifdef FIXED_POINT
+ speex_free(st->wtmp2);
+#endif
+ speex_free(st->memX);
+ speex_free(st->memD);
+ speex_free(st->memE);
+ speex_free(st->notch_mem);
+
+ speex_free(st->play_buf);
+ speex_free(st);
+
+#ifdef DUMP_ECHO_CANCEL_DATA
+ fclose(rFile);
+ fclose(pFile);
+ fclose(oFile);
+ rFile = pFile = oFile = NULL;
+#endif
+}
+
+EXPORT void speex_echo_capture(SpeexEchoState *st, const spx_int16_t *rec, spx_int16_t *out)
+{
+ int i;
+ /*speex_warning_int("capture with fill level ", st->play_buf_pos/st->frame_size);*/
+ st->play_buf_started = 1;
+ if (st->play_buf_pos>=st->frame_size)
+ {
+ speex_echo_cancellation(st, rec, st->play_buf, out);
+ st->play_buf_pos -= st->frame_size;
+ for (i=0;i<st->play_buf_pos;i++)
+ st->play_buf[i] = st->play_buf[i+st->frame_size];
+ } else {
+ speex_warning("No playback frame available (your application is buggy and/or got xruns)");
+ if (st->play_buf_pos!=0)
+ {
+ speex_warning("internal playback buffer corruption?");
+ st->play_buf_pos = 0;
+ }
+ for (i=0;i<st->frame_size;i++)
+ out[i] = rec[i];
+ }
+}
+
+EXPORT void speex_echo_playback(SpeexEchoState *st, const spx_int16_t *play)
+{
+ /*speex_warning_int("playback with fill level ", st->play_buf_pos/st->frame_size);*/
+ if (!st->play_buf_started)
+ {
+ speex_warning("discarded first playback frame");
+ return;
+ }
+ if (st->play_buf_pos<=PLAYBACK_DELAY*st->frame_size)
+ {
+ int i;
+ for (i=0;i<st->frame_size;i++)
+ st->play_buf[st->play_buf_pos+i] = play[i];
+ st->play_buf_pos += st->frame_size;
+ if (st->play_buf_pos <= (PLAYBACK_DELAY-1)*st->frame_size)
+ {
+ speex_warning("Auto-filling the buffer (your application is buggy and/or got xruns)");
+ for (i=0;i<st->frame_size;i++)
+ st->play_buf[st->play_buf_pos+i] = play[i];
+ st->play_buf_pos += st->frame_size;
+ }
+ } else {
+ speex_warning("Had to discard a playback frame (your application is buggy and/or got xruns)");
+ }
+}
+
+/** Performs echo cancellation on a frame (deprecated, last arg now ignored) */
+EXPORT void speex_echo_cancel(SpeexEchoState *st, const spx_int16_t *in, const spx_int16_t *far_end, spx_int16_t *out, spx_int32_t *Yout)
+{
+ speex_echo_cancellation(st, in, far_end, out);
+}
+
+/** Performs echo cancellation on a frame */
+EXPORT void speex_echo_cancellation(SpeexEchoState *st, const spx_int16_t *in, const spx_int16_t *far_end, spx_int16_t *out)
+{
+ int i,j, chan, speak;
+ int N,M, C, K;
+ spx_word32_t Syy,See,Sxx,Sdd, Sff;
+#ifdef TWO_PATH
+ spx_word32_t Dbf;
+ int update_foreground;
+#endif
+ spx_word32_t Sey;
+ spx_word16_t ss, ss_1;
+ spx_float_t Pey = FLOAT_ONE, Pyy=FLOAT_ONE;
+ spx_float_t alpha, alpha_1;
+ spx_word16_t RER;
+ spx_word32_t tmp32;
+
+ N = st->window_size;
+ M = st->M;
+ C = st->C;
+ K = st->K;
+
+ st->cancel_count++;
+#ifdef FIXED_POINT
+ ss=DIV32_16(11469,M);
+ ss_1 = SUB16(32767,ss);
+#else
+ ss=.35/M;
+ ss_1 = 1-ss;
+#endif
+
+ for (chan = 0; chan < C; chan++)
+ {
+ /* Apply a notch filter to make sure DC doesn't end up causing problems */
+ filter_dc_notch16(in+chan, st->notch_radius, st->input+chan*st->frame_size, st->frame_size, st->notch_mem+2*chan, C);
+ /* Copy input data to buffer and apply pre-emphasis */
+ /* Copy input data to buffer */
+ for (i=0;i<st->frame_size;i++)
+ {
+ spx_word32_t tmp32;
+ /* FIXME: This core has changed a bit, need to merge properly */
+ tmp32 = SUB32(EXTEND32(st->input[chan*st->frame_size+i]), EXTEND32(MULT16_16_P15(st->preemph, st->memD[chan])));
+#ifdef FIXED_POINT
+ if (tmp32 > 32767)
+ {
+ tmp32 = 32767;
+ if (st->saturated == 0)
+ st->saturated = 1;
+ }
+ if (tmp32 < -32767)
+ {
+ tmp32 = -32767;
+ if (st->saturated == 0)
+ st->saturated = 1;
+ }
+#endif
+ st->memD[chan] = st->input[chan*st->frame_size+i];
+ st->input[chan*st->frame_size+i] = EXTRACT16(tmp32);
+ }
+ }
+
+ for (speak = 0; speak < K; speak++)
+ {
+ for (i=0;i<st->frame_size;i++)
+ {
+ spx_word32_t tmp32;
+ st->x[speak*N+i] = st->x[speak*N+i+st->frame_size];
+ tmp32 = SUB32(EXTEND32(far_end[i*K+speak]), EXTEND32(MULT16_16_P15(st->preemph, st->memX[speak])));
+#ifdef FIXED_POINT
+ /*FIXME: If saturation occurs here, we need to freeze adaptation for M frames (not just one) */
+ if (tmp32 > 32767)
+ {
+ tmp32 = 32767;
+ st->saturated = M+1;
+ }
+ if (tmp32 < -32767)
+ {
+ tmp32 = -32767;
+ st->saturated = M+1;
+ }
+#endif
+ st->x[speak*N+i+st->frame_size] = EXTRACT16(tmp32);
+ st->memX[speak] = far_end[i*K+speak];
+ }
+ }
+
+ for (speak = 0; speak < K; speak++)
+ {
+ /* Shift memory: this could be optimized eventually*/
+ for (j=M-1;j>=0;j--)
+ {
+ for (i=0;i<N;i++)
+ st->X[(j+1)*N*K+speak*N+i] = st->X[j*N*K+speak*N+i];
+ }
+ /* Convert x (echo input) to frequency domain */
+ spx_fft(st->fft_table, st->x+speak*N, &st->X[speak*N]);
+ }
+
+ Sxx = 0;
+ for (speak = 0; speak < K; speak++)
+ {
+ Sxx += mdf_inner_prod(st->x+speak*N+st->frame_size, st->x+speak*N+st->frame_size, st->frame_size);
+ power_spectrum_accum(st->X+speak*N, st->Xf, N);
+ }
+
+ Sff = 0;
+ for (chan = 0; chan < C; chan++)
+ {
+#ifdef TWO_PATH
+ /* Compute foreground filter */
+ spectral_mul_accum16(st->X, st->foreground+chan*N*K*M, st->Y+chan*N, N, M*K);
+ spx_ifft(st->fft_table, st->Y+chan*N, st->e+chan*N);
+ for (i=0;i<st->frame_size;i++)
+ st->e[chan*N+i] = SUB16(st->input[chan*st->frame_size+i], st->e[chan*N+i+st->frame_size]);
+ Sff += mdf_inner_prod(st->e+chan*N, st->e+chan*N, st->frame_size);
+#endif
+ }
+
+ /* Adjust proportional adaption rate */
+ /* FIXME: Adjust that for C, K*/
+ if (st->adapted)
+ mdf_adjust_prop (st->W, N, M, C*K, st->prop);
+ /* Compute weight gradient */
+ if (st->saturated == 0)
+ {
+ for (chan = 0; chan < C; chan++)
+ {
+ for (speak = 0; speak < K; speak++)
+ {
+ for (j=M-1;j>=0;j--)
+ {
+ weighted_spectral_mul_conj(st->power_1, FLOAT_SHL(PSEUDOFLOAT(st->prop[j]),-15), &st->X[(j+1)*N*K+speak*N], st->E+chan*N, st->PHI, N);
+ for (i=0;i<N;i++)
+ st->W[chan*N*K*M + j*N*K + speak*N + i] += st->PHI[i];
+ }
+ }
+ }
+ } else {
+ st->saturated--;
+ }
+
+ /* FIXME: MC conversion required */
+ /* Update weight to prevent circular convolution (MDF / AUMDF) */
+ for (chan = 0; chan < C; chan++)
+ {
+ for (speak = 0; speak < K; speak++)
+ {
+ for (j=0;j<M;j++)
+ {
+ /* This is a variant of the Alternatively Updated MDF (AUMDF) */
+ /* Remove the "if" to make this an MDF filter */
+ if (j==0 || st->cancel_count%(M-1) == j-1)
+ {
+#ifdef FIXED_POINT
+ for (i=0;i<N;i++)
+ st->wtmp2[i] = EXTRACT16(PSHR32(st->W[chan*N*K*M + j*N*K + speak*N + i],NORMALIZE_SCALEDOWN+16));
+ spx_ifft(st->fft_table, st->wtmp2, st->wtmp);
+ for (i=0;i<st->frame_size;i++)
+ {
+ st->wtmp[i]=0;
+ }
+ for (i=st->frame_size;i<N;i++)
+ {
+ st->wtmp[i]=SHL16(st->wtmp[i],NORMALIZE_SCALEUP);
+ }
+ spx_fft(st->fft_table, st->wtmp, st->wtmp2);
+ /* The "-1" in the shift is a sort of kludge that trades less efficient update speed for decrease noise */
+ for (i=0;i<N;i++)
+ st->W[chan*N*K*M + j*N*K + speak*N + i] -= SHL32(EXTEND32(st->wtmp2[i]),16+NORMALIZE_SCALEDOWN-NORMALIZE_SCALEUP-1);
+#else
+ spx_ifft(st->fft_table, &st->W[chan*N*K*M + j*N*K + speak*N], st->wtmp);
+ for (i=st->frame_size;i<N;i++)
+ {
+ st->wtmp[i]=0;
+ }
+ spx_fft(st->fft_table, st->wtmp, &st->W[chan*N*K*M + j*N*K + speak*N]);
+#endif
+ }
+ }
+ }
+ }
+
+ /* So we can use power_spectrum_accum */
+ for (i=0;i<=st->frame_size;i++)
+ st->Rf[i] = st->Yf[i] = st->Xf[i] = 0;
+
+ Dbf = 0;
+ See = 0;
+#ifdef TWO_PATH
+ /* Difference in response, this is used to estimate the variance of our residual power estimate */
+ for (chan = 0; chan < C; chan++)
+ {
+ spectral_mul_accum(st->X, st->W+chan*N*K*M, st->Y+chan*N, N, M*K);
+ spx_ifft(st->fft_table, st->Y+chan*N, st->y+chan*N);
+ for (i=0;i<st->frame_size;i++)
+ st->e[chan*N+i] = SUB16(st->e[chan*N+i+st->frame_size], st->y[chan*N+i+st->frame_size]);
+ Dbf += 10+mdf_inner_prod(st->e+chan*N, st->e+chan*N, st->frame_size);
+ for (i=0;i<st->frame_size;i++)
+ st->e[chan*N+i] = SUB16(st->input[chan*st->frame_size+i], st->y[chan*N+i+st->frame_size]);
+ See += mdf_inner_prod(st->e+chan*N, st->e+chan*N, st->frame_size);
+ }
+#endif
+
+#ifndef TWO_PATH
+ Sff = See;
+#endif
+
+#ifdef TWO_PATH
+ /* Logic for updating the foreground filter */
+
+ /* For two time windows, compute the mean of the energy difference, as well as the variance */
+ st->Davg1 = ADD32(MULT16_32_Q15(QCONST16(.6f,15),st->Davg1), MULT16_32_Q15(QCONST16(.4f,15),SUB32(Sff,See)));
+ st->Davg2 = ADD32(MULT16_32_Q15(QCONST16(.85f,15),st->Davg2), MULT16_32_Q15(QCONST16(.15f,15),SUB32(Sff,See)));
+ st->Dvar1 = FLOAT_ADD(FLOAT_MULT(VAR1_SMOOTH, st->Dvar1), FLOAT_MUL32U(MULT16_32_Q15(QCONST16(.4f,15),Sff), MULT16_32_Q15(QCONST16(.4f,15),Dbf)));
+ st->Dvar2 = FLOAT_ADD(FLOAT_MULT(VAR2_SMOOTH, st->Dvar2), FLOAT_MUL32U(MULT16_32_Q15(QCONST16(.15f,15),Sff), MULT16_32_Q15(QCONST16(.15f,15),Dbf)));
+
+ /* Equivalent float code:
+ st->Davg1 = .6*st->Davg1 + .4*(Sff-See);
+ st->Davg2 = .85*st->Davg2 + .15*(Sff-See);
+ st->Dvar1 = .36*st->Dvar1 + .16*Sff*Dbf;
+ st->Dvar2 = .7225*st->Dvar2 + .0225*Sff*Dbf;
+ */
+
+ update_foreground = 0;
+ /* Check if we have a statistically significant reduction in the residual echo */
+ /* Note that this is *not* Gaussian, so we need to be careful about the longer tail */
+ if (FLOAT_GT(FLOAT_MUL32U(SUB32(Sff,See),ABS32(SUB32(Sff,See))), FLOAT_MUL32U(Sff,Dbf)))
+ update_foreground = 1;
+ else if (FLOAT_GT(FLOAT_MUL32U(st->Davg1, ABS32(st->Davg1)), FLOAT_MULT(VAR1_UPDATE,(st->Dvar1))))
+ update_foreground = 1;
+ else if (FLOAT_GT(FLOAT_MUL32U(st->Davg2, ABS32(st->Davg2)), FLOAT_MULT(VAR2_UPDATE,(st->Dvar2))))
+ update_foreground = 1;
+
+ /* Do we update? */
+ if (update_foreground)
+ {
+ st->Davg1 = st->Davg2 = 0;
+ st->Dvar1 = st->Dvar2 = FLOAT_ZERO;
+ /* Copy background filter to foreground filter */
+ for (i=0;i<N*M*C*K;i++)
+ st->foreground[i] = EXTRACT16(PSHR32(st->W[i],16));
+ /* Apply a smooth transition so as to not introduce blocking artifacts */
+ for (chan = 0; chan < C; chan++)
+ for (i=0;i<st->frame_size;i++)
+ st->e[chan*N+i+st->frame_size] = MULT16_16_Q15(st->window[i+st->frame_size],st->e[chan*N+i+st->frame_size]) + MULT16_16_Q15(st->window[i],st->y[chan*N+i+st->frame_size]);
+ } else {
+ int reset_background=0;
+ /* Otherwise, check if the background filter is significantly worse */
+ if (FLOAT_GT(FLOAT_MUL32U(NEG32(SUB32(Sff,See)),ABS32(SUB32(Sff,See))), FLOAT_MULT(VAR_BACKTRACK,FLOAT_MUL32U(Sff,Dbf))))
+ reset_background = 1;
+ if (FLOAT_GT(FLOAT_MUL32U(NEG32(st->Davg1), ABS32(st->Davg1)), FLOAT_MULT(VAR_BACKTRACK,st->Dvar1)))
+ reset_background = 1;
+ if (FLOAT_GT(FLOAT_MUL32U(NEG32(st->Davg2), ABS32(st->Davg2)), FLOAT_MULT(VAR_BACKTRACK,st->Dvar2)))
+ reset_background = 1;
+ if (reset_background)
+ {
+ /* Copy foreground filter to background filter */
+ for (i=0;i<N*M*C*K;i++)
+ st->W[i] = SHL32(EXTEND32(st->foreground[i]),16);
+ /* We also need to copy the output so as to get correct adaptation */
+ for (chan = 0; chan < C; chan++)
+ {
+ for (i=0;i<st->frame_size;i++)
+ st->y[chan*N+i+st->frame_size] = st->e[chan*N+i+st->frame_size];
+ for (i=0;i<st->frame_size;i++)
+ st->e[chan*N+i] = SUB16(st->input[chan*st->frame_size+i], st->y[chan*N+i+st->frame_size]);
+ }
+ See = Sff;
+ st->Davg1 = st->Davg2 = 0;
+ st->Dvar1 = st->Dvar2 = FLOAT_ZERO;
+ }
+ }
+#endif
+
+ Sey = Syy = Sdd = 0;
+ for (chan = 0; chan < C; chan++)
+ {
+ /* Compute error signal (for the output with de-emphasis) */
+ for (i=0;i<st->frame_size;i++)
+ {
+ spx_word32_t tmp_out;
+#ifdef TWO_PATH
+ tmp_out = SUB32(EXTEND32(st->input[chan*st->frame_size+i]), EXTEND32(st->e[chan*N+i+st->frame_size]));
+#else
+ tmp_out = SUB32(EXTEND32(st->input[chan*st->frame_size+i]), EXTEND32(st->y[chan*N+i+st->frame_size]));
+#endif
+ tmp_out = ADD32(tmp_out, EXTEND32(MULT16_16_P15(st->preemph, st->memE[chan])));
+ /* This is an arbitrary test for saturation in the microphone signal */
+ if (in[i*C+chan] <= -32000 || in[i*C+chan] >= 32000)
+ {
+ if (st->saturated == 0)
+ st->saturated = 1;
+ }
+ out[i*C+chan] = WORD2INT(tmp_out);
+ st->memE[chan] = tmp_out;
+ }
+
+#ifdef DUMP_ECHO_CANCEL_DATA
+ dump_audio(in, far_end, out, st->frame_size);
+#endif
+
+ /* Compute error signal (filter update version) */
+ for (i=0;i<st->frame_size;i++)
+ {
+ st->e[chan*N+i+st->frame_size] = st->e[chan*N+i];
+ st->e[chan*N+i] = 0;
+ }
+
+ /* Compute a bunch of correlations */
+ /* FIXME: bad merge */
+ Sey += mdf_inner_prod(st->e+chan*N+st->frame_size, st->y+chan*N+st->frame_size, st->frame_size);
+ Syy += mdf_inner_prod(st->y+chan*N+st->frame_size, st->y+chan*N+st->frame_size, st->frame_size);
+ Sdd += mdf_inner_prod(st->input+chan*st->frame_size, st->input+chan*st->frame_size, st->frame_size);
+
+ /* Convert error to frequency domain */
+ spx_fft(st->fft_table, st->e+chan*N, st->E+chan*N);
+ for (i=0;i<st->frame_size;i++)
+ st->y[i+chan*N] = 0;
+ spx_fft(st->fft_table, st->y+chan*N, st->Y+chan*N);
+
+ /* Compute power spectrum of echo (X), error (E) and filter response (Y) */
+ power_spectrum_accum(st->E+chan*N, st->Rf, N);
+ power_spectrum_accum(st->Y+chan*N, st->Yf, N);
+
+ }
+
+ /*printf ("%f %f %f %f\n", Sff, See, Syy, Sdd, st->update_cond);*/
+
+ /* Do some sanity check */
+ if (!(Syy>=0 && Sxx>=0 && See >= 0)
+#ifndef FIXED_POINT
+ || !(Sff < N*1e9 && Syy < N*1e9 && Sxx < N*1e9)
+#endif
+ )
+ {
+ /* Things have gone really bad */
+ st->screwed_up += 50;
+ for (i=0;i<st->frame_size*C;i++)
+ out[i] = 0;
+ } else if (SHR32(Sff, 2) > ADD32(Sdd, SHR32(MULT16_16(N, 10000),6)))
+ {
+ /* AEC seems to add lots of echo instead of removing it, let's see if it will improve */
+ st->screwed_up++;
+ } else {
+ /* Everything's fine */
+ st->screwed_up=0;
+ }
+ if (st->screwed_up>=50)
+ {
+ speex_warning("The echo canceller started acting funny and got slapped (reset). It swears it will behave now.");
+ speex_echo_state_reset(st);
+ return;
+ }
+
+ /* Add a small noise floor to make sure not to have problems when dividing */
+ See = MAX32(See, SHR32(MULT16_16(N, 100),6));
+
+ for (speak = 0; speak < K; speak++)
+ {
+ Sxx += mdf_inner_prod(st->x+speak*N+st->frame_size, st->x+speak*N+st->frame_size, st->frame_size);
+ power_spectrum_accum(st->X+speak*N, st->Xf, N);
+ }
+
+
+ /* Smooth far end energy estimate over time */
+ for (j=0;j<=st->frame_size;j++)
+ st->power[j] = MULT16_32_Q15(ss_1,st->power[j]) + 1 + MULT16_32_Q15(ss,st->Xf[j]);
+
+ /* Compute filtered spectra and (cross-)correlations */
+ for (j=st->frame_size;j>=0;j--)
+ {
+ spx_float_t Eh, Yh;
+ Eh = PSEUDOFLOAT(st->Rf[j] - st->Eh[j]);
+ Yh = PSEUDOFLOAT(st->Yf[j] - st->Yh[j]);
+ Pey = FLOAT_ADD(Pey,FLOAT_MULT(Eh,Yh));
+ Pyy = FLOAT_ADD(Pyy,FLOAT_MULT(Yh,Yh));
+#ifdef FIXED_POINT
+ st->Eh[j] = MAC16_32_Q15(MULT16_32_Q15(SUB16(32767,st->spec_average),st->Eh[j]), st->spec_average, st->Rf[j]);
+ st->Yh[j] = MAC16_32_Q15(MULT16_32_Q15(SUB16(32767,st->spec_average),st->Yh[j]), st->spec_average, st->Yf[j]);
+#else
+ st->Eh[j] = (1-st->spec_average)*st->Eh[j] + st->spec_average*st->Rf[j];
+ st->Yh[j] = (1-st->spec_average)*st->Yh[j] + st->spec_average*st->Yf[j];
+#endif
+ }
+
+ Pyy = FLOAT_SQRT(Pyy);
+ Pey = FLOAT_DIVU(Pey,Pyy);
+
+ /* Compute correlation updatete rate */
+ tmp32 = MULT16_32_Q15(st->beta0,Syy);
+ if (tmp32 > MULT16_32_Q15(st->beta_max,See))
+ tmp32 = MULT16_32_Q15(st->beta_max,See);
+ alpha = FLOAT_DIV32(tmp32, See);
+ alpha_1 = FLOAT_SUB(FLOAT_ONE, alpha);
+ /* Update correlations (recursive average) */
+ st->Pey = FLOAT_ADD(FLOAT_MULT(alpha_1,st->Pey) , FLOAT_MULT(alpha,Pey));
+ st->Pyy = FLOAT_ADD(FLOAT_MULT(alpha_1,st->Pyy) , FLOAT_MULT(alpha,Pyy));
+ if (FLOAT_LT(st->Pyy, FLOAT_ONE))
+ st->Pyy = FLOAT_ONE;
+ /* We don't really hope to get better than 33 dB (MIN_LEAK-3dB) attenuation anyway */
+ if (FLOAT_LT(st->Pey, FLOAT_MULT(MIN_LEAK,st->Pyy)))
+ st->Pey = FLOAT_MULT(MIN_LEAK,st->Pyy);
+ if (FLOAT_GT(st->Pey, st->Pyy))
+ st->Pey = st->Pyy;
+ /* leak_estimate is the linear regression result */
+ st->leak_estimate = FLOAT_EXTRACT16(FLOAT_SHL(FLOAT_DIVU(st->Pey, st->Pyy),14));
+ /* This looks like a stupid bug, but it's right (because we convert from Q14 to Q15) */
+ if (st->leak_estimate > 16383)
+ st->leak_estimate = 32767;
+ else
+ st->leak_estimate = SHL16(st->leak_estimate,1);
+ /*printf ("%f\n", st->leak_estimate);*/
+
+ /* Compute Residual to Error Ratio */
+#ifdef FIXED_POINT
+ tmp32 = MULT16_32_Q15(st->leak_estimate,Syy);
+ tmp32 = ADD32(SHR32(Sxx,13), ADD32(tmp32, SHL32(tmp32,1)));
+ /* Check for y in e (lower bound on RER) */
+ {
+ spx_float_t bound = PSEUDOFLOAT(Sey);
+ bound = FLOAT_DIVU(FLOAT_MULT(bound, bound), PSEUDOFLOAT(ADD32(1,Syy)));
+ if (FLOAT_GT(bound, PSEUDOFLOAT(See)))
+ tmp32 = See;
+ else if (tmp32 < FLOAT_EXTRACT32(bound))
+ tmp32 = FLOAT_EXTRACT32(bound);
+ }
+ if (tmp32 > SHR32(See,1))
+ tmp32 = SHR32(See,1);
+ RER = FLOAT_EXTRACT16(FLOAT_SHL(FLOAT_DIV32(tmp32,See),15));
+#else
+ RER = (.0001*Sxx + 3.*MULT16_32_Q15(st->leak_estimate,Syy)) / See;
+ /* Check for y in e (lower bound on RER) */
+ if (RER < Sey*Sey/(1+See*Syy))
+ RER = Sey*Sey/(1+See*Syy);
+ if (RER > .5)
+ RER = .5;
+#endif
+
+ /* We consider that the filter has had minimal adaptation if the following is true*/
+ if (!st->adapted && st->sum_adapt > SHL32(EXTEND32(M),15) && MULT16_32_Q15(st->leak_estimate,Syy) > MULT16_32_Q15(QCONST16(.03f,15),Syy))
+ {
+ st->adapted = 1;
+ }
+
+ if (st->adapted)
+ {
+ /* Normal learning rate calculation once we're past the minimal adaptation phase */
+ for (i=0;i<=st->frame_size;i++)
+ {
+ spx_word32_t r, e;
+ /* Compute frequency-domain adaptation mask */
+ r = MULT16_32_Q15(st->leak_estimate,SHL32(st->Yf[i],3));
+ e = SHL32(st->Rf[i],3)+1;
+#ifdef FIXED_POINT
+ if (r>SHR32(e,1))
+ r = SHR32(e,1);
+#else
+ if (r>.5*e)
+ r = .5*e;
+#endif
+ r = MULT16_32_Q15(QCONST16(.7,15),r) + MULT16_32_Q15(QCONST16(.3,15),(spx_word32_t)(MULT16_32_Q15(RER,e)));
+ /*st->power_1[i] = adapt_rate*r/(e*(1+st->power[i]));*/
+ st->power_1[i] = FLOAT_SHL(FLOAT_DIV32_FLOAT(r,FLOAT_MUL32U(e,st->power[i]+10)),WEIGHT_SHIFT+16);
+ }
+ } else {
+ /* Temporary adaption rate if filter is not yet adapted enough */
+ spx_word16_t adapt_rate=0;
+
+ if (Sxx > SHR32(MULT16_16(N, 1000),6))
+ {
+ tmp32 = MULT16_32_Q15(QCONST16(.25f, 15), Sxx);
+#ifdef FIXED_POINT
+ if (tmp32 > SHR32(See,2))
+ tmp32 = SHR32(See,2);
+#else
+ if (tmp32 > .25*See)
+ tmp32 = .25*See;
+#endif
+ adapt_rate = FLOAT_EXTRACT16(FLOAT_SHL(FLOAT_DIV32(tmp32, See),15));
+ }
+ for (i=0;i<=st->frame_size;i++)
+ st->power_1[i] = FLOAT_SHL(FLOAT_DIV32(EXTEND32(adapt_rate),ADD32(st->power[i],10)),WEIGHT_SHIFT+1);
+
+
+ /* How much have we adapted so far? */
+ st->sum_adapt = ADD32(st->sum_adapt,adapt_rate);
+ }
+
+ /* FIXME: MC conversion required */
+ for (i=0;i<st->frame_size;i++)
+ st->last_y[i] = st->last_y[st->frame_size+i];
+ if (st->adapted)
+ {
+ /* If the filter is adapted, take the filtered echo */
+ for (i=0;i<st->frame_size;i++)
+ st->last_y[st->frame_size+i] = in[i]-out[i];
+ } else {
+ /* If filter isn't adapted yet, all we can do is take the far end signal directly */
+ /* moved earlier: for (i=0;i<N;i++)
+ st->last_y[i] = st->x[i];*/
+ }
+
+}
+
+/* Compute spectrum of estimated echo for use in an echo post-filter */
+void speex_echo_get_residual(SpeexEchoState *st, spx_word32_t *residual_echo, int len)
+{
+ int i;
+ spx_word16_t leak2;
+ int N;
+
+ N = st->window_size;
+
+ /* Apply hanning window (should pre-compute it)*/
+ for (i=0;i<N;i++)
+ st->y[i] = MULT16_16_Q15(st->window[i],st->last_y[i]);
+
+ /* Compute power spectrum of the echo */
+ spx_fft(st->fft_table, st->y, st->Y);
+ power_spectrum(st->Y, residual_echo, N);
+
+#ifdef FIXED_POINT
+ if (st->leak_estimate > 16383)
+ leak2 = 32767;
+ else
+ leak2 = SHL16(st->leak_estimate, 1);
+#else
+ if (st->leak_estimate>.5)
+ leak2 = 1;
+ else
+ leak2 = 2*st->leak_estimate;
+#endif
+ /* Estimate residual echo */
+ for (i=0;i<=st->frame_size;i++)
+ residual_echo[i] = (spx_int32_t)MULT16_32_Q15(leak2,residual_echo[i]);
+
+}
+
+EXPORT int speex_echo_ctl(SpeexEchoState *st, int request, void *ptr)
+{
+ switch(request)
+ {
+
+ case SPEEX_ECHO_GET_FRAME_SIZE:
+ (*(int*)ptr) = st->frame_size;
+ break;
+ case SPEEX_ECHO_SET_SAMPLING_RATE:
+ st->sampling_rate = (*(int*)ptr);
+ st->spec_average = DIV32_16(SHL32(EXTEND32(st->frame_size), 15), st->sampling_rate);
+#ifdef FIXED_POINT
+ st->beta0 = DIV32_16(SHL32(EXTEND32(st->frame_size), 16), st->sampling_rate);
+ st->beta_max = DIV32_16(SHL32(EXTEND32(st->frame_size), 14), st->sampling_rate);
+#else
+ st->beta0 = (2.0f*st->frame_size)/st->sampling_rate;
+ st->beta_max = (.5f*st->frame_size)/st->sampling_rate;
+#endif
+ if (st->sampling_rate<12000)
+ st->notch_radius = QCONST16(.9, 15);
+ else if (st->sampling_rate<24000)
+ st->notch_radius = QCONST16(.982, 15);
+ else
+ st->notch_radius = QCONST16(.992, 15);
+ break;
+ case SPEEX_ECHO_GET_SAMPLING_RATE:
+ (*(int*)ptr) = st->sampling_rate;
+ break;
+ case SPEEX_ECHO_GET_IMPULSE_RESPONSE_SIZE:
+ /*FIXME: Implement this for multiple channels */
+ *((spx_int32_t *)ptr) = st->M * st->frame_size;
+ break;
+ case SPEEX_ECHO_GET_IMPULSE_RESPONSE:
+ {
+ int M = st->M, N = st->window_size, n = st->frame_size, i, j;
+ spx_int32_t *filt = (spx_int32_t *) ptr;
+ for(j=0;j<M;j++)
+ {
+ /*FIXME: Implement this for multiple channels */
+#ifdef FIXED_POINT
+ for (i=0;i<N;i++)
+ st->wtmp2[i] = EXTRACT16(PSHR32(st->W[j*N+i],16+NORMALIZE_SCALEDOWN));
+ spx_ifft(st->fft_table, st->wtmp2, st->wtmp);
+#else
+ spx_ifft(st->fft_table, &st->W[j*N], st->wtmp);
+#endif
+ for(i=0;i<n;i++)
+ filt[j*n+i] = PSHR32(MULT16_16(32767,st->wtmp[i]), WEIGHT_SHIFT-NORMALIZE_SCALEDOWN);
+ }
+ }
+ break;
+ default:
+ speex_warning_int("Unknown speex_echo_ctl request: ", request);
+ return -1;
+ }
+ return 0;
+}
diff --git a/lib/speexdsp/libspeexdsp/misc_bfin.h b/lib/speexdsp/libspeexdsp/misc_bfin.h
new file mode 100644
index 00000000..4e27681c
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/misc_bfin.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 2005 Analog Devices */
+/**
+ @file misc_bfin.h
+ @author Jean-Marc Valin
+ @brief Various compatibility routines for Speex (Blackfin version)
+*/
+/*
+ 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 "bfin.h"
+
+#define OVERRIDE_SPEEX_MOVE
+void *speex_move (void *dest, void *src, int n)
+{
+ __asm__ __volatile__
+ (
+ "L0 = 0;\n\t"
+ "I0 = %0;\n\t"
+ "R0 = [I0++];\n\t"
+ "LOOP move%= LC0 = %2;\n\t"
+ "LOOP_BEGIN move%=;\n\t"
+ "[%1++] = R0 || R0 = [I0++];\n\t"
+ "LOOP_END move%=;\n\t"
+ "[%1++] = R0;\n\t"
+ : "=a" (src), "=a" (dest)
+ : "a" ((n>>2)-1), "0" (src), "1" (dest)
+ : "R0", "I0", "L0", "memory" BFIN_HWLOOP0_REGS
+ );
+ return dest;
+}
diff --git a/lib/speexdsp/libspeexdsp/os_support.h b/lib/speexdsp/libspeexdsp/os_support.h
new file mode 100644
index 00000000..0db31a61
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/os_support.h
@@ -0,0 +1,169 @@
+/* Copyright (C) 2007 Jean-Marc Valin
+
+ File: os_support.h
+ This is the (tiny) OS abstraction layer. Aside from math.h, this is the
+ only place where system headers are allowed.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. 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.
+
+ 3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+*/
+
+#ifndef OS_SUPPORT_H
+#define OS_SUPPORT_H
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#ifdef OS_SUPPORT_CUSTOM
+#include "os_support_custom.h"
+#endif
+
+/** Speex wrapper for calloc. To do your own dynamic allocation, all you need to do is replace this function, speex_realloc and speex_free
+ NOTE: speex_alloc needs to CLEAR THE MEMORY */
+#ifndef OVERRIDE_SPEEX_ALLOC
+static inline void *speex_alloc (int size)
+{
+ /* WARNING: this is not equivalent to malloc(). If you want to use malloc()
+ or your own allocator, YOU NEED TO CLEAR THE MEMORY ALLOCATED. Otherwise
+ you will experience strange bugs */
+ return calloc(size,1);
+}
+#endif
+
+/** Same as speex_alloc, except that the area is only needed inside a Speex call (might cause problem with wideband though) */
+#ifndef OVERRIDE_SPEEX_ALLOC_SCRATCH
+static inline void *speex_alloc_scratch (int size)
+{
+ /* Scratch space doesn't need to be cleared */
+ return calloc(size,1);
+}
+#endif
+
+/** Speex wrapper for realloc. To do your own dynamic allocation, all you need to do is replace this function, speex_alloc and speex_free */
+#ifndef OVERRIDE_SPEEX_REALLOC
+static inline void *speex_realloc (void *ptr, int size)
+{
+ return realloc(ptr, size);
+}
+#endif
+
+/** Speex wrapper for calloc. To do your own dynamic allocation, all you need to do is replace this function, speex_realloc and speex_alloc */
+#ifndef OVERRIDE_SPEEX_FREE
+static inline void speex_free (void *ptr)
+{
+ free(ptr);
+}
+#endif
+
+/** Same as speex_free, except that the area is only needed inside a Speex call (might cause problem with wideband though) */
+#ifndef OVERRIDE_SPEEX_FREE_SCRATCH
+static inline void speex_free_scratch (void *ptr)
+{
+ free(ptr);
+}
+#endif
+
+/** Copy n elements from src to dst. The 0* term provides compile-time type checking */
+#ifndef OVERRIDE_SPEEX_COPY
+#define SPEEX_COPY(dst, src, n) (memcpy((dst), (src), (n)*sizeof(*(dst)) + 0*((dst)-(src)) ))
+#endif
+
+/** Copy n elements from src to dst, allowing overlapping regions. The 0* term
+ provides compile-time type checking */
+#ifndef OVERRIDE_SPEEX_MOVE
+#define SPEEX_MOVE(dst, src, n) (memmove((dst), (src), (n)*sizeof(*(dst)) + 0*((dst)-(src)) ))
+#endif
+
+/** For n elements worth of memory, set every byte to the value of c, starting at address dst */
+#ifndef OVERRIDE_SPEEX_MEMSET
+#define SPEEX_MEMSET(dst, c, n) (memset((dst), (c), (n)*sizeof(*(dst))))
+#endif
+
+
+#ifndef OVERRIDE_SPEEX_FATAL
+static inline void _speex_fatal(const char *str, const char *file, int line)
+{
+ fprintf (stderr, "Fatal (internal) error in %s, line %d: %s\n", file, line, str);
+ exit(1);
+}
+#endif
+
+#ifndef OVERRIDE_SPEEX_WARNING
+static inline void speex_warning(const char *str)
+{
+#ifndef DISABLE_WARNINGS
+ fprintf (stderr, "warning: %s\n", str);
+#endif
+}
+#endif
+
+#ifndef OVERRIDE_SPEEX_WARNING_INT
+static inline void speex_warning_int(const char *str, int val)
+{
+#ifndef DISABLE_WARNINGS
+ fprintf (stderr, "warning: %s %d\n", str, val);
+#endif
+}
+#endif
+
+#ifndef OVERRIDE_SPEEX_NOTIFY
+static inline void speex_notify(const char *str)
+{
+#ifndef DISABLE_NOTIFICATIONS
+ fprintf (stderr, "notification: %s\n", str);
+#endif
+}
+#endif
+
+#ifndef OVERRIDE_SPEEX_PUTC
+/** Speex wrapper for putc */
+static inline void _speex_putc(int ch, void *file)
+{
+ FILE *f = (FILE *)file;
+ fprintf(f, "%c", ch);
+}
+#endif
+
+#define speex_fatal(str) _speex_fatal(str, __FILE__, __LINE__);
+#define speex_assert(cond) {if (!(cond)) {speex_fatal("assertion failed: " #cond);}}
+
+#ifndef RELEASE
+static inline void print_vec(float *vec, int len, char *name)
+{
+ int i;
+ printf ("%s ", name);
+ for (i=0;i<len;i++)
+ printf (" %f", vec[i]);
+ printf ("\n");
+}
+#endif
+
+#endif
+
diff --git a/lib/speexdsp/libspeexdsp/preprocess.c b/lib/speexdsp/libspeexdsp/preprocess.c
new file mode 100644
index 00000000..3053eb5a
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/preprocess.c
@@ -0,0 +1,1215 @@
+/* Copyright (C) 2003 Epic Games (written by Jean-Marc Valin)
+ Copyright (C) 2004-2006 Epic Games
+
+ File: preprocess.c
+ Preprocessor with denoising based on the algorithm by Ephraim and Malah
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. 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.
+
+ 3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+*/
+
+
+/*
+ Recommended papers:
+
+ Y. Ephraim and D. Malah, "Speech enhancement using minimum mean-square error
+ short-time spectral amplitude estimator". IEEE Transactions on Acoustics,
+ Speech and Signal Processing, vol. ASSP-32, no. 6, pp. 1109-1121, 1984.
+
+ Y. Ephraim and D. Malah, "Speech enhancement using minimum mean-square error
+ log-spectral amplitude estimator". IEEE Transactions on Acoustics, Speech and
+ Signal Processing, vol. ASSP-33, no. 2, pp. 443-445, 1985.
+
+ I. Cohen and B. Berdugo, "Speech enhancement for non-stationary noise environments".
+ Signal Processing, vol. 81, no. 2, pp. 2403-2418, 2001.
+
+ Stefan Gustafsson, Rainer Martin, Peter Jax, and Peter Vary. "A psychoacoustic
+ approach to combined acoustic echo cancellation and noise reduction". IEEE
+ Transactions on Speech and Audio Processing, 2002.
+
+ J.-M. Valin, J. Rouat, and F. Michaud, "Microphone array post-filter for separation
+ of simultaneous non-stationary sources". In Proceedings IEEE International
+ Conference on Acoustics, Speech, and Signal Processing, 2004.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <math.h>
+#include "speex/speex_preprocess.h"
+#include "speex/speex_echo.h"
+#include "arch.h"
+#include "fftwrap.h"
+#include "filterbank.h"
+#include "math_approx.h"
+#include "os_support.h"
+
+#define LOUDNESS_EXP 5.f
+#define AMP_SCALE .001f
+#define AMP_SCALE_1 1000.f
+
+#define NB_BANDS 24
+
+#define SPEECH_PROB_START_DEFAULT QCONST16(0.35f,15)
+#define SPEECH_PROB_CONTINUE_DEFAULT QCONST16(0.20f,15)
+#define NOISE_SUPPRESS_DEFAULT -15
+#define ECHO_SUPPRESS_DEFAULT -40
+#define ECHO_SUPPRESS_ACTIVE_DEFAULT -15
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#define SQR(x) ((x)*(x))
+#define SQR16(x) (MULT16_16((x),(x)))
+#define SQR16_Q15(x) (MULT16_16_Q15((x),(x)))
+
+#ifdef FIXED_POINT
+static inline spx_word16_t DIV32_16_Q8(spx_word32_t a, spx_word32_t b)
+{
+ if (SHR32(a,7) >= b)
+ {
+ return 32767;
+ } else {
+ if (b>=QCONST32(1,23))
+ {
+ a = SHR32(a,8);
+ b = SHR32(b,8);
+ }
+ if (b>=QCONST32(1,19))
+ {
+ a = SHR32(a,4);
+ b = SHR32(b,4);
+ }
+ if (b>=QCONST32(1,15))
+ {
+ a = SHR32(a,4);
+ b = SHR32(b,4);
+ }
+ a = SHL32(a,8);
+ return PDIV32_16(a,b);
+ }
+
+}
+static inline spx_word16_t DIV32_16_Q15(spx_word32_t a, spx_word32_t b)
+{
+ if (SHR32(a,15) >= b)
+ {
+ return 32767;
+ } else {
+ if (b>=QCONST32(1,23))
+ {
+ a = SHR32(a,8);
+ b = SHR32(b,8);
+ }
+ if (b>=QCONST32(1,19))
+ {
+ a = SHR32(a,4);
+ b = SHR32(b,4);
+ }
+ if (b>=QCONST32(1,15))
+ {
+ a = SHR32(a,4);
+ b = SHR32(b,4);
+ }
+ a = SHL32(a,15)-a;
+ return DIV32_16(a,b);
+ }
+}
+#define SNR_SCALING 256.f
+#define SNR_SCALING_1 0.0039062f
+#define SNR_SHIFT 8
+
+#define FRAC_SCALING 32767.f
+#define FRAC_SCALING_1 3.0518e-05
+#define FRAC_SHIFT 1
+
+#define EXPIN_SCALING 2048.f
+#define EXPIN_SCALING_1 0.00048828f
+#define EXPIN_SHIFT 11
+#define EXPOUT_SCALING_1 1.5259e-05
+
+#define NOISE_SHIFT 7
+
+#else
+
+#define DIV32_16_Q8(a,b) ((a)/(b))
+#define DIV32_16_Q15(a,b) ((a)/(b))
+#define SNR_SCALING 1.f
+#define SNR_SCALING_1 1.f
+#define SNR_SHIFT 0
+#define FRAC_SCALING 1.f
+#define FRAC_SCALING_1 1.f
+#define FRAC_SHIFT 0
+#define NOISE_SHIFT 0
+
+#define EXPIN_SCALING 1.f
+#define EXPIN_SCALING_1 1.f
+#define EXPOUT_SCALING_1 1.f
+
+#endif
+
+/** Speex pre-processor state. */
+struct SpeexPreprocessState_ {
+ /* Basic info */
+ int frame_size; /**< Number of samples processed each time */
+ int ps_size; /**< Number of points in the power spectrum */
+ int sampling_rate; /**< Sampling rate of the input/output */
+ int nbands;
+ FilterBank *bank;
+
+ /* Parameters */
+ int denoise_enabled;
+ int vad_enabled;
+ int dereverb_enabled;
+ spx_word16_t reverb_decay;
+ spx_word16_t reverb_level;
+ spx_word16_t speech_prob_start;
+ spx_word16_t speech_prob_continue;
+ int noise_suppress;
+ int echo_suppress;
+ int echo_suppress_active;
+ SpeexEchoState *echo_state;
+
+ spx_word16_t speech_prob; /**< Probability last frame was speech */
+
+ /* DSP-related arrays */
+ spx_word16_t *frame; /**< Processing frame (2*ps_size) */
+ spx_word16_t *ft; /**< Processing frame in freq domain (2*ps_size) */
+ spx_word32_t *ps; /**< Current power spectrum */
+ spx_word16_t *gain2; /**< Adjusted gains */
+ spx_word16_t *gain_floor; /**< Minimum gain allowed */
+ spx_word16_t *window; /**< Analysis/Synthesis window */
+ spx_word32_t *noise; /**< Noise estimate */
+ spx_word32_t *reverb_estimate; /**< Estimate of reverb energy */
+ spx_word32_t *old_ps; /**< Power spectrum for last frame */
+ spx_word16_t *gain; /**< Ephraim Malah gain */
+ spx_word16_t *prior; /**< A-priori SNR */
+ spx_word16_t *post; /**< A-posteriori SNR */
+
+ spx_word32_t *S; /**< Smoothed power spectrum */
+ spx_word32_t *Smin; /**< See Cohen paper */
+ spx_word32_t *Stmp; /**< See Cohen paper */
+ int *update_prob; /**< Probability of speech presence for noise update */
+
+ spx_word16_t *zeta; /**< Smoothed a priori SNR */
+ spx_word32_t *echo_noise;
+ spx_word32_t *residual_echo;
+
+ /* Misc */
+ spx_word16_t *inbuf; /**< Input buffer (overlapped analysis) */
+ spx_word16_t *outbuf; /**< Output buffer (for overlap and add) */
+
+ /* AGC stuff, only for floating point for now */
+#ifndef FIXED_POINT
+ int agc_enabled;
+ float agc_level;
+ float loudness_accum;
+ float *loudness_weight; /**< Perceptual loudness curve */
+ float loudness; /**< Loudness estimate */
+ float agc_gain; /**< Current AGC gain */
+ float max_gain; /**< Maximum gain allowed */
+ float max_increase_step; /**< Maximum increase in gain from one frame to another */
+ float max_decrease_step; /**< Maximum decrease in gain from one frame to another */
+ float prev_loudness; /**< Loudness of previous frame */
+ float init_max; /**< Current gain limit during initialisation */
+#endif
+ int nb_adapt; /**< Number of frames used for adaptation so far */
+ int was_speech;
+ int min_count; /**< Number of frames processed so far */
+ void *fft_lookup; /**< Lookup table for the FFT */
+#ifdef FIXED_POINT
+ int frame_shift;
+#endif
+};
+
+
+static void conj_window(spx_word16_t *w, int len)
+{
+ int i;
+ for (i=0;i<len;i++)
+ {
+ spx_word16_t tmp;
+#ifdef FIXED_POINT
+ spx_word16_t x = DIV32_16(MULT16_16(32767,i),len);
+#else
+ spx_word16_t x = DIV32_16(MULT16_16(QCONST16(4.f,13),i),len);
+#endif
+ int inv=0;
+ if (x<QCONST16(1.f,13))
+ {
+ } else if (x<QCONST16(2.f,13))
+ {
+ x=QCONST16(2.f,13)-x;
+ inv=1;
+ } else if (x<QCONST16(3.f,13))
+ {
+ x=x-QCONST16(2.f,13);
+ inv=1;
+ } else {
+ x=QCONST16(2.f,13)-x+QCONST16(2.f,13); /* 4 - x */
+ }
+ x = MULT16_16_Q14(QCONST16(1.271903f,14), x);
+ tmp = SQR16_Q15(QCONST16(.5f,15)-MULT16_16_P15(QCONST16(.5f,15),spx_cos_norm(SHL32(EXTEND32(x),2))));
+ if (inv)
+ tmp=SUB16(Q15_ONE,tmp);
+ w[i]=spx_sqrt(SHL32(EXTEND32(tmp),15));
+ }
+}
+
+
+#ifdef FIXED_POINT
+/* This function approximates the gain function
+ y = gamma(1.25)^2 * M(-.25;1;-x) / sqrt(x)
+ which multiplied by xi/(1+xi) is the optimal gain
+ in the loudness domain ( sqrt[amplitude] )
+ Input in Q11 format, output in Q15
+*/
+static inline spx_word32_t hypergeom_gain(spx_word32_t xx)
+{
+ int ind;
+ spx_word16_t frac;
+ /* Q13 table */
+ static const spx_word16_t table[21] = {
+ 6730, 8357, 9868, 11267, 12563, 13770, 14898,
+ 15959, 16961, 17911, 18816, 19682, 20512, 21311,
+ 22082, 22827, 23549, 24250, 24931, 25594, 26241};
+ ind = SHR32(xx,10);
+ if (ind<0)
+ return Q15_ONE;
+ if (ind>19)
+ return ADD32(EXTEND32(Q15_ONE),EXTEND32(DIV32_16(QCONST32(.1296,23), SHR32(xx,EXPIN_SHIFT-SNR_SHIFT))));
+ frac = SHL32(xx-SHL32(ind,10),5);
+ return SHL32(DIV32_16(PSHR32(MULT16_16(Q15_ONE-frac,table[ind]) + MULT16_16(frac,table[ind+1]),7),(spx_sqrt(SHL32(xx,15)+6711))),7);
+}
+
+static inline spx_word16_t qcurve(spx_word16_t x)
+{
+ x = MAX16(x, 1);
+ return DIV32_16(SHL32(EXTEND32(32767),9),ADD16(512,MULT16_16_Q15(QCONST16(.60f,15),DIV32_16(32767,x))));
+}
+
+/* Compute the gain floor based on different floors for the background noise and residual echo */
+static void compute_gain_floor(int noise_suppress, int effective_echo_suppress, spx_word32_t *noise, spx_word32_t *echo, spx_word16_t *gain_floor, int len)
+{
+ int i;
+
+ if (noise_suppress > effective_echo_suppress)
+ {
+ spx_word16_t noise_gain, gain_ratio;
+ noise_gain = EXTRACT16(MIN32(Q15_ONE,SHR32(spx_exp(MULT16_16(QCONST16(0.11513,11),noise_suppress)),1)));
+ gain_ratio = EXTRACT16(MIN32(Q15_ONE,SHR32(spx_exp(MULT16_16(QCONST16(.2302585f,11),effective_echo_suppress-noise_suppress)),1)));
+
+ /* gain_floor = sqrt [ (noise*noise_floor + echo*echo_floor) / (noise+echo) ] */
+ for (i=0;i<len;i++)
+ gain_floor[i] = MULT16_16_Q15(noise_gain,
+ spx_sqrt(SHL32(EXTEND32(DIV32_16_Q15(PSHR32(noise[i],NOISE_SHIFT) + MULT16_32_Q15(gain_ratio,echo[i]),
+ (1+PSHR32(noise[i],NOISE_SHIFT) + echo[i]) )),15)));
+ } else {
+ spx_word16_t echo_gain, gain_ratio;
+ echo_gain = EXTRACT16(MIN32(Q15_ONE,SHR32(spx_exp(MULT16_16(QCONST16(0.11513,11),effective_echo_suppress)),1)));
+ gain_ratio = EXTRACT16(MIN32(Q15_ONE,SHR32(spx_exp(MULT16_16(QCONST16(.2302585f,11),noise_suppress-effective_echo_suppress)),1)));
+
+ /* gain_floor = sqrt [ (noise*noise_floor + echo*echo_floor) / (noise+echo) ] */
+ for (i=0;i<len;i++)
+ gain_floor[i] = MULT16_16_Q15(echo_gain,
+ spx_sqrt(SHL32(EXTEND32(DIV32_16_Q15(MULT16_32_Q15(gain_ratio,PSHR32(noise[i],NOISE_SHIFT)) + echo[i],
+ (1+PSHR32(noise[i],NOISE_SHIFT) + echo[i]) )),15)));
+ }
+}
+
+#else
+/* This function approximates the gain function
+ y = gamma(1.25)^2 * M(-.25;1;-x) / sqrt(x)
+ which multiplied by xi/(1+xi) is the optimal gain
+ in the loudness domain ( sqrt[amplitude] )
+*/
+static inline spx_word32_t hypergeom_gain(spx_word32_t xx)
+{
+ int ind;
+ float integer, frac;
+ float x;
+ static const float table[21] = {
+ 0.82157f, 1.02017f, 1.20461f, 1.37534f, 1.53363f, 1.68092f, 1.81865f,
+ 1.94811f, 2.07038f, 2.18638f, 2.29688f, 2.40255f, 2.50391f, 2.60144f,
+ 2.69551f, 2.78647f, 2.87458f, 2.96015f, 3.04333f, 3.12431f, 3.20326f};
+ x = EXPIN_SCALING_1*xx;
+ integer = floor(2*x);
+ ind = (int)integer;
+ if (ind<0)
+ return FRAC_SCALING;
+ if (ind>19)
+ return FRAC_SCALING*(1+.1296/x);
+ frac = 2*x-integer;
+ return FRAC_SCALING*((1-frac)*table[ind] + frac*table[ind+1])/sqrt(x+.0001f);
+}
+
+static inline spx_word16_t qcurve(spx_word16_t x)
+{
+ return 1.f/(1.f+.15f/(SNR_SCALING_1*x));
+}
+
+static void compute_gain_floor(int noise_suppress, int effective_echo_suppress, spx_word32_t *noise, spx_word32_t *echo, spx_word16_t *gain_floor, int len)
+{
+ int i;
+ float echo_floor;
+ float noise_floor;
+
+ noise_floor = exp(.2302585f*noise_suppress);
+ echo_floor = exp(.2302585f*effective_echo_suppress);
+
+ /* Compute the gain floor based on different floors for the background noise and residual echo */
+ for (i=0;i<len;i++)
+ gain_floor[i] = FRAC_SCALING*sqrt(noise_floor*PSHR32(noise[i],NOISE_SHIFT) + echo_floor*echo[i])/sqrt(1+PSHR32(noise[i],NOISE_SHIFT) + echo[i]);
+}
+
+#endif
+EXPORT SpeexPreprocessState *speex_preprocess_state_init(int frame_size, int sampling_rate)
+{
+ int i;
+ int N, N3, N4, M;
+
+ SpeexPreprocessState *st = (SpeexPreprocessState *)speex_alloc(sizeof(SpeexPreprocessState));
+ st->frame_size = frame_size;
+
+ /* Round ps_size down to the nearest power of two */
+#if 0
+ i=1;
+ st->ps_size = st->frame_size;
+ while(1)
+ {
+ if (st->ps_size & ~i)
+ {
+ st->ps_size &= ~i;
+ i<<=1;
+ } else {
+ break;
+ }
+ }
+
+
+ if (st->ps_size < 3*st->frame_size/4)
+ st->ps_size = st->ps_size * 3 / 2;
+#else
+ st->ps_size = st->frame_size;
+#endif
+
+ N = st->ps_size;
+ N3 = 2*N - st->frame_size;
+ N4 = st->frame_size - N3;
+
+ st->sampling_rate = sampling_rate;
+ st->denoise_enabled = 1;
+ st->vad_enabled = 0;
+ st->dereverb_enabled = 0;
+ st->reverb_decay = 0;
+ st->reverb_level = 0;
+ st->noise_suppress = NOISE_SUPPRESS_DEFAULT;
+ st->echo_suppress = ECHO_SUPPRESS_DEFAULT;
+ st->echo_suppress_active = ECHO_SUPPRESS_ACTIVE_DEFAULT;
+
+ st->speech_prob_start = SPEECH_PROB_START_DEFAULT;
+ st->speech_prob_continue = SPEECH_PROB_CONTINUE_DEFAULT;
+
+ st->echo_state = NULL;
+
+ st->nbands = NB_BANDS;
+ M = st->nbands;
+ st->bank = filterbank_new(M, sampling_rate, N, 1);
+
+ st->frame = (spx_word16_t*)speex_alloc(2*N*sizeof(spx_word16_t));
+ st->window = (spx_word16_t*)speex_alloc(2*N*sizeof(spx_word16_t));
+ st->ft = (spx_word16_t*)speex_alloc(2*N*sizeof(spx_word16_t));
+
+ st->ps = (spx_word32_t*)speex_alloc((N+M)*sizeof(spx_word32_t));
+ st->noise = (spx_word32_t*)speex_alloc((N+M)*sizeof(spx_word32_t));
+ st->echo_noise = (spx_word32_t*)speex_alloc((N+M)*sizeof(spx_word32_t));
+ st->residual_echo = (spx_word32_t*)speex_alloc((N+M)*sizeof(spx_word32_t));
+ st->reverb_estimate = (spx_word32_t*)speex_alloc((N+M)*sizeof(spx_word32_t));
+ st->old_ps = (spx_word32_t*)speex_alloc((N+M)*sizeof(spx_word32_t));
+ st->prior = (spx_word16_t*)speex_alloc((N+M)*sizeof(spx_word16_t));
+ st->post = (spx_word16_t*)speex_alloc((N+M)*sizeof(spx_word16_t));
+ st->gain = (spx_word16_t*)speex_alloc((N+M)*sizeof(spx_word16_t));
+ st->gain2 = (spx_word16_t*)speex_alloc((N+M)*sizeof(spx_word16_t));
+ st->gain_floor = (spx_word16_t*)speex_alloc((N+M)*sizeof(spx_word16_t));
+ st->zeta = (spx_word16_t*)speex_alloc((N+M)*sizeof(spx_word16_t));
+
+ st->S = (spx_word32_t*)speex_alloc(N*sizeof(spx_word32_t));
+ st->Smin = (spx_word32_t*)speex_alloc(N*sizeof(spx_word32_t));
+ st->Stmp = (spx_word32_t*)speex_alloc(N*sizeof(spx_word32_t));
+ st->update_prob = (int*)speex_alloc(N*sizeof(int));
+
+ st->inbuf = (spx_word16_t*)speex_alloc(N3*sizeof(spx_word16_t));
+ st->outbuf = (spx_word16_t*)speex_alloc(N3*sizeof(spx_word16_t));
+
+ conj_window(st->window, 2*N3);
+ for (i=2*N3;i<2*st->ps_size;i++)
+ st->window[i]=Q15_ONE;
+
+ if (N4>0)
+ {
+ for (i=N3-1;i>=0;i--)
+ {
+ st->window[i+N3+N4]=st->window[i+N3];
+ st->window[i+N3]=1;
+ }
+ }
+ for (i=0;i<N+M;i++)
+ {
+ st->noise[i]=QCONST32(1.f,NOISE_SHIFT);
+ st->reverb_estimate[i]=0;
+ st->old_ps[i]=1;
+ st->gain[i]=Q15_ONE;
+ st->post[i]=SHL16(1, SNR_SHIFT);
+ st->prior[i]=SHL16(1, SNR_SHIFT);
+ }
+
+ for (i=0;i<N;i++)
+ st->update_prob[i] = 1;
+ for (i=0;i<N3;i++)
+ {
+ st->inbuf[i]=0;
+ st->outbuf[i]=0;
+ }
+#ifndef FIXED_POINT
+ st->agc_enabled = 0;
+ st->agc_level = 8000;
+ st->loudness_weight = (float*)speex_alloc(N*sizeof(float));
+ for (i=0;i<N;i++)
+ {
+ float ff=((float)i)*.5*sampling_rate/((float)N);
+ /*st->loudness_weight[i] = .5f*(1.f/(1.f+ff/8000.f))+1.f*exp(-.5f*(ff-3800.f)*(ff-3800.f)/9e5f);*/
+ st->loudness_weight[i] = .35f-.35f*ff/16000.f+.73f*exp(-.5f*(ff-3800)*(ff-3800)/9e5f);
+ if (st->loudness_weight[i]<.01f)
+ st->loudness_weight[i]=.01f;
+ st->loudness_weight[i] *= st->loudness_weight[i];
+ }
+ /*st->loudness = pow(AMP_SCALE*st->agc_level,LOUDNESS_EXP);*/
+ st->loudness = 1e-15;
+ st->agc_gain = 1;
+ st->max_gain = 30;
+ st->max_increase_step = exp(0.11513f * 12.*st->frame_size / st->sampling_rate);
+ st->max_decrease_step = exp(-0.11513f * 40.*st->frame_size / st->sampling_rate);
+ st->prev_loudness = 1;
+ st->init_max = 1;
+#endif
+ st->was_speech = 0;
+
+ st->fft_lookup = spx_fft_init(2*N);
+
+ st->nb_adapt=0;
+ st->min_count=0;
+ return st;
+}
+
+EXPORT void speex_preprocess_state_destroy(SpeexPreprocessState *st)
+{
+ speex_free(st->frame);
+ speex_free(st->ft);
+ speex_free(st->ps);
+ speex_free(st->gain2);
+ speex_free(st->gain_floor);
+ speex_free(st->window);
+ speex_free(st->noise);
+ speex_free(st->reverb_estimate);
+ speex_free(st->old_ps);
+ speex_free(st->gain);
+ speex_free(st->prior);
+ speex_free(st->post);
+#ifndef FIXED_POINT
+ speex_free(st->loudness_weight);
+#endif
+ speex_free(st->echo_noise);
+ speex_free(st->residual_echo);
+
+ speex_free(st->S);
+ speex_free(st->Smin);
+ speex_free(st->Stmp);
+ speex_free(st->update_prob);
+ speex_free(st->zeta);
+
+ speex_free(st->inbuf);
+ speex_free(st->outbuf);
+
+ spx_fft_destroy(st->fft_lookup);
+ filterbank_destroy(st->bank);
+ speex_free(st);
+}
+
+/* FIXME: The AGC doesn't work yet with fixed-point*/
+#ifndef FIXED_POINT
+static void speex_compute_agc(SpeexPreprocessState *st, spx_word16_t Pframe, spx_word16_t *ft)
+{
+ int i;
+ int N = st->ps_size;
+ float target_gain;
+ float loudness=1.f;
+ float rate;
+
+ for (i=2;i<N;i++)
+ {
+ loudness += 2.f*N*st->ps[i]* st->loudness_weight[i];
+ }
+ loudness=sqrt(loudness);
+ /*if (loudness < 2*pow(st->loudness, 1.0/LOUDNESS_EXP) &&
+ loudness*2 > pow(st->loudness, 1.0/LOUDNESS_EXP))*/
+ if (Pframe>.3f)
+ {
+ /*rate=2.0f*Pframe*Pframe/(1+st->nb_loudness_adapt);*/
+ rate = .03*Pframe*Pframe;
+ st->loudness = (1-rate)*st->loudness + (rate)*pow(AMP_SCALE*loudness, LOUDNESS_EXP);
+ st->loudness_accum = (1-rate)*st->loudness_accum + rate;
+ if (st->init_max < st->max_gain && st->nb_adapt > 20)
+ st->init_max *= 1.f + .1f*Pframe*Pframe;
+ }
+ /*printf ("%f %f %f %f\n", Pframe, loudness, pow(st->loudness, 1.0f/LOUDNESS_EXP), st->loudness2);*/
+
+ target_gain = AMP_SCALE*st->agc_level*pow(st->loudness/(1e-4+st->loudness_accum), -1.0f/LOUDNESS_EXP);
+
+ if ((Pframe>.5 && st->nb_adapt > 20) || target_gain < st->agc_gain)
+ {
+ if (target_gain > st->max_increase_step*st->agc_gain)
+ target_gain = st->max_increase_step*st->agc_gain;
+ if (target_gain < st->max_decrease_step*st->agc_gain && loudness < 10*st->prev_loudness)
+ target_gain = st->max_decrease_step*st->agc_gain;
+ if (target_gain > st->max_gain)
+ target_gain = st->max_gain;
+ if (target_gain > st->init_max)
+ target_gain = st->init_max;
+
+ st->agc_gain = target_gain;
+ }
+ /*fprintf (stderr, "%f %f %f\n", loudness, (float)AMP_SCALE_1*pow(st->loudness, 1.0f/LOUDNESS_EXP), st->agc_gain);*/
+
+ for (i=0;i<2*N;i++)
+ ft[i] *= st->agc_gain;
+ st->prev_loudness = loudness;
+}
+#endif
+
+static void preprocess_analysis(SpeexPreprocessState *st, spx_int16_t *x)
+{
+ int i;
+ int N = st->ps_size;
+ int N3 = 2*N - st->frame_size;
+ int N4 = st->frame_size - N3;
+ spx_word32_t *ps=st->ps;
+
+ /* 'Build' input frame */
+ for (i=0;i<N3;i++)
+ st->frame[i]=st->inbuf[i];
+ for (i=0;i<st->frame_size;i++)
+ st->frame[N3+i]=x[i];
+
+ /* Update inbuf */
+ for (i=0;i<N3;i++)
+ st->inbuf[i]=x[N4+i];
+
+ /* Windowing */
+ for (i=0;i<2*N;i++)
+ st->frame[i] = MULT16_16_Q15(st->frame[i], st->window[i]);
+
+#ifdef FIXED_POINT
+ {
+ spx_word16_t max_val=0;
+ for (i=0;i<2*N;i++)
+ max_val = MAX16(max_val, ABS16(st->frame[i]));
+ st->frame_shift = 14-spx_ilog2(EXTEND32(max_val));
+ for (i=0;i<2*N;i++)
+ st->frame[i] = SHL16(st->frame[i], st->frame_shift);
+ }
+#endif
+
+ /* Perform FFT */
+ spx_fft(st->fft_lookup, st->frame, st->ft);
+
+ /* Power spectrum */
+ ps[0]=MULT16_16(st->ft[0],st->ft[0]);
+ for (i=1;i<N;i++)
+ ps[i]=MULT16_16(st->ft[2*i-1],st->ft[2*i-1]) + MULT16_16(st->ft[2*i],st->ft[2*i]);
+ for (i=0;i<N;i++)
+ st->ps[i] = PSHR32(st->ps[i], 2*st->frame_shift);
+
+ filterbank_compute_bank32(st->bank, ps, ps+N);
+}
+
+static void update_noise_prob(SpeexPreprocessState *st)
+{
+ int i;
+ int min_range;
+ int N = st->ps_size;
+
+ for (i=1;i<N-1;i++)
+ st->S[i] = MULT16_32_Q15(QCONST16(.8f,15),st->S[i]) + MULT16_32_Q15(QCONST16(.05f,15),st->ps[i-1])
+ + MULT16_32_Q15(QCONST16(.1f,15),st->ps[i]) + MULT16_32_Q15(QCONST16(.05f,15),st->ps[i+1]);
+ st->S[0] = MULT16_32_Q15(QCONST16(.8f,15),st->S[0]) + MULT16_32_Q15(QCONST16(.2f,15),st->ps[0]);
+ st->S[N-1] = MULT16_32_Q15(QCONST16(.8f,15),st->S[N-1]) + MULT16_32_Q15(QCONST16(.2f,15),st->ps[N-1]);
+
+ if (st->nb_adapt==1)
+ {
+ for (i=0;i<N;i++)
+ st->Smin[i] = st->Stmp[i] = 0;
+ }
+
+ if (st->nb_adapt < 100)
+ min_range = 15;
+ else if (st->nb_adapt < 1000)
+ min_range = 50;
+ else if (st->nb_adapt < 10000)
+ min_range = 150;
+ else
+ min_range = 300;
+ if (st->min_count > min_range)
+ {
+ st->min_count = 0;
+ for (i=0;i<N;i++)
+ {
+ st->Smin[i] = MIN32(st->Stmp[i], st->S[i]);
+ st->Stmp[i] = st->S[i];
+ }
+ } else {
+ for (i=0;i<N;i++)
+ {
+ st->Smin[i] = MIN32(st->Smin[i], st->S[i]);
+ st->Stmp[i] = MIN32(st->Stmp[i], st->S[i]);
+ }
+ }
+ for (i=0;i<N;i++)
+ {
+ if (MULT16_32_Q15(QCONST16(.4f,15),st->S[i]) > st->Smin[i])
+ st->update_prob[i] = 1;
+ else
+ st->update_prob[i] = 0;
+ /*fprintf (stderr, "%f ", st->S[i]/st->Smin[i]);*/
+ /*fprintf (stderr, "%f ", st->update_prob[i]);*/
+ }
+
+}
+
+#define NOISE_OVERCOMPENS 1.
+
+void speex_echo_get_residual(SpeexEchoState *st, spx_word32_t *Yout, int len);
+
+EXPORT int speex_preprocess(SpeexPreprocessState *st, spx_int16_t *x, spx_int32_t *echo)
+{
+ return speex_preprocess_run(st, x);
+}
+
+EXPORT int speex_preprocess_run(SpeexPreprocessState *st, spx_int16_t *x)
+{
+ int i;
+ int M;
+ int N = st->ps_size;
+ int N3 = 2*N - st->frame_size;
+ int N4 = st->frame_size - N3;
+ spx_word32_t *ps=st->ps;
+ spx_word32_t Zframe;
+ spx_word16_t Pframe;
+ spx_word16_t beta, beta_1;
+ spx_word16_t effective_echo_suppress;
+
+ st->nb_adapt++;
+ if (st->nb_adapt>20000)
+ st->nb_adapt = 20000;
+ st->min_count++;
+
+ beta = MAX16(QCONST16(.03,15),DIV32_16(Q15_ONE,st->nb_adapt));
+ beta_1 = Q15_ONE-beta;
+ M = st->nbands;
+ /* Deal with residual echo if provided */
+ if (st->echo_state)
+ {
+ speex_echo_get_residual(st->echo_state, st->residual_echo, N);
+#ifndef FIXED_POINT
+ /* If there are NaNs or ridiculous values, it'll show up in the DC and we just reset everything to zero */
+ if (!(st->residual_echo[0] >=0 && st->residual_echo[0]<N*1e9f))
+ {
+ for (i=0;i<N;i++)
+ st->residual_echo[i] = 0;
+ }
+#endif
+ for (i=0;i<N;i++)
+ st->echo_noise[i] = MAX32(MULT16_32_Q15(QCONST16(.6f,15),st->echo_noise[i]), st->residual_echo[i]);
+ filterbank_compute_bank32(st->bank, st->echo_noise, st->echo_noise+N);
+ } else {
+ for (i=0;i<N+M;i++)
+ st->echo_noise[i] = 0;
+ }
+ preprocess_analysis(st, x);
+
+ update_noise_prob(st);
+
+ /* Noise estimation always updated for the 10 first frames */
+ /*if (st->nb_adapt<10)
+ {
+ for (i=1;i<N-1;i++)
+ st->update_prob[i] = 0;
+ }
+ */
+
+ /* Update the noise estimate for the frequencies where it can be */
+ for (i=0;i<N;i++)
+ {
+ if (!st->update_prob[i] || st->ps[i] < PSHR32(st->noise[i], NOISE_SHIFT))
+ st->noise[i] = MAX32(EXTEND32(0),MULT16_32_Q15(beta_1,st->noise[i]) + MULT16_32_Q15(beta,SHL32(st->ps[i],NOISE_SHIFT)));
+ }
+ filterbank_compute_bank32(st->bank, st->noise, st->noise+N);
+
+ /* Special case for first frame */
+ if (st->nb_adapt==1)
+ for (i=0;i<N+M;i++)
+ st->old_ps[i] = ps[i];
+
+ /* Compute a posteriori SNR */
+ for (i=0;i<N+M;i++)
+ {
+ spx_word16_t gamma;
+
+ /* Total noise estimate including residual echo and reverberation */
+ spx_word32_t tot_noise = ADD32(ADD32(ADD32(EXTEND32(1), PSHR32(st->noise[i],NOISE_SHIFT)) , st->echo_noise[i]) , st->reverb_estimate[i]);
+
+ /* A posteriori SNR = ps/noise - 1*/
+ st->post[i] = SUB16(DIV32_16_Q8(ps[i],tot_noise), QCONST16(1.f,SNR_SHIFT));
+ st->post[i]=MIN16(st->post[i], QCONST16(100.f,SNR_SHIFT));
+
+ /* Computing update gamma = .1 + .9*(old/(old+noise))^2 */
+ gamma = QCONST16(.1f,15)+MULT16_16_Q15(QCONST16(.89f,15),SQR16_Q15(DIV32_16_Q15(st->old_ps[i],ADD32(st->old_ps[i],tot_noise))));
+
+ /* A priori SNR update = gamma*max(0,post) + (1-gamma)*old/noise */
+ st->prior[i] = EXTRACT16(PSHR32(ADD32(MULT16_16(gamma,MAX16(0,st->post[i])), MULT16_16(Q15_ONE-gamma,DIV32_16_Q8(st->old_ps[i],tot_noise))), 15));
+ st->prior[i]=MIN16(st->prior[i], QCONST16(100.f,SNR_SHIFT));
+ }
+
+ /*print_vec(st->post, N+M, "");*/
+
+ /* Recursive average of the a priori SNR. A bit smoothed for the psd components */
+ st->zeta[0] = PSHR32(ADD32(MULT16_16(QCONST16(.7f,15),st->zeta[0]), MULT16_16(QCONST16(.3f,15),st->prior[0])),15);
+ for (i=1;i<N-1;i++)
+ st->zeta[i] = PSHR32(ADD32(ADD32(ADD32(MULT16_16(QCONST16(.7f,15),st->zeta[i]), MULT16_16(QCONST16(.15f,15),st->prior[i])),
+ MULT16_16(QCONST16(.075f,15),st->prior[i-1])), MULT16_16(QCONST16(.075f,15),st->prior[i+1])),15);
+ for (i=N-1;i<N+M;i++)
+ st->zeta[i] = PSHR32(ADD32(MULT16_16(QCONST16(.7f,15),st->zeta[i]), MULT16_16(QCONST16(.3f,15),st->prior[i])),15);
+
+ /* Speech probability of presence for the entire frame is based on the average filterbank a priori SNR */
+ Zframe = 0;
+ for (i=N;i<N+M;i++)
+ Zframe = ADD32(Zframe, EXTEND32(st->zeta[i]));
+ Pframe = QCONST16(.1f,15)+MULT16_16_Q15(QCONST16(.899f,15),qcurve(DIV32_16(Zframe,st->nbands)));
+
+ effective_echo_suppress = EXTRACT16(PSHR32(ADD32(MULT16_16(SUB16(Q15_ONE,Pframe), st->echo_suppress), MULT16_16(Pframe, st->echo_suppress_active)),15));
+
+ compute_gain_floor(st->noise_suppress, effective_echo_suppress, st->noise+N, st->echo_noise+N, st->gain_floor+N, M);
+
+ /* Compute Ephraim & Malah gain speech probability of presence for each critical band (Bark scale)
+ Technically this is actually wrong because the EM gaim assumes a slightly different probability
+ distribution */
+ for (i=N;i<N+M;i++)
+ {
+ /* See EM and Cohen papers*/
+ spx_word32_t theta;
+ /* Gain from hypergeometric function */
+ spx_word32_t MM;
+ /* Weiner filter gain */
+ spx_word16_t prior_ratio;
+ /* a priority probability of speech presence based on Bark sub-band alone */
+ spx_word16_t P1;
+ /* Speech absence a priori probability (considering sub-band and frame) */
+ spx_word16_t q;
+#ifdef FIXED_POINT
+ spx_word16_t tmp;
+#endif
+
+ prior_ratio = PDIV32_16(SHL32(EXTEND32(st->prior[i]), 15), ADD16(st->prior[i], SHL32(1,SNR_SHIFT)));
+ theta = MULT16_32_P15(prior_ratio, QCONST32(1.f,EXPIN_SHIFT)+SHL32(EXTEND32(st->post[i]),EXPIN_SHIFT-SNR_SHIFT));
+
+ MM = hypergeom_gain(theta);
+ /* Gain with bound */
+ st->gain[i] = EXTRACT16(MIN32(Q15_ONE, MULT16_32_Q15(prior_ratio, MM)));
+ /* Save old Bark power spectrum */
+ st->old_ps[i] = MULT16_32_P15(QCONST16(.2f,15),st->old_ps[i]) + MULT16_32_P15(MULT16_16_P15(QCONST16(.8f,15),SQR16_Q15(st->gain[i])),ps[i]);
+
+ P1 = QCONST16(.199f,15)+MULT16_16_Q15(QCONST16(.8f,15),qcurve (st->zeta[i]));
+ q = Q15_ONE-MULT16_16_Q15(Pframe,P1);
+#ifdef FIXED_POINT
+ theta = MIN32(theta, EXTEND32(32767));
+/*Q8*/tmp = MULT16_16_Q15((SHL32(1,SNR_SHIFT)+st->prior[i]),EXTRACT16(MIN32(Q15ONE,SHR32(spx_exp(-EXTRACT16(theta)),1))));
+ tmp = MIN16(QCONST16(3.,SNR_SHIFT), tmp); /* Prevent overflows in the next line*/
+/*Q8*/tmp = EXTRACT16(PSHR32(MULT16_16(PDIV32_16(SHL32(EXTEND32(q),8),(Q15_ONE-q)),tmp),8));
+ st->gain2[i]=DIV32_16(SHL32(EXTEND32(32767),SNR_SHIFT), ADD16(256,tmp));
+#else
+ st->gain2[i]=1/(1.f + (q/(1.f-q))*(1+st->prior[i])*exp(-theta));
+#endif
+ }
+ /* Convert the EM gains and speech prob to linear frequency */
+ filterbank_compute_psd16(st->bank,st->gain2+N, st->gain2);
+ filterbank_compute_psd16(st->bank,st->gain+N, st->gain);
+
+ /* Use 1 for linear gain resolution (best) or 0 for Bark gain resolution (faster) */
+ if (1)
+ {
+ filterbank_compute_psd16(st->bank,st->gain_floor+N, st->gain_floor);
+
+ /* Compute gain according to the Ephraim-Malah algorithm -- linear frequency */
+ for (i=0;i<N;i++)
+ {
+ spx_word32_t MM;
+ spx_word32_t theta;
+ spx_word16_t prior_ratio;
+ spx_word16_t tmp;
+ spx_word16_t p;
+ spx_word16_t g;
+
+ /* Wiener filter gain */
+ prior_ratio = PDIV32_16(SHL32(EXTEND32(st->prior[i]), 15), ADD16(st->prior[i], SHL32(1,SNR_SHIFT)));
+ theta = MULT16_32_P15(prior_ratio, QCONST32(1.f,EXPIN_SHIFT)+SHL32(EXTEND32(st->post[i]),EXPIN_SHIFT-SNR_SHIFT));
+
+ /* Optimal estimator for loudness domain */
+ MM = hypergeom_gain(theta);
+ /* EM gain with bound */
+ g = EXTRACT16(MIN32(Q15_ONE, MULT16_32_Q15(prior_ratio, MM)));
+ /* Interpolated speech probability of presence */
+ p = st->gain2[i];
+
+ /* Constrain the gain to be close to the Bark scale gain */
+ if (MULT16_16_Q15(QCONST16(.333f,15),g) > st->gain[i])
+ g = MULT16_16(3,st->gain[i]);
+ st->gain[i] = g;
+
+ /* Save old power spectrum */
+ st->old_ps[i] = MULT16_32_P15(QCONST16(.2f,15),st->old_ps[i]) + MULT16_32_P15(MULT16_16_P15(QCONST16(.8f,15),SQR16_Q15(st->gain[i])),ps[i]);
+
+ /* Apply gain floor */
+ if (st->gain[i] < st->gain_floor[i])
+ st->gain[i] = st->gain_floor[i];
+
+ /* Exponential decay model for reverberation (unused) */
+ /*st->reverb_estimate[i] = st->reverb_decay*st->reverb_estimate[i] + st->reverb_decay*st->reverb_level*st->gain[i]*st->gain[i]*st->ps[i];*/
+
+ /* Take into account speech probability of presence (loudness domain MMSE estimator) */
+ /* gain2 = [p*sqrt(gain)+(1-p)*sqrt(gain _floor) ]^2 */
+ tmp = MULT16_16_P15(p,spx_sqrt(SHL32(EXTEND32(st->gain[i]),15))) + MULT16_16_P15(SUB16(Q15_ONE,p),spx_sqrt(SHL32(EXTEND32(st->gain_floor[i]),15)));
+ st->gain2[i]=SQR16_Q15(tmp);
+
+ /* Use this if you want a log-domain MMSE estimator instead */
+ /*st->gain2[i] = pow(st->gain[i], p) * pow(st->gain_floor[i],1.f-p);*/
+ }
+ } else {
+ for (i=N;i<N+M;i++)
+ {
+ spx_word16_t tmp;
+ spx_word16_t p = st->gain2[i];
+ st->gain[i] = MAX16(st->gain[i], st->gain_floor[i]);
+ tmp = MULT16_16_P15(p,spx_sqrt(SHL32(EXTEND32(st->gain[i]),15))) + MULT16_16_P15(SUB16(Q15_ONE,p),spx_sqrt(SHL32(EXTEND32(st->gain_floor[i]),15)));
+ st->gain2[i]=SQR16_Q15(tmp);
+ }
+ filterbank_compute_psd16(st->bank,st->gain2+N, st->gain2);
+ }
+
+ /* If noise suppression is off, don't apply the gain (but then why call this in the first place!) */
+ if (!st->denoise_enabled)
+ {
+ for (i=0;i<N+M;i++)
+ st->gain2[i]=Q15_ONE;
+ }
+
+ /* Apply computed gain */
+ for (i=1;i<N;i++)
+ {
+ st->ft[2*i-1] = MULT16_16_P15(st->gain2[i],st->ft[2*i-1]);
+ st->ft[2*i] = MULT16_16_P15(st->gain2[i],st->ft[2*i]);
+ }
+ st->ft[0] = MULT16_16_P15(st->gain2[0],st->ft[0]);
+ st->ft[2*N-1] = MULT16_16_P15(st->gain2[N-1],st->ft[2*N-1]);
+
+ /*FIXME: This *will* not work for fixed-point */
+#ifndef FIXED_POINT
+ if (st->agc_enabled)
+ speex_compute_agc(st, Pframe, st->ft);
+#endif
+
+ /* Inverse FFT with 1/N scaling */
+ spx_ifft(st->fft_lookup, st->ft, st->frame);
+ /* Scale back to original (lower) amplitude */
+ for (i=0;i<2*N;i++)
+ st->frame[i] = PSHR16(st->frame[i], st->frame_shift);
+
+ /*FIXME: This *will* not work for fixed-point */
+#ifndef FIXED_POINT
+ if (st->agc_enabled)
+ {
+ float max_sample=0;
+ for (i=0;i<2*N;i++)
+ if (fabs(st->frame[i])>max_sample)
+ max_sample = fabs(st->frame[i]);
+ if (max_sample>28000.f)
+ {
+ float damp = 28000.f/max_sample;
+ for (i=0;i<2*N;i++)
+ st->frame[i] *= damp;
+ }
+ }
+#endif
+
+ /* Synthesis window (for WOLA) */
+ for (i=0;i<2*N;i++)
+ st->frame[i] = MULT16_16_Q15(st->frame[i], st->window[i]);
+
+ /* Perform overlap and add */
+ for (i=0;i<N3;i++)
+ x[i] = WORD2INT(ADD32(EXTEND32(st->outbuf[i]), EXTEND32(st->frame[i])));
+ for (i=0;i<N4;i++)
+ x[N3+i] = st->frame[N3+i];
+
+ /* Update outbuf */
+ for (i=0;i<N3;i++)
+ st->outbuf[i] = st->frame[st->frame_size+i];
+
+ /* FIXME: This VAD is a kludge */
+ st->speech_prob = Pframe;
+ if (st->vad_enabled)
+ {
+ if (st->speech_prob > st->speech_prob_start || (st->was_speech && st->speech_prob > st->speech_prob_continue))
+ {
+ st->was_speech=1;
+ return 1;
+ } else
+ {
+ st->was_speech=0;
+ return 0;
+ }
+ } else {
+ return 1;
+ }
+}
+
+EXPORT void speex_preprocess_estimate_update(SpeexPreprocessState *st, spx_int16_t *x)
+{
+ int i;
+ int N = st->ps_size;
+ int N3 = 2*N - st->frame_size;
+ int M;
+ spx_word32_t *ps=st->ps;
+
+ M = st->nbands;
+ st->min_count++;
+
+ preprocess_analysis(st, x);
+
+ update_noise_prob(st);
+
+ for (i=1;i<N-1;i++)
+ {
+ if (!st->update_prob[i] || st->ps[i] < PSHR32(st->noise[i],NOISE_SHIFT))
+ {
+ st->noise[i] = MULT16_32_Q15(QCONST16(.95f,15),st->noise[i]) + MULT16_32_Q15(QCONST16(.05f,15),SHL32(st->ps[i],NOISE_SHIFT));
+ }
+ }
+
+ for (i=0;i<N3;i++)
+ st->outbuf[i] = MULT16_16_Q15(x[st->frame_size-N3+i],st->window[st->frame_size+i]);
+
+ /* Save old power spectrum */
+ for (i=0;i<N+M;i++)
+ st->old_ps[i] = ps[i];
+
+ for (i=0;i<N;i++)
+ st->reverb_estimate[i] = MULT16_32_Q15(st->reverb_decay, st->reverb_estimate[i]);
+}
+
+
+EXPORT int speex_preprocess_ctl(SpeexPreprocessState *state, int request, void *ptr)
+{
+ int i;
+ SpeexPreprocessState *st;
+ st=(SpeexPreprocessState*)state;
+ switch(request)
+ {
+ case SPEEX_PREPROCESS_SET_DENOISE:
+ st->denoise_enabled = (*(spx_int32_t*)ptr);
+ break;
+ case SPEEX_PREPROCESS_GET_DENOISE:
+ (*(spx_int32_t*)ptr) = st->denoise_enabled;
+ break;
+#ifndef FIXED_POINT
+ case SPEEX_PREPROCESS_SET_AGC:
+ st->agc_enabled = (*(spx_int32_t*)ptr);
+ break;
+ case SPEEX_PREPROCESS_GET_AGC:
+ (*(spx_int32_t*)ptr) = st->agc_enabled;
+ break;
+#ifndef DISABLE_FLOAT_API
+ case SPEEX_PREPROCESS_SET_AGC_LEVEL:
+ st->agc_level = (*(float*)ptr);
+ if (st->agc_level<1)
+ st->agc_level=1;
+ if (st->agc_level>32768)
+ st->agc_level=32768;
+ break;
+ case SPEEX_PREPROCESS_GET_AGC_LEVEL:
+ (*(float*)ptr) = st->agc_level;
+ break;
+#endif /* #ifndef DISABLE_FLOAT_API */
+ case SPEEX_PREPROCESS_SET_AGC_INCREMENT:
+ st->max_increase_step = exp(0.11513f * (*(spx_int32_t*)ptr)*st->frame_size / st->sampling_rate);
+ break;
+ case SPEEX_PREPROCESS_GET_AGC_INCREMENT:
+ (*(spx_int32_t*)ptr) = floor(.5+8.6858*log(st->max_increase_step)*st->sampling_rate/st->frame_size);
+ break;
+ case SPEEX_PREPROCESS_SET_AGC_DECREMENT:
+ st->max_decrease_step = exp(0.11513f * (*(spx_int32_t*)ptr)*st->frame_size / st->sampling_rate);
+ break;
+ case SPEEX_PREPROCESS_GET_AGC_DECREMENT:
+ (*(spx_int32_t*)ptr) = floor(.5+8.6858*log(st->max_decrease_step)*st->sampling_rate/st->frame_size);
+ break;
+ case SPEEX_PREPROCESS_SET_AGC_MAX_GAIN:
+ st->max_gain = exp(0.11513f * (*(spx_int32_t*)ptr));
+ break;
+ case SPEEX_PREPROCESS_GET_AGC_MAX_GAIN:
+ (*(spx_int32_t*)ptr) = floor(.5+8.6858*log(st->max_gain));
+ break;
+#endif
+ case SPEEX_PREPROCESS_SET_VAD:
+ speex_warning("The VAD has been replaced by a hack pending a complete rewrite");
+ st->vad_enabled = (*(spx_int32_t*)ptr);
+ break;
+ case SPEEX_PREPROCESS_GET_VAD:
+ (*(spx_int32_t*)ptr) = st->vad_enabled;
+ break;
+
+ case SPEEX_PREPROCESS_SET_DEREVERB:
+ st->dereverb_enabled = (*(spx_int32_t*)ptr);
+ for (i=0;i<st->ps_size;i++)
+ st->reverb_estimate[i]=0;
+ break;
+ case SPEEX_PREPROCESS_GET_DEREVERB:
+ (*(spx_int32_t*)ptr) = st->dereverb_enabled;
+ break;
+
+ case SPEEX_PREPROCESS_SET_DEREVERB_LEVEL:
+ /* FIXME: Re-enable when de-reverberation is actually enabled again */
+ /*st->reverb_level = (*(float*)ptr);*/
+ break;
+ case SPEEX_PREPROCESS_GET_DEREVERB_LEVEL:
+ /* FIXME: Re-enable when de-reverberation is actually enabled again */
+ /*(*(float*)ptr) = st->reverb_level;*/
+ break;
+
+ case SPEEX_PREPROCESS_SET_DEREVERB_DECAY:
+ /* FIXME: Re-enable when de-reverberation is actually enabled again */
+ /*st->reverb_decay = (*(float*)ptr);*/
+ break;
+ case SPEEX_PREPROCESS_GET_DEREVERB_DECAY:
+ /* FIXME: Re-enable when de-reverberation is actually enabled again */
+ /*(*(float*)ptr) = st->reverb_decay;*/
+ break;
+
+ case SPEEX_PREPROCESS_SET_PROB_START:
+ *(spx_int32_t*)ptr = MIN32(100,MAX32(0, *(spx_int32_t*)ptr));
+ st->speech_prob_start = DIV32_16(MULT16_16(Q15ONE,*(spx_int32_t*)ptr), 100);
+ break;
+ case SPEEX_PREPROCESS_GET_PROB_START:
+ (*(spx_int32_t*)ptr) = MULT16_16_Q15(st->speech_prob_start, 100);
+ break;
+
+ case SPEEX_PREPROCESS_SET_PROB_CONTINUE:
+ *(spx_int32_t*)ptr = MIN32(100,MAX32(0, *(spx_int32_t*)ptr));
+ st->speech_prob_continue = DIV32_16(MULT16_16(Q15ONE,*(spx_int32_t*)ptr), 100);
+ break;
+ case SPEEX_PREPROCESS_GET_PROB_CONTINUE:
+ (*(spx_int32_t*)ptr) = MULT16_16_Q15(st->speech_prob_continue, 100);
+ break;
+
+ case SPEEX_PREPROCESS_SET_NOISE_SUPPRESS:
+ st->noise_suppress = -ABS(*(spx_int32_t*)ptr);
+ break;
+ case SPEEX_PREPROCESS_GET_NOISE_SUPPRESS:
+ (*(spx_int32_t*)ptr) = st->noise_suppress;
+ break;
+ case SPEEX_PREPROCESS_SET_ECHO_SUPPRESS:
+ st->echo_suppress = -ABS(*(spx_int32_t*)ptr);
+ break;
+ case SPEEX_PREPROCESS_GET_ECHO_SUPPRESS:
+ (*(spx_int32_t*)ptr) = st->echo_suppress;
+ break;
+ case SPEEX_PREPROCESS_SET_ECHO_SUPPRESS_ACTIVE:
+ st->echo_suppress_active = -ABS(*(spx_int32_t*)ptr);
+ break;
+ case SPEEX_PREPROCESS_GET_ECHO_SUPPRESS_ACTIVE:
+ (*(spx_int32_t*)ptr) = st->echo_suppress_active;
+ break;
+ case SPEEX_PREPROCESS_SET_ECHO_STATE:
+ st->echo_state = (SpeexEchoState*)ptr;
+ break;
+ case SPEEX_PREPROCESS_GET_ECHO_STATE:
+ (*(SpeexEchoState**)ptr) = (SpeexEchoState*)st->echo_state;
+ break;
+#ifndef FIXED_POINT
+ case SPEEX_PREPROCESS_GET_AGC_LOUDNESS:
+ (*(spx_int32_t*)ptr) = pow(st->loudness, 1.0/LOUDNESS_EXP);
+ break;
+ case SPEEX_PREPROCESS_GET_AGC_GAIN:
+ (*(spx_int32_t*)ptr) = floor(.5+8.6858*log(st->agc_gain));
+ break;
+#endif
+ case SPEEX_PREPROCESS_GET_PSD_SIZE:
+ case SPEEX_PREPROCESS_GET_NOISE_PSD_SIZE:
+ (*(spx_int32_t*)ptr) = st->ps_size;
+ break;
+ case SPEEX_PREPROCESS_GET_PSD:
+ for(i=0;i<st->ps_size;i++)
+ ((spx_int32_t *)ptr)[i] = (spx_int32_t) st->ps[i];
+ break;
+ case SPEEX_PREPROCESS_GET_NOISE_PSD:
+ for(i=0;i<st->ps_size;i++)
+ ((spx_int32_t *)ptr)[i] = (spx_int32_t) PSHR32(st->noise[i], NOISE_SHIFT);
+ break;
+ case SPEEX_PREPROCESS_GET_PROB:
+ (*(spx_int32_t*)ptr) = MULT16_16_Q15(st->speech_prob, 100);
+ break;
+#ifndef FIXED_POINT
+ case SPEEX_PREPROCESS_SET_AGC_TARGET:
+ st->agc_level = (*(spx_int32_t*)ptr);
+ if (st->agc_level<1)
+ st->agc_level=1;
+ if (st->agc_level>32768)
+ st->agc_level=32768;
+ break;
+ case SPEEX_PREPROCESS_GET_AGC_TARGET:
+ (*(spx_int32_t*)ptr) = st->agc_level;
+ break;
+#endif
+ default:
+ speex_warning_int("Unknown speex_preprocess_ctl request: ", request);
+ return -1;
+ }
+ return 0;
+}
+
+#ifdef FIXED_DEBUG
+long long spx_mips=0;
+#endif
+
diff --git a/lib/speexdsp/libspeexdsp/pseudofloat.h b/lib/speexdsp/libspeexdsp/pseudofloat.h
new file mode 100644
index 00000000..ed5ab14b
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/pseudofloat.h
@@ -0,0 +1,379 @@
+/* Copyright (C) 2005 Jean-Marc Valin */
+/**
+ @file pseudofloat.h
+ @brief Pseudo-floating point
+ * This header file provides a lightweight floating point type for
+ * use on fixed-point platforms when a large dynamic range is
+ * required. The new type is not compatible with the 32-bit IEEE format,
+ * it is not even remotely as accurate as 32-bit floats, and is not
+ * even guaranteed to produce even remotely correct results for code
+ * other than Speex. It makes all kinds of shortcuts that are acceptable
+ * for Speex, but may not be acceptable for your application. You're
+ * quite welcome to reuse this code and improve it, but don't assume
+ * it works out of the box. Most likely, it doesn't.
+ */
+/*
+ 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.
+*/
+
+#ifndef PSEUDOFLOAT_H
+#define PSEUDOFLOAT_H
+
+#include "arch.h"
+#include "os_support.h"
+#include "math_approx.h"
+#include <math.h>
+
+#ifdef FIXED_POINT
+
+typedef struct {
+ spx_int16_t m;
+ spx_int16_t e;
+} spx_float_t;
+
+static const spx_float_t FLOAT_ZERO = {0,0};
+static const spx_float_t FLOAT_ONE = {16384,-14};
+static const spx_float_t FLOAT_HALF = {16384,-15};
+
+#define MIN(a,b) ((a)<(b)?(a):(b))
+static inline spx_float_t PSEUDOFLOAT(spx_int32_t x)
+{
+ int e=0;
+ int sign=0;
+ if (x<0)
+ {
+ sign = 1;
+ x = -x;
+ }
+ if (x==0)
+ {
+ spx_float_t r = {0,0};
+ return r;
+ }
+ e = spx_ilog2(ABS32(x))-14;
+ x = VSHR32(x, e);
+ if (sign)
+ {
+ spx_float_t r;
+ r.m = -x;
+ r.e = e;
+ return r;
+ }
+ else
+ {
+ spx_float_t r;
+ r.m = x;
+ r.e = e;
+ return r;
+ }
+}
+
+
+static inline spx_float_t FLOAT_ADD(spx_float_t a, spx_float_t b)
+{
+ spx_float_t r;
+ if (a.m==0)
+ return b;
+ else if (b.m==0)
+ return a;
+ if ((a).e > (b).e)
+ {
+ r.m = ((a).m>>1) + ((b).m>>MIN(15,(a).e-(b).e+1));
+ r.e = (a).e+1;
+ }
+ else
+ {
+ r.m = ((b).m>>1) + ((a).m>>MIN(15,(b).e-(a).e+1));
+ r.e = (b).e+1;
+ }
+ if (r.m>0)
+ {
+ if (r.m<16384)
+ {
+ r.m<<=1;
+ r.e-=1;
+ }
+ } else {
+ if (r.m>-16384)
+ {
+ r.m<<=1;
+ r.e-=1;
+ }
+ }
+ /*printf ("%f + %f = %f\n", REALFLOAT(a), REALFLOAT(b), REALFLOAT(r));*/
+ return r;
+}
+
+static inline spx_float_t FLOAT_SUB(spx_float_t a, spx_float_t b)
+{
+ spx_float_t r;
+ if (a.m==0)
+ return b;
+ else if (b.m==0)
+ return a;
+ if ((a).e > (b).e)
+ {
+ r.m = ((a).m>>1) - ((b).m>>MIN(15,(a).e-(b).e+1));
+ r.e = (a).e+1;
+ }
+ else
+ {
+ r.m = ((a).m>>MIN(15,(b).e-(a).e+1)) - ((b).m>>1);
+ r.e = (b).e+1;
+ }
+ if (r.m>0)
+ {
+ if (r.m<16384)
+ {
+ r.m<<=1;
+ r.e-=1;
+ }
+ } else {
+ if (r.m>-16384)
+ {
+ r.m<<=1;
+ r.e-=1;
+ }
+ }
+ /*printf ("%f + %f = %f\n", REALFLOAT(a), REALFLOAT(b), REALFLOAT(r));*/
+ return r;
+}
+
+static inline int FLOAT_LT(spx_float_t a, spx_float_t b)
+{
+ if (a.m==0)
+ return b.m>0;
+ else if (b.m==0)
+ return a.m<0;
+ if ((a).e > (b).e)
+ return ((a).m>>1) < ((b).m>>MIN(15,(a).e-(b).e+1));
+ else
+ return ((b).m>>1) > ((a).m>>MIN(15,(b).e-(a).e+1));
+
+}
+
+static inline int FLOAT_GT(spx_float_t a, spx_float_t b)
+{
+ return FLOAT_LT(b,a);
+}
+
+static inline spx_float_t FLOAT_MULT(spx_float_t a, spx_float_t b)
+{
+ spx_float_t r;
+ r.m = (spx_int16_t)((spx_int32_t)(a).m*(b).m>>15);
+ r.e = (a).e+(b).e+15;
+ if (r.m>0)
+ {
+ if (r.m<16384)
+ {
+ r.m<<=1;
+ r.e-=1;
+ }
+ } else {
+ if (r.m>-16384)
+ {
+ r.m<<=1;
+ r.e-=1;
+ }
+ }
+ /*printf ("%f * %f = %f\n", REALFLOAT(a), REALFLOAT(b), REALFLOAT(r));*/
+ return r;
+}
+
+static inline spx_float_t FLOAT_AMULT(spx_float_t a, spx_float_t b)
+{
+ spx_float_t r;
+ r.m = (spx_int16_t)((spx_int32_t)(a).m*(b).m>>15);
+ r.e = (a).e+(b).e+15;
+ return r;
+}
+
+
+static inline spx_float_t FLOAT_SHL(spx_float_t a, int b)
+{
+ spx_float_t r;
+ r.m = a.m;
+ r.e = a.e+b;
+ return r;
+}
+
+static inline spx_int16_t FLOAT_EXTRACT16(spx_float_t a)
+{
+ if (a.e<0)
+ return EXTRACT16((EXTEND32(a.m)+(EXTEND32(1)<<(-a.e-1)))>>-a.e);
+ else
+ return a.m<<a.e;
+}
+
+static inline spx_int32_t FLOAT_EXTRACT32(spx_float_t a)
+{
+ if (a.e<0)
+ return (EXTEND32(a.m)+(EXTEND32(1)<<(-a.e-1)))>>-a.e;
+ else
+ return EXTEND32(a.m)<<a.e;
+}
+
+static inline spx_int32_t FLOAT_MUL32(spx_float_t a, spx_word32_t b)
+{
+ return VSHR32(MULT16_32_Q15(a.m, b),-a.e-15);
+}
+
+static inline spx_float_t FLOAT_MUL32U(spx_word32_t a, spx_word32_t b)
+{
+ int e1, e2;
+ spx_float_t r;
+ if (a==0 || b==0)
+ {
+ return FLOAT_ZERO;
+ }
+ e1 = spx_ilog2(ABS32(a));
+ a = VSHR32(a, e1-14);
+ e2 = spx_ilog2(ABS32(b));
+ b = VSHR32(b, e2-14);
+ r.m = MULT16_16_Q15(a,b);
+ r.e = e1+e2-13;
+ return r;
+}
+
+/* Do NOT attempt to divide by a negative number */
+static inline spx_float_t FLOAT_DIV32_FLOAT(spx_word32_t a, spx_float_t b)
+{
+ int e=0;
+ spx_float_t r;
+ if (a==0)
+ {
+ return FLOAT_ZERO;
+ }
+ e = spx_ilog2(ABS32(a))-spx_ilog2(b.m-1)-15;
+ a = VSHR32(a, e);
+ if (ABS32(a)>=SHL32(EXTEND32(b.m-1),15))
+ {
+ a >>= 1;
+ e++;
+ }
+ r.m = DIV32_16(a,b.m);
+ r.e = e-b.e;
+ return r;
+}
+
+
+/* Do NOT attempt to divide by a negative number */
+static inline spx_float_t FLOAT_DIV32(spx_word32_t a, spx_word32_t b)
+{
+ int e0=0,e=0;
+ spx_float_t r;
+ if (a==0)
+ {
+ return FLOAT_ZERO;
+ }
+ if (b>32767)
+ {
+ e0 = spx_ilog2(b)-14;
+ b = VSHR32(b, e0);
+ e0 = -e0;
+ }
+ e = spx_ilog2(ABS32(a))-spx_ilog2(b-1)-15;
+ a = VSHR32(a, e);
+ if (ABS32(a)>=SHL32(EXTEND32(b-1),15))
+ {
+ a >>= 1;
+ e++;
+ }
+ e += e0;
+ r.m = DIV32_16(a,b);
+ r.e = e;
+ return r;
+}
+
+/* Do NOT attempt to divide by a negative number */
+static inline spx_float_t FLOAT_DIVU(spx_float_t a, spx_float_t b)
+{
+ int e=0;
+ spx_int32_t num;
+ spx_float_t r;
+ if (b.m<=0)
+ {
+ speex_warning_int("Attempted to divide by", b.m);
+ return FLOAT_ONE;
+ }
+ num = a.m;
+ a.m = ABS16(a.m);
+ while (a.m >= b.m)
+ {
+ e++;
+ a.m >>= 1;
+ }
+ num = num << (15-e);
+ r.m = DIV32_16(num,b.m);
+ r.e = a.e-b.e-15+e;
+ return r;
+}
+
+static inline spx_float_t FLOAT_SQRT(spx_float_t a)
+{
+ spx_float_t r;
+ spx_int32_t m;
+ m = SHL32(EXTEND32(a.m), 14);
+ r.e = a.e - 14;
+ if (r.e & 1)
+ {
+ r.e -= 1;
+ m <<= 1;
+ }
+ r.e >>= 1;
+ r.m = spx_sqrt(m);
+ return r;
+}
+
+#else
+
+#define spx_float_t float
+#define FLOAT_ZERO 0.f
+#define FLOAT_ONE 1.f
+#define FLOAT_HALF 0.5f
+#define PSEUDOFLOAT(x) (x)
+#define FLOAT_MULT(a,b) ((a)*(b))
+#define FLOAT_AMULT(a,b) ((a)*(b))
+#define FLOAT_MUL32(a,b) ((a)*(b))
+#define FLOAT_DIV32(a,b) ((a)/(b))
+#define FLOAT_EXTRACT16(a) (a)
+#define FLOAT_EXTRACT32(a) (a)
+#define FLOAT_ADD(a,b) ((a)+(b))
+#define FLOAT_SUB(a,b) ((a)-(b))
+#define REALFLOAT(x) (x)
+#define FLOAT_DIV32_FLOAT(a,b) ((a)/(b))
+#define FLOAT_MUL32U(a,b) ((a)*(b))
+#define FLOAT_SHL(a,b) (a)
+#define FLOAT_LT(a,b) ((a)<(b))
+#define FLOAT_GT(a,b) ((a)>(b))
+#define FLOAT_DIVU(a,b) ((a)/(b))
+#define FLOAT_SQRT(a) (spx_sqrt(a))
+
+#endif
+
+#endif
diff --git a/lib/speexdsp/libspeexdsp/resample.c b/lib/speexdsp/libspeexdsp/resample.c
new file mode 100644
index 00000000..2bfbc1c0
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/resample.c
@@ -0,0 +1,1239 @@
+/* Copyright (C) 2007-2008 Jean-Marc Valin
+ Copyright (C) 2008 Thorvald Natvig
+
+ File: resample.c
+ Arbitrary resampling code
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. 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.
+
+ 3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 design goals of this code are:
+ - Very fast algorithm
+ - SIMD-friendly algorithm
+ - Low memory requirement
+ - Good *perceptual* quality (and not best SNR)
+
+ Warning: This resampler is relatively new. Although I think I got rid of
+ all the major bugs and I don't expect the API to change anymore, there
+ may be something I've missed. So use with caution.
+
+ This algorithm is based on this original resampling algorithm:
+ Smith, Julius O. Digital Audio Resampling Home Page
+ Center for Computer Research in Music and Acoustics (CCRMA),
+ Stanford University, 2007.
+ Web published at https://ccrma.stanford.edu/~jos/resample/.
+
+ There is one main difference, though. This resampler uses cubic
+ interpolation instead of linear interpolation in the above paper. This
+ makes the table much smaller and makes it possible to compute that table
+ on a per-stream basis. In turn, being able to tweak the table for each
+ stream makes it possible to both reduce complexity on simple ratios
+ (e.g. 2/3), and get rid of the rounding operations in the inner loop.
+ The latter both reduces CPU time and makes the algorithm more SIMD-friendly.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef OUTSIDE_SPEEX
+#include <stdlib.h>
+static void *speex_alloc(int size) {return calloc(size,1);}
+static void *speex_realloc(void *ptr, int size) {return realloc(ptr, size);}
+static void speex_free(void *ptr) {free(ptr);}
+#ifndef EXPORT
+#define EXPORT
+#endif
+#include "speex_resampler.h"
+#include "arch.h"
+#else /* OUTSIDE_SPEEX */
+
+#include "speex/speex_resampler.h"
+#include "arch.h"
+#include "os_support.h"
+#endif /* OUTSIDE_SPEEX */
+
+#include <math.h>
+#include <limits.h>
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+#define IMAX(a,b) ((a) > (b) ? (a) : (b))
+#define IMIN(a,b) ((a) < (b) ? (a) : (b))
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef UINT32_MAX
+#define UINT32_MAX 4294967295U
+#endif
+
+#ifdef USE_SSE
+#include "resample_sse.h"
+#endif
+
+#ifdef USE_NEON
+#include "resample_neon.h"
+#endif
+
+/* Numer of elements to allocate on the stack */
+#ifdef VAR_ARRAYS
+#define FIXED_STACK_ALLOC 8192
+#else
+#define FIXED_STACK_ALLOC 1024
+#endif
+
+typedef int (*resampler_basic_func)(SpeexResamplerState *, spx_uint32_t , const spx_word16_t *, spx_uint32_t *, spx_word16_t *, spx_uint32_t *);
+
+struct SpeexResamplerState_ {
+ spx_uint32_t in_rate;
+ spx_uint32_t out_rate;
+ spx_uint32_t num_rate;
+ spx_uint32_t den_rate;
+
+ int quality;
+ spx_uint32_t nb_channels;
+ spx_uint32_t filt_len;
+ spx_uint32_t mem_alloc_size;
+ spx_uint32_t buffer_size;
+ int int_advance;
+ int frac_advance;
+ float cutoff;
+ spx_uint32_t oversample;
+ int initialised;
+ int started;
+
+ /* These are per-channel */
+ spx_int32_t *last_sample;
+ spx_uint32_t *samp_frac_num;
+ spx_uint32_t *magic_samples;
+
+ spx_word16_t *mem;
+ spx_word16_t *sinc_table;
+ spx_uint32_t sinc_table_length;
+ resampler_basic_func resampler_ptr;
+
+ int in_stride;
+ int out_stride;
+} ;
+
+static const double kaiser12_table[68] = {
+ 0.99859849, 1.00000000, 0.99859849, 0.99440475, 0.98745105, 0.97779076,
+ 0.96549770, 0.95066529, 0.93340547, 0.91384741, 0.89213598, 0.86843014,
+ 0.84290116, 0.81573067, 0.78710866, 0.75723148, 0.72629970, 0.69451601,
+ 0.66208321, 0.62920216, 0.59606986, 0.56287762, 0.52980938, 0.49704014,
+ 0.46473455, 0.43304576, 0.40211431, 0.37206735, 0.34301800, 0.31506490,
+ 0.28829195, 0.26276832, 0.23854851, 0.21567274, 0.19416736, 0.17404546,
+ 0.15530766, 0.13794294, 0.12192957, 0.10723616, 0.09382272, 0.08164178,
+ 0.07063950, 0.06075685, 0.05193064, 0.04409466, 0.03718069, 0.03111947,
+ 0.02584161, 0.02127838, 0.01736250, 0.01402878, 0.01121463, 0.00886058,
+ 0.00691064, 0.00531256, 0.00401805, 0.00298291, 0.00216702, 0.00153438,
+ 0.00105297, 0.00069463, 0.00043489, 0.00025272, 0.00013031, 0.0000527734,
+ 0.00001000, 0.00000000};
+/*
+static const double kaiser12_table[36] = {
+ 0.99440475, 1.00000000, 0.99440475, 0.97779076, 0.95066529, 0.91384741,
+ 0.86843014, 0.81573067, 0.75723148, 0.69451601, 0.62920216, 0.56287762,
+ 0.49704014, 0.43304576, 0.37206735, 0.31506490, 0.26276832, 0.21567274,
+ 0.17404546, 0.13794294, 0.10723616, 0.08164178, 0.06075685, 0.04409466,
+ 0.03111947, 0.02127838, 0.01402878, 0.00886058, 0.00531256, 0.00298291,
+ 0.00153438, 0.00069463, 0.00025272, 0.0000527734, 0.00000500, 0.00000000};
+*/
+static const double kaiser10_table[36] = {
+ 0.99537781, 1.00000000, 0.99537781, 0.98162644, 0.95908712, 0.92831446,
+ 0.89005583, 0.84522401, 0.79486424, 0.74011713, 0.68217934, 0.62226347,
+ 0.56155915, 0.50119680, 0.44221549, 0.38553619, 0.33194107, 0.28205962,
+ 0.23636152, 0.19515633, 0.15859932, 0.12670280, 0.09935205, 0.07632451,
+ 0.05731132, 0.04193980, 0.02979584, 0.02044510, 0.01345224, 0.00839739,
+ 0.00488951, 0.00257636, 0.00115101, 0.00035515, 0.00000000, 0.00000000};
+
+static const double kaiser8_table[36] = {
+ 0.99635258, 1.00000000, 0.99635258, 0.98548012, 0.96759014, 0.94302200,
+ 0.91223751, 0.87580811, 0.83439927, 0.78875245, 0.73966538, 0.68797126,
+ 0.63451750, 0.58014482, 0.52566725, 0.47185369, 0.41941150, 0.36897272,
+ 0.32108304, 0.27619388, 0.23465776, 0.19672670, 0.16255380, 0.13219758,
+ 0.10562887, 0.08273982, 0.06335451, 0.04724088, 0.03412321, 0.02369490,
+ 0.01563093, 0.00959968, 0.00527363, 0.00233883, 0.00050000, 0.00000000};
+
+static const double kaiser6_table[36] = {
+ 0.99733006, 1.00000000, 0.99733006, 0.98935595, 0.97618418, 0.95799003,
+ 0.93501423, 0.90755855, 0.87598009, 0.84068475, 0.80211977, 0.76076565,
+ 0.71712752, 0.67172623, 0.62508937, 0.57774224, 0.53019925, 0.48295561,
+ 0.43647969, 0.39120616, 0.34752997, 0.30580127, 0.26632152, 0.22934058,
+ 0.19505503, 0.16360756, 0.13508755, 0.10953262, 0.08693120, 0.06722600,
+ 0.05031820, 0.03607231, 0.02432151, 0.01487334, 0.00752000, 0.00000000};
+
+struct FuncDef {
+ const double *table;
+ int oversample;
+};
+
+static const struct FuncDef kaiser12_funcdef = {kaiser12_table, 64};
+#define KAISER12 (&kaiser12_funcdef)
+static const struct FuncDef kaiser10_funcdef = {kaiser10_table, 32};
+#define KAISER10 (&kaiser10_funcdef)
+static const struct FuncDef kaiser8_funcdef = {kaiser8_table, 32};
+#define KAISER8 (&kaiser8_funcdef)
+static const struct FuncDef kaiser6_funcdef = {kaiser6_table, 32};
+#define KAISER6 (&kaiser6_funcdef)
+
+struct QualityMapping {
+ int base_length;
+ int oversample;
+ float downsample_bandwidth;
+ float upsample_bandwidth;
+ const struct FuncDef *window_func;
+};
+
+
+/* This table maps conversion quality to internal parameters. There are two
+ reasons that explain why the up-sampling bandwidth is larger than the
+ down-sampling bandwidth:
+ 1) When up-sampling, we can assume that the spectrum is already attenuated
+ close to the Nyquist rate (from an A/D or a previous resampling filter)
+ 2) Any aliasing that occurs very close to the Nyquist rate will be masked
+ by the sinusoids/noise just below the Nyquist rate (guaranteed only for
+ up-sampling).
+*/
+static const struct QualityMapping quality_map[11] = {
+ { 8, 4, 0.830f, 0.860f, KAISER6 }, /* Q0 */
+ { 16, 4, 0.850f, 0.880f, KAISER6 }, /* Q1 */
+ { 32, 4, 0.882f, 0.910f, KAISER6 }, /* Q2 */ /* 82.3% cutoff ( ~60 dB stop) 6 */
+ { 48, 8, 0.895f, 0.917f, KAISER8 }, /* Q3 */ /* 84.9% cutoff ( ~80 dB stop) 8 */
+ { 64, 8, 0.921f, 0.940f, KAISER8 }, /* Q4 */ /* 88.7% cutoff ( ~80 dB stop) 8 */
+ { 80, 16, 0.922f, 0.940f, KAISER10}, /* Q5 */ /* 89.1% cutoff (~100 dB stop) 10 */
+ { 96, 16, 0.940f, 0.945f, KAISER10}, /* Q6 */ /* 91.5% cutoff (~100 dB stop) 10 */
+ {128, 16, 0.950f, 0.950f, KAISER10}, /* Q7 */ /* 93.1% cutoff (~100 dB stop) 10 */
+ {160, 16, 0.960f, 0.960f, KAISER10}, /* Q8 */ /* 94.5% cutoff (~100 dB stop) 10 */
+ {192, 32, 0.968f, 0.968f, KAISER12}, /* Q9 */ /* 95.5% cutoff (~100 dB stop) 10 */
+ {256, 32, 0.975f, 0.975f, KAISER12}, /* Q10 */ /* 96.6% cutoff (~100 dB stop) 10 */
+};
+/*8,24,40,56,80,104,128,160,200,256,320*/
+static double compute_func(float x, const struct FuncDef *func)
+{
+ float y, frac;
+ double interp[4];
+ int ind;
+ y = x*func->oversample;
+ ind = (int)floor(y);
+ frac = (y-ind);
+ /* CSE with handle the repeated powers */
+ interp[3] = -0.1666666667*frac + 0.1666666667*(frac*frac*frac);
+ interp[2] = frac + 0.5*(frac*frac) - 0.5*(frac*frac*frac);
+ /*interp[2] = 1.f - 0.5f*frac - frac*frac + 0.5f*frac*frac*frac;*/
+ interp[0] = -0.3333333333*frac + 0.5*(frac*frac) - 0.1666666667*(frac*frac*frac);
+ /* Just to make sure we don't have rounding problems */
+ interp[1] = 1.f-interp[3]-interp[2]-interp[0];
+
+ /*sum = frac*accum[1] + (1-frac)*accum[2];*/
+ return interp[0]*func->table[ind] + interp[1]*func->table[ind+1] + interp[2]*func->table[ind+2] + interp[3]*func->table[ind+3];
+}
+
+#if 0
+#include <stdio.h>
+int main(int argc, char **argv)
+{
+ int i;
+ for (i=0;i<256;i++)
+ {
+ printf ("%f\n", compute_func(i/256., KAISER12));
+ }
+ return 0;
+}
+#endif
+
+#ifdef FIXED_POINT
+/* The slow way of computing a sinc for the table. Should improve that some day */
+static spx_word16_t sinc(float cutoff, float x, int N, const struct FuncDef *window_func)
+{
+ /*fprintf (stderr, "%f ", x);*/
+ float xx = x * cutoff;
+ if (fabs(x)<1e-6f)
+ return WORD2INT(32768.*cutoff);
+ else if (fabs(x) > .5f*N)
+ return 0;
+ /*FIXME: Can it really be any slower than this? */
+ return WORD2INT(32768.*cutoff*sin(M_PI*xx)/(M_PI*xx) * compute_func(fabs(2.*x/N), window_func));
+}
+#else
+/* The slow way of computing a sinc for the table. Should improve that some day */
+static spx_word16_t sinc(float cutoff, float x, int N, const struct FuncDef *window_func)
+{
+ /*fprintf (stderr, "%f ", x);*/
+ float xx = x * cutoff;
+ if (fabs(x)<1e-6)
+ return cutoff;
+ else if (fabs(x) > .5*N)
+ return 0;
+ /*FIXME: Can it really be any slower than this? */
+ return cutoff*sin(M_PI*xx)/(M_PI*xx) * compute_func(fabs(2.*x/N), window_func);
+}
+#endif
+
+#ifdef FIXED_POINT
+static void cubic_coef(spx_word16_t x, spx_word16_t interp[4])
+{
+ /* Compute interpolation coefficients. I'm not sure whether this corresponds to cubic interpolation
+ but I know it's MMSE-optimal on a sinc */
+ spx_word16_t x2, x3;
+ x2 = MULT16_16_P15(x, x);
+ x3 = MULT16_16_P15(x, x2);
+ interp[0] = PSHR32(MULT16_16(QCONST16(-0.16667f, 15),x) + MULT16_16(QCONST16(0.16667f, 15),x3),15);
+ interp[1] = EXTRACT16(EXTEND32(x) + SHR32(SUB32(EXTEND32(x2),EXTEND32(x3)),1));
+ interp[3] = PSHR32(MULT16_16(QCONST16(-0.33333f, 15),x) + MULT16_16(QCONST16(.5f,15),x2) - MULT16_16(QCONST16(0.16667f, 15),x3),15);
+ /* Just to make sure we don't have rounding problems */
+ interp[2] = Q15_ONE-interp[0]-interp[1]-interp[3];
+ if (interp[2]<32767)
+ interp[2]+=1;
+}
+#else
+static void cubic_coef(spx_word16_t frac, spx_word16_t interp[4])
+{
+ /* Compute interpolation coefficients. I'm not sure whether this corresponds to cubic interpolation
+ but I know it's MMSE-optimal on a sinc */
+ interp[0] = -0.16667f*frac + 0.16667f*frac*frac*frac;
+ interp[1] = frac + 0.5f*frac*frac - 0.5f*frac*frac*frac;
+ /*interp[2] = 1.f - 0.5f*frac - frac*frac + 0.5f*frac*frac*frac;*/
+ interp[3] = -0.33333f*frac + 0.5f*frac*frac - 0.16667f*frac*frac*frac;
+ /* Just to make sure we don't have rounding problems */
+ interp[2] = 1.-interp[0]-interp[1]-interp[3];
+}
+#endif
+
+static int resampler_basic_direct_single(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len)
+{
+ const int N = st->filt_len;
+ int out_sample = 0;
+ int last_sample = st->last_sample[channel_index];
+ spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index];
+ const spx_word16_t *sinc_table = st->sinc_table;
+ const int out_stride = st->out_stride;
+ const int int_advance = st->int_advance;
+ const int frac_advance = st->frac_advance;
+ const spx_uint32_t den_rate = st->den_rate;
+ spx_word32_t sum;
+
+ while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len))
+ {
+ const spx_word16_t *sinct = & sinc_table[samp_frac_num*N];
+ const spx_word16_t *iptr = & in[last_sample];
+
+#ifndef OVERRIDE_INNER_PRODUCT_SINGLE
+ int j;
+ sum = 0;
+ for(j=0;j<N;j++) sum += MULT16_16(sinct[j], iptr[j]);
+
+/* This code is slower on most DSPs which have only 2 accumulators.
+ Plus this this forces truncation to 32 bits and you lose the HW guard bits.
+ I think we can trust the compiler and let it vectorize and/or unroll itself.
+ spx_word32_t accum[4] = {0,0,0,0};
+ for(j=0;j<N;j+=4) {
+ accum[0] += MULT16_16(sinct[j], iptr[j]);
+ accum[1] += MULT16_16(sinct[j+1], iptr[j+1]);
+ accum[2] += MULT16_16(sinct[j+2], iptr[j+2]);
+ accum[3] += MULT16_16(sinct[j+3], iptr[j+3]);
+ }
+ sum = accum[0] + accum[1] + accum[2] + accum[3];
+*/
+ sum = SATURATE32PSHR(sum, 15, 32767);
+#else
+ sum = inner_product_single(sinct, iptr, N);
+#endif
+
+ out[out_stride * out_sample++] = sum;
+ last_sample += int_advance;
+ samp_frac_num += frac_advance;
+ if (samp_frac_num >= den_rate)
+ {
+ samp_frac_num -= den_rate;
+ last_sample++;
+ }
+ }
+
+ st->last_sample[channel_index] = last_sample;
+ st->samp_frac_num[channel_index] = samp_frac_num;
+ return out_sample;
+}
+
+#ifdef FIXED_POINT
+#else
+/* This is the same as the previous function, except with a double-precision accumulator */
+static int resampler_basic_direct_double(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len)
+{
+ const int N = st->filt_len;
+ int out_sample = 0;
+ int last_sample = st->last_sample[channel_index];
+ spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index];
+ const spx_word16_t *sinc_table = st->sinc_table;
+ const int out_stride = st->out_stride;
+ const int int_advance = st->int_advance;
+ const int frac_advance = st->frac_advance;
+ const spx_uint32_t den_rate = st->den_rate;
+ double sum;
+
+ while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len))
+ {
+ const spx_word16_t *sinct = & sinc_table[samp_frac_num*N];
+ const spx_word16_t *iptr = & in[last_sample];
+
+#ifndef OVERRIDE_INNER_PRODUCT_DOUBLE
+ int j;
+ double accum[4] = {0,0,0,0};
+
+ for(j=0;j<N;j+=4) {
+ accum[0] += sinct[j]*iptr[j];
+ accum[1] += sinct[j+1]*iptr[j+1];
+ accum[2] += sinct[j+2]*iptr[j+2];
+ accum[3] += sinct[j+3]*iptr[j+3];
+ }
+ sum = accum[0] + accum[1] + accum[2] + accum[3];
+#else
+ sum = inner_product_double(sinct, iptr, N);
+#endif
+
+ out[out_stride * out_sample++] = PSHR32(sum, 15);
+ last_sample += int_advance;
+ samp_frac_num += frac_advance;
+ if (samp_frac_num >= den_rate)
+ {
+ samp_frac_num -= den_rate;
+ last_sample++;
+ }
+ }
+
+ st->last_sample[channel_index] = last_sample;
+ st->samp_frac_num[channel_index] = samp_frac_num;
+ return out_sample;
+}
+#endif
+
+static int resampler_basic_interpolate_single(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len)
+{
+ const int N = st->filt_len;
+ int out_sample = 0;
+ int last_sample = st->last_sample[channel_index];
+ spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index];
+ const int out_stride = st->out_stride;
+ const int int_advance = st->int_advance;
+ const int frac_advance = st->frac_advance;
+ const spx_uint32_t den_rate = st->den_rate;
+ spx_word32_t sum;
+
+ while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len))
+ {
+ const spx_word16_t *iptr = & in[last_sample];
+
+ const int offset = samp_frac_num*st->oversample/st->den_rate;
+#ifdef FIXED_POINT
+ const spx_word16_t frac = PDIV32(SHL32((samp_frac_num*st->oversample) % st->den_rate,15),st->den_rate);
+#else
+ const spx_word16_t frac = ((float)((samp_frac_num*st->oversample) % st->den_rate))/st->den_rate;
+#endif
+ spx_word16_t interp[4];
+
+
+#ifndef OVERRIDE_INTERPOLATE_PRODUCT_SINGLE
+ int j;
+ spx_word32_t accum[4] = {0,0,0,0};
+
+ for(j=0;j<N;j++) {
+ const spx_word16_t curr_in=iptr[j];
+ accum[0] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-2]);
+ accum[1] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-1]);
+ accum[2] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset]);
+ accum[3] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset+1]);
+ }
+
+ cubic_coef(frac, interp);
+ sum = MULT16_32_Q15(interp[0],accum[0]) + MULT16_32_Q15(interp[1],accum[1]) + MULT16_32_Q15(interp[2],accum[2]) + MULT16_32_Q15(interp[3],accum[3]);
+ sum = SATURATE32PSHR(sum, 15, 32767);
+#else
+ cubic_coef(frac, interp);
+ sum = interpolate_product_single(iptr, st->sinc_table + st->oversample + 4 - offset - 2, N, st->oversample, interp);
+#endif
+
+ out[out_stride * out_sample++] = sum;
+ last_sample += int_advance;
+ samp_frac_num += frac_advance;
+ if (samp_frac_num >= den_rate)
+ {
+ samp_frac_num -= den_rate;
+ last_sample++;
+ }
+ }
+
+ st->last_sample[channel_index] = last_sample;
+ st->samp_frac_num[channel_index] = samp_frac_num;
+ return out_sample;
+}
+
+#ifdef FIXED_POINT
+#else
+/* This is the same as the previous function, except with a double-precision accumulator */
+static int resampler_basic_interpolate_double(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len)
+{
+ const int N = st->filt_len;
+ int out_sample = 0;
+ int last_sample = st->last_sample[channel_index];
+ spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index];
+ const int out_stride = st->out_stride;
+ const int int_advance = st->int_advance;
+ const int frac_advance = st->frac_advance;
+ const spx_uint32_t den_rate = st->den_rate;
+ spx_word32_t sum;
+
+ while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len))
+ {
+ const spx_word16_t *iptr = & in[last_sample];
+
+ const int offset = samp_frac_num*st->oversample/st->den_rate;
+#ifdef FIXED_POINT
+ const spx_word16_t frac = PDIV32(SHL32((samp_frac_num*st->oversample) % st->den_rate,15),st->den_rate);
+#else
+ const spx_word16_t frac = ((float)((samp_frac_num*st->oversample) % st->den_rate))/st->den_rate;
+#endif
+ spx_word16_t interp[4];
+
+
+#ifndef OVERRIDE_INTERPOLATE_PRODUCT_DOUBLE
+ int j;
+ double accum[4] = {0,0,0,0};
+
+ for(j=0;j<N;j++) {
+ const double curr_in=iptr[j];
+ accum[0] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-2]);
+ accum[1] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-1]);
+ accum[2] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset]);
+ accum[3] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset+1]);
+ }
+
+ cubic_coef(frac, interp);
+ sum = MULT16_32_Q15(interp[0],accum[0]) + MULT16_32_Q15(interp[1],accum[1]) + MULT16_32_Q15(interp[2],accum[2]) + MULT16_32_Q15(interp[3],accum[3]);
+#else
+ cubic_coef(frac, interp);
+ sum = interpolate_product_double(iptr, st->sinc_table + st->oversample + 4 - offset - 2, N, st->oversample, interp);
+#endif
+
+ out[out_stride * out_sample++] = PSHR32(sum,15);
+ last_sample += int_advance;
+ samp_frac_num += frac_advance;
+ if (samp_frac_num >= den_rate)
+ {
+ samp_frac_num -= den_rate;
+ last_sample++;
+ }
+ }
+
+ st->last_sample[channel_index] = last_sample;
+ st->samp_frac_num[channel_index] = samp_frac_num;
+ return out_sample;
+}
+#endif
+
+/* This resampler is used to produce zero output in situations where memory
+ for the filter could not be allocated. The expected numbers of input and
+ output samples are still processed so that callers failing to check error
+ codes are not surprised, possibly getting into infinite loops. */
+static int resampler_basic_zero(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len)
+{
+ int out_sample = 0;
+ int last_sample = st->last_sample[channel_index];
+ spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index];
+ const int out_stride = st->out_stride;
+ const int int_advance = st->int_advance;
+ const int frac_advance = st->frac_advance;
+ const spx_uint32_t den_rate = st->den_rate;
+
+ (void)in;
+ while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len))
+ {
+ out[out_stride * out_sample++] = 0;
+ last_sample += int_advance;
+ samp_frac_num += frac_advance;
+ if (samp_frac_num >= den_rate)
+ {
+ samp_frac_num -= den_rate;
+ last_sample++;
+ }
+ }
+
+ st->last_sample[channel_index] = last_sample;
+ st->samp_frac_num[channel_index] = samp_frac_num;
+ return out_sample;
+}
+
+static int multiply_frac(spx_uint32_t *result, spx_uint32_t value, spx_uint32_t num, spx_uint32_t den)
+{
+ spx_uint32_t major = value / den;
+ spx_uint32_t remain = value % den;
+ /* TODO: Could use 64 bits operation to check for overflow. But only guaranteed in C99+ */
+ if (remain > UINT32_MAX / num || major > UINT32_MAX / num
+ || major * num > UINT32_MAX - remain * num / den)
+ return RESAMPLER_ERR_OVERFLOW;
+ *result = remain * num / den + major * num;
+ return RESAMPLER_ERR_SUCCESS;
+}
+
+static int update_filter(SpeexResamplerState *st)
+{
+ spx_uint32_t old_length = st->filt_len;
+ spx_uint32_t old_alloc_size = st->mem_alloc_size;
+ int use_direct;
+ spx_uint32_t min_sinc_table_length;
+ spx_uint32_t min_alloc_size;
+
+ st->int_advance = st->num_rate/st->den_rate;
+ st->frac_advance = st->num_rate%st->den_rate;
+ st->oversample = quality_map[st->quality].oversample;
+ st->filt_len = quality_map[st->quality].base_length;
+
+ if (st->num_rate > st->den_rate)
+ {
+ /* down-sampling */
+ st->cutoff = quality_map[st->quality].downsample_bandwidth * st->den_rate / st->num_rate;
+ if (multiply_frac(&st->filt_len,st->filt_len,st->num_rate,st->den_rate) != RESAMPLER_ERR_SUCCESS)
+ goto fail;
+ /* Round up to make sure we have a multiple of 8 for SSE */
+ st->filt_len = ((st->filt_len-1)&(~0x7))+8;
+ if (2*st->den_rate < st->num_rate)
+ st->oversample >>= 1;
+ if (4*st->den_rate < st->num_rate)
+ st->oversample >>= 1;
+ if (8*st->den_rate < st->num_rate)
+ st->oversample >>= 1;
+ if (16*st->den_rate < st->num_rate)
+ st->oversample >>= 1;
+ if (st->oversample < 1)
+ st->oversample = 1;
+ } else {
+ /* up-sampling */
+ st->cutoff = quality_map[st->quality].upsample_bandwidth;
+ }
+
+#ifdef RESAMPLE_FULL_SINC_TABLE
+ use_direct = 1;
+ if (INT_MAX/sizeof(spx_word16_t)/st->den_rate < st->filt_len)
+ goto fail;
+#else
+ /* Choose the resampling type that requires the least amount of memory */
+ use_direct = st->filt_len*st->den_rate <= st->filt_len*st->oversample+8
+ && INT_MAX/sizeof(spx_word16_t)/st->den_rate >= st->filt_len;
+#endif
+ if (use_direct)
+ {
+ min_sinc_table_length = st->filt_len*st->den_rate;
+ } else {
+ if ((INT_MAX/sizeof(spx_word16_t)-8)/st->oversample < st->filt_len)
+ goto fail;
+
+ min_sinc_table_length = st->filt_len*st->oversample+8;
+ }
+ if (st->sinc_table_length < min_sinc_table_length)
+ {
+ spx_word16_t *sinc_table = (spx_word16_t *)speex_realloc(st->sinc_table,min_sinc_table_length*sizeof(spx_word16_t));
+ if (!sinc_table)
+ goto fail;
+
+ st->sinc_table = sinc_table;
+ st->sinc_table_length = min_sinc_table_length;
+ }
+ if (use_direct)
+ {
+ spx_uint32_t i;
+ for (i=0;i<st->den_rate;i++)
+ {
+ spx_int32_t j;
+ for (j=0;j<st->filt_len;j++)
+ {
+ st->sinc_table[i*st->filt_len+j] = sinc(st->cutoff,((j-(spx_int32_t)st->filt_len/2+1)-((float)i)/st->den_rate), st->filt_len, quality_map[st->quality].window_func);
+ }
+ }
+#ifdef FIXED_POINT
+ st->resampler_ptr = resampler_basic_direct_single;
+#else
+ if (st->quality>8)
+ st->resampler_ptr = resampler_basic_direct_double;
+ else
+ st->resampler_ptr = resampler_basic_direct_single;
+#endif
+ /*fprintf (stderr, "resampler uses direct sinc table and normalised cutoff %f\n", cutoff);*/
+ } else {
+ spx_int32_t i;
+ for (i=-4;i<(spx_int32_t)(st->oversample*st->filt_len+4);i++)
+ st->sinc_table[i+4] = sinc(st->cutoff,(i/(float)st->oversample - st->filt_len/2), st->filt_len, quality_map[st->quality].window_func);
+#ifdef FIXED_POINT
+ st->resampler_ptr = resampler_basic_interpolate_single;
+#else
+ if (st->quality>8)
+ st->resampler_ptr = resampler_basic_interpolate_double;
+ else
+ st->resampler_ptr = resampler_basic_interpolate_single;
+#endif
+ /*fprintf (stderr, "resampler uses interpolated sinc table and normalised cutoff %f\n", cutoff);*/
+ }
+
+ /* Here's the place where we update the filter memory to take into account
+ the change in filter length. It's probably the messiest part of the code
+ due to handling of lots of corner cases. */
+
+ /* Adding buffer_size to filt_len won't overflow here because filt_len
+ could be multiplied by sizeof(spx_word16_t) above. */
+ min_alloc_size = st->filt_len-1 + st->buffer_size;
+ if (min_alloc_size > st->mem_alloc_size)
+ {
+ spx_word16_t *mem;
+ if (INT_MAX/sizeof(spx_word16_t)/st->nb_channels < min_alloc_size)
+ goto fail;
+ else if (!(mem = (spx_word16_t*)speex_realloc(st->mem, st->nb_channels*min_alloc_size * sizeof(*mem))))
+ goto fail;
+
+ st->mem = mem;
+ st->mem_alloc_size = min_alloc_size;
+ }
+ if (!st->started)
+ {
+ spx_uint32_t i;
+ for (i=0;i<st->nb_channels*st->mem_alloc_size;i++)
+ st->mem[i] = 0;
+ /*speex_warning("reinit filter");*/
+ } else if (st->filt_len > old_length)
+ {
+ spx_uint32_t i;
+ /* Increase the filter length */
+ /*speex_warning("increase filter size");*/
+ for (i=st->nb_channels;i--;)
+ {
+ spx_uint32_t j;
+ spx_uint32_t olen = old_length;
+ /*if (st->magic_samples[i])*/
+ {
+ /* Try and remove the magic samples as if nothing had happened */
+
+ /* FIXME: This is wrong but for now we need it to avoid going over the array bounds */
+ olen = old_length + 2*st->magic_samples[i];
+ for (j=old_length-1+st->magic_samples[i];j--;)
+ st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]] = st->mem[i*old_alloc_size+j];
+ for (j=0;j<st->magic_samples[i];j++)
+ st->mem[i*st->mem_alloc_size+j] = 0;
+ st->magic_samples[i] = 0;
+ }
+ if (st->filt_len > olen)
+ {
+ /* If the new filter length is still bigger than the "augmented" length */
+ /* Copy data going backward */
+ for (j=0;j<olen-1;j++)
+ st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = st->mem[i*st->mem_alloc_size+(olen-2-j)];
+ /* Then put zeros for lack of anything better */
+ for (;j<st->filt_len-1;j++)
+ st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = 0;
+ /* Adjust last_sample */
+ st->last_sample[i] += (st->filt_len - olen)/2;
+ } else {
+ /* Put back some of the magic! */
+ st->magic_samples[i] = (olen - st->filt_len)/2;
+ for (j=0;j<st->filt_len-1+st->magic_samples[i];j++)
+ st->mem[i*st->mem_alloc_size+j] = st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]];
+ }
+ }
+ } else if (st->filt_len < old_length)
+ {
+ spx_uint32_t i;
+ /* Reduce filter length, this a bit tricky. We need to store some of the memory as "magic"
+ samples so they can be used directly as input the next time(s) */
+ for (i=0;i<st->nb_channels;i++)
+ {
+ spx_uint32_t j;
+ spx_uint32_t old_magic = st->magic_samples[i];
+ st->magic_samples[i] = (old_length - st->filt_len)/2;
+ /* We must copy some of the memory that's no longer used */
+ /* Copy data going backward */
+ for (j=0;j<st->filt_len-1+st->magic_samples[i]+old_magic;j++)
+ st->mem[i*st->mem_alloc_size+j] = st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]];
+ st->magic_samples[i] += old_magic;
+ }
+ }
+ return RESAMPLER_ERR_SUCCESS;
+
+fail:
+ st->resampler_ptr = resampler_basic_zero;
+ /* st->mem may still contain consumed input samples for the filter.
+ Restore filt_len so that filt_len - 1 still points to the position after
+ the last of these samples. */
+ st->filt_len = old_length;
+ return RESAMPLER_ERR_ALLOC_FAILED;
+}
+
+EXPORT SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels, spx_uint32_t in_rate, spx_uint32_t out_rate, int quality, int *err)
+{
+ return speex_resampler_init_frac(nb_channels, in_rate, out_rate, in_rate, out_rate, quality, err);
+}
+
+EXPORT SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels, spx_uint32_t ratio_num, spx_uint32_t ratio_den, spx_uint32_t in_rate, spx_uint32_t out_rate, int quality, int *err)
+{
+ SpeexResamplerState *st;
+ int filter_err;
+
+ if (nb_channels == 0 || ratio_num == 0 || ratio_den == 0 || quality > 10 || quality < 0)
+ {
+ if (err)
+ *err = RESAMPLER_ERR_INVALID_ARG;
+ return NULL;
+ }
+ st = (SpeexResamplerState *)speex_alloc(sizeof(SpeexResamplerState));
+ if (!st)
+ {
+ if (err)
+ *err = RESAMPLER_ERR_ALLOC_FAILED;
+ return NULL;
+ }
+ st->initialised = 0;
+ st->started = 0;
+ st->in_rate = 0;
+ st->out_rate = 0;
+ st->num_rate = 0;
+ st->den_rate = 0;
+ st->quality = -1;
+ st->sinc_table_length = 0;
+ st->mem_alloc_size = 0;
+ st->filt_len = 0;
+ st->mem = 0;
+ st->resampler_ptr = 0;
+
+ st->cutoff = 1.f;
+ st->nb_channels = nb_channels;
+ st->in_stride = 1;
+ st->out_stride = 1;
+
+ st->buffer_size = 160;
+
+ /* Per channel data */
+ if (!(st->last_sample = (spx_int32_t*)speex_alloc(nb_channels*sizeof(spx_int32_t))))
+ goto fail;
+ if (!(st->magic_samples = (spx_uint32_t*)speex_alloc(nb_channels*sizeof(spx_uint32_t))))
+ goto fail;
+ if (!(st->samp_frac_num = (spx_uint32_t*)speex_alloc(nb_channels*sizeof(spx_uint32_t))))
+ goto fail;
+
+ speex_resampler_set_quality(st, quality);
+ speex_resampler_set_rate_frac(st, ratio_num, ratio_den, in_rate, out_rate);
+
+ filter_err = update_filter(st);
+ if (filter_err == RESAMPLER_ERR_SUCCESS)
+ {
+ st->initialised = 1;
+ } else {
+ speex_resampler_destroy(st);
+ st = NULL;
+ }
+ if (err)
+ *err = filter_err;
+
+ return st;
+
+fail:
+ if (err)
+ *err = RESAMPLER_ERR_ALLOC_FAILED;
+ speex_resampler_destroy(st);
+ return NULL;
+}
+
+EXPORT void speex_resampler_destroy(SpeexResamplerState *st)
+{
+ speex_free(st->mem);
+ speex_free(st->sinc_table);
+ speex_free(st->last_sample);
+ speex_free(st->magic_samples);
+ speex_free(st->samp_frac_num);
+ speex_free(st);
+}
+
+static int speex_resampler_process_native(SpeexResamplerState *st, spx_uint32_t channel_index, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len)
+{
+ int j=0;
+ const int N = st->filt_len;
+ int out_sample = 0;
+ spx_word16_t *mem = st->mem + channel_index * st->mem_alloc_size;
+ spx_uint32_t ilen;
+
+ st->started = 1;
+
+ /* Call the right resampler through the function ptr */
+ out_sample = st->resampler_ptr(st, channel_index, mem, in_len, out, out_len);
+
+ if (st->last_sample[channel_index] < (spx_int32_t)*in_len)
+ *in_len = st->last_sample[channel_index];
+ *out_len = out_sample;
+ st->last_sample[channel_index] -= *in_len;
+
+ ilen = *in_len;
+
+ for(j=0;j<N-1;++j)
+ mem[j] = mem[j+ilen];
+
+ return RESAMPLER_ERR_SUCCESS;
+}
+
+static int speex_resampler_magic(SpeexResamplerState *st, spx_uint32_t channel_index, spx_word16_t **out, spx_uint32_t out_len) {
+ spx_uint32_t tmp_in_len = st->magic_samples[channel_index];
+ spx_word16_t *mem = st->mem + channel_index * st->mem_alloc_size;
+ const int N = st->filt_len;
+
+ speex_resampler_process_native(st, channel_index, &tmp_in_len, *out, &out_len);
+
+ st->magic_samples[channel_index] -= tmp_in_len;
+
+ /* If we couldn't process all "magic" input samples, save the rest for next time */
+ if (st->magic_samples[channel_index])
+ {
+ spx_uint32_t i;
+ for (i=0;i<st->magic_samples[channel_index];i++)
+ mem[N-1+i]=mem[N-1+i+tmp_in_len];
+ }
+ *out += out_len*st->out_stride;
+ return out_len;
+}
+
+#ifdef FIXED_POINT
+EXPORT int speex_resampler_process_int(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len)
+#else
+EXPORT int speex_resampler_process_float(SpeexResamplerState *st, spx_uint32_t channel_index, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len)
+#endif
+{
+ int j;
+ spx_uint32_t ilen = *in_len;
+ spx_uint32_t olen = *out_len;
+ spx_word16_t *x = st->mem + channel_index * st->mem_alloc_size;
+ const int filt_offs = st->filt_len - 1;
+ const spx_uint32_t xlen = st->mem_alloc_size - filt_offs;
+ const int istride = st->in_stride;
+
+ if (st->magic_samples[channel_index])
+ olen -= speex_resampler_magic(st, channel_index, &out, olen);
+ if (! st->magic_samples[channel_index]) {
+ while (ilen && olen) {
+ spx_uint32_t ichunk = (ilen > xlen) ? xlen : ilen;
+ spx_uint32_t ochunk = olen;
+
+ if (in) {
+ for(j=0;j<ichunk;++j)
+ x[j+filt_offs]=in[j*istride];
+ } else {
+ for(j=0;j<ichunk;++j)
+ x[j+filt_offs]=0;
+ }
+ speex_resampler_process_native(st, channel_index, &ichunk, out, &ochunk);
+ ilen -= ichunk;
+ olen -= ochunk;
+ out += ochunk * st->out_stride;
+ if (in)
+ in += ichunk * istride;
+ }
+ }
+ *in_len -= ilen;
+ *out_len -= olen;
+ return st->resampler_ptr == resampler_basic_zero ? RESAMPLER_ERR_ALLOC_FAILED : RESAMPLER_ERR_SUCCESS;
+}
+
+#ifdef FIXED_POINT
+EXPORT int speex_resampler_process_float(SpeexResamplerState *st, spx_uint32_t channel_index, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len)
+#else
+EXPORT int speex_resampler_process_int(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len)
+#endif
+{
+ int j;
+ const int istride_save = st->in_stride;
+ const int ostride_save = st->out_stride;
+ spx_uint32_t ilen = *in_len;
+ spx_uint32_t olen = *out_len;
+ spx_word16_t *x = st->mem + channel_index * st->mem_alloc_size;
+ const spx_uint32_t xlen = st->mem_alloc_size - (st->filt_len - 1);
+#ifdef VAR_ARRAYS
+ const unsigned int ylen = (olen < FIXED_STACK_ALLOC) ? olen : FIXED_STACK_ALLOC;
+ spx_word16_t ystack[ylen];
+#else
+ const unsigned int ylen = FIXED_STACK_ALLOC;
+ spx_word16_t ystack[FIXED_STACK_ALLOC];
+#endif
+
+ st->out_stride = 1;
+
+ while (ilen && olen) {
+ spx_word16_t *y = ystack;
+ spx_uint32_t ichunk = (ilen > xlen) ? xlen : ilen;
+ spx_uint32_t ochunk = (olen > ylen) ? ylen : olen;
+ spx_uint32_t omagic = 0;
+
+ if (st->magic_samples[channel_index]) {
+ omagic = speex_resampler_magic(st, channel_index, &y, ochunk);
+ ochunk -= omagic;
+ olen -= omagic;
+ }
+ if (! st->magic_samples[channel_index]) {
+ if (in) {
+ for(j=0;j<ichunk;++j)
+#ifdef FIXED_POINT
+ x[j+st->filt_len-1]=WORD2INT(in[j*istride_save]);
+#else
+ x[j+st->filt_len-1]=in[j*istride_save];
+#endif
+ } else {
+ for(j=0;j<ichunk;++j)
+ x[j+st->filt_len-1]=0;
+ }
+
+ speex_resampler_process_native(st, channel_index, &ichunk, y, &ochunk);
+ } else {
+ ichunk = 0;
+ ochunk = 0;
+ }
+
+ for (j=0;j<ochunk+omagic;++j)
+#ifdef FIXED_POINT
+ out[j*ostride_save] = ystack[j];
+#else
+ out[j*ostride_save] = WORD2INT(ystack[j]);
+#endif
+
+ ilen -= ichunk;
+ olen -= ochunk;
+ out += (ochunk+omagic) * ostride_save;
+ if (in)
+ in += ichunk * istride_save;
+ }
+ st->out_stride = ostride_save;
+ *in_len -= ilen;
+ *out_len -= olen;
+
+ return st->resampler_ptr == resampler_basic_zero ? RESAMPLER_ERR_ALLOC_FAILED : RESAMPLER_ERR_SUCCESS;
+}
+
+EXPORT int speex_resampler_process_interleaved_float(SpeexResamplerState *st, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len)
+{
+ spx_uint32_t i;
+ int istride_save, ostride_save;
+ spx_uint32_t bak_out_len = *out_len;
+ spx_uint32_t bak_in_len = *in_len;
+ istride_save = st->in_stride;
+ ostride_save = st->out_stride;
+ st->in_stride = st->out_stride = st->nb_channels;
+ for (i=0;i<st->nb_channels;i++)
+ {
+ *out_len = bak_out_len;
+ *in_len = bak_in_len;
+ if (in != NULL)
+ speex_resampler_process_float(st, i, in+i, in_len, out+i, out_len);
+ else
+ speex_resampler_process_float(st, i, NULL, in_len, out+i, out_len);
+ }
+ st->in_stride = istride_save;
+ st->out_stride = ostride_save;
+ return st->resampler_ptr == resampler_basic_zero ? RESAMPLER_ERR_ALLOC_FAILED : RESAMPLER_ERR_SUCCESS;
+}
+
+EXPORT int speex_resampler_process_interleaved_int(SpeexResamplerState *st, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len)
+{
+ spx_uint32_t i;
+ int istride_save, ostride_save;
+ spx_uint32_t bak_out_len = *out_len;
+ spx_uint32_t bak_in_len = *in_len;
+ istride_save = st->in_stride;
+ ostride_save = st->out_stride;
+ st->in_stride = st->out_stride = st->nb_channels;
+ for (i=0;i<st->nb_channels;i++)
+ {
+ *out_len = bak_out_len;
+ *in_len = bak_in_len;
+ if (in != NULL)
+ speex_resampler_process_int(st, i, in+i, in_len, out+i, out_len);
+ else
+ speex_resampler_process_int(st, i, NULL, in_len, out+i, out_len);
+ }
+ st->in_stride = istride_save;
+ st->out_stride = ostride_save;
+ return st->resampler_ptr == resampler_basic_zero ? RESAMPLER_ERR_ALLOC_FAILED : RESAMPLER_ERR_SUCCESS;
+}
+
+EXPORT int speex_resampler_set_rate(SpeexResamplerState *st, spx_uint32_t in_rate, spx_uint32_t out_rate)
+{
+ return speex_resampler_set_rate_frac(st, in_rate, out_rate, in_rate, out_rate);
+}
+
+EXPORT void speex_resampler_get_rate(SpeexResamplerState *st, spx_uint32_t *in_rate, spx_uint32_t *out_rate)
+{
+ *in_rate = st->in_rate;
+ *out_rate = st->out_rate;
+}
+
+static inline spx_uint32_t compute_gcd(spx_uint32_t a, spx_uint32_t b)
+{
+ while (b != 0)
+ {
+ spx_uint32_t temp = a;
+
+ a = b;
+ b = temp % b;
+ }
+ return a;
+}
+
+EXPORT int speex_resampler_set_rate_frac(SpeexResamplerState *st, spx_uint32_t ratio_num, spx_uint32_t ratio_den, spx_uint32_t in_rate, spx_uint32_t out_rate)
+{
+ spx_uint32_t fact;
+ spx_uint32_t old_den;
+ spx_uint32_t i;
+
+ if (ratio_num == 0 || ratio_den == 0)
+ return RESAMPLER_ERR_INVALID_ARG;
+
+ if (st->in_rate == in_rate && st->out_rate == out_rate && st->num_rate == ratio_num && st->den_rate == ratio_den)
+ return RESAMPLER_ERR_SUCCESS;
+
+ old_den = st->den_rate;
+ st->in_rate = in_rate;
+ st->out_rate = out_rate;
+ st->num_rate = ratio_num;
+ st->den_rate = ratio_den;
+
+ fact = compute_gcd(st->num_rate, st->den_rate);
+
+ st->num_rate /= fact;
+ st->den_rate /= fact;
+
+ if (old_den > 0)
+ {
+ for (i=0;i<st->nb_channels;i++)
+ {
+ if (multiply_frac(&st->samp_frac_num[i],st->samp_frac_num[i],st->den_rate,old_den) != RESAMPLER_ERR_SUCCESS)
+ return RESAMPLER_ERR_OVERFLOW;
+ /* Safety net */
+ if (st->samp_frac_num[i] >= st->den_rate)
+ st->samp_frac_num[i] = st->den_rate-1;
+ }
+ }
+
+ if (st->initialised)
+ return update_filter(st);
+ return RESAMPLER_ERR_SUCCESS;
+}
+
+EXPORT void speex_resampler_get_ratio(SpeexResamplerState *st, spx_uint32_t *ratio_num, spx_uint32_t *ratio_den)
+{
+ *ratio_num = st->num_rate;
+ *ratio_den = st->den_rate;
+}
+
+EXPORT int speex_resampler_set_quality(SpeexResamplerState *st, int quality)
+{
+ if (quality > 10 || quality < 0)
+ return RESAMPLER_ERR_INVALID_ARG;
+ if (st->quality == quality)
+ return RESAMPLER_ERR_SUCCESS;
+ st->quality = quality;
+ if (st->initialised)
+ return update_filter(st);
+ return RESAMPLER_ERR_SUCCESS;
+}
+
+EXPORT void speex_resampler_get_quality(SpeexResamplerState *st, int *quality)
+{
+ *quality = st->quality;
+}
+
+EXPORT void speex_resampler_set_input_stride(SpeexResamplerState *st, spx_uint32_t stride)
+{
+ st->in_stride = stride;
+}
+
+EXPORT void speex_resampler_get_input_stride(SpeexResamplerState *st, spx_uint32_t *stride)
+{
+ *stride = st->in_stride;
+}
+
+EXPORT void speex_resampler_set_output_stride(SpeexResamplerState *st, spx_uint32_t stride)
+{
+ st->out_stride = stride;
+}
+
+EXPORT void speex_resampler_get_output_stride(SpeexResamplerState *st, spx_uint32_t *stride)
+{
+ *stride = st->out_stride;
+}
+
+EXPORT int speex_resampler_get_input_latency(SpeexResamplerState *st)
+{
+ return st->filt_len / 2;
+}
+
+EXPORT int speex_resampler_get_output_latency(SpeexResamplerState *st)
+{
+ return ((st->filt_len / 2) * st->den_rate + (st->num_rate >> 1)) / st->num_rate;
+}
+
+EXPORT int speex_resampler_skip_zeros(SpeexResamplerState *st)
+{
+ spx_uint32_t i;
+ for (i=0;i<st->nb_channels;i++)
+ st->last_sample[i] = st->filt_len/2;
+ return RESAMPLER_ERR_SUCCESS;
+}
+
+EXPORT int speex_resampler_reset_mem(SpeexResamplerState *st)
+{
+ spx_uint32_t i;
+ for (i=0;i<st->nb_channels;i++)
+ {
+ st->last_sample[i] = 0;
+ st->magic_samples[i] = 0;
+ st->samp_frac_num[i] = 0;
+ }
+ for (i=0;i<st->nb_channels*(st->filt_len-1);i++)
+ st->mem[i] = 0;
+ return RESAMPLER_ERR_SUCCESS;
+}
+
+EXPORT const char *speex_resampler_strerror(int err)
+{
+ switch (err)
+ {
+ case RESAMPLER_ERR_SUCCESS:
+ return "Success.";
+ case RESAMPLER_ERR_ALLOC_FAILED:
+ return "Memory allocation failed.";
+ case RESAMPLER_ERR_BAD_STATE:
+ return "Bad resampler state.";
+ case RESAMPLER_ERR_INVALID_ARG:
+ return "Invalid argument.";
+ case RESAMPLER_ERR_PTR_OVERLAP:
+ return "Input and output buffers overlap.";
+ default:
+ return "Unknown error. Bad error code or strange version mismatch.";
+ }
+}
diff --git a/lib/speexdsp/libspeexdsp/resample_neon.h b/lib/speexdsp/libspeexdsp/resample_neon.h
new file mode 100644
index 00000000..e14ffe19
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/resample_neon.h
@@ -0,0 +1,339 @@
+/* Copyright (C) 2007-2008 Jean-Marc Valin
+ * Copyright (C) 2008 Thorvald Natvig
+ * Copyright (C) 2011 Texas Instruments
+ * author Jyri Sarha
+ */
+/**
+ @file resample_neon.h
+ @brief Resampler functions (NEON version)
+*/
+/*
+ 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.
+*/
+
+#ifdef FIXED_POINT
+#if defined(__aarch64__)
+static inline int32_t saturate_32bit_to_16bit(int32_t a) {
+ int32_t ret;
+ asm ("fmov s0, %w[a]\n"
+ "sqxtn h0, s0\n"
+ "sxtl v0.4s, v0.4h\n"
+ "fmov %w[ret], s0\n"
+ : [ret] "=r" (ret)
+ : [a] "r" (a)
+ : "v0" );
+ return ret;
+}
+#elif defined(__thumb2__)
+static inline int32_t saturate_32bit_to_16bit(int32_t a) {
+ int32_t ret;
+ asm ("ssat %[ret], #16, %[a]"
+ : [ret] "=r" (ret)
+ : [a] "r" (a)
+ : );
+ return ret;
+}
+#else
+static inline int32_t saturate_32bit_to_16bit(int32_t a) {
+ int32_t ret;
+ asm ("vmov.s32 d0[0], %[a]\n"
+ "vqmovn.s32 d0, q0\n"
+ "vmov.s16 %[ret], d0[0]\n"
+ : [ret] "=r" (ret)
+ : [a] "r" (a)
+ : "q0");
+ return ret;
+}
+#endif
+#undef WORD2INT
+#define WORD2INT(x) (saturate_32bit_to_16bit(x))
+
+#define OVERRIDE_INNER_PRODUCT_SINGLE
+/* Only works when len % 4 == 0 and len >= 4 */
+#if defined(__aarch64__)
+static inline int32_t inner_product_single(const int16_t *a, const int16_t *b, unsigned int len)
+{
+ int32_t ret;
+ uint32_t remainder = len % 16;
+ len = len - remainder;
+
+ asm volatile (" cmp %w[len], #0\n"
+ " b.ne 1f\n"
+ " ld1 {v16.4h}, [%[b]], #8\n"
+ " ld1 {v20.4h}, [%[a]], #8\n"
+ " subs %w[remainder], %w[remainder], #4\n"
+ " smull v0.4s, v16.4h, v20.4h\n"
+ " b.ne 4f\n"
+ " b 5f\n"
+ "1:"
+ " ld1 {v16.4h, v17.4h, v18.4h, v19.4h}, [%[b]], #32\n"
+ " ld1 {v20.4h, v21.4h, v22.4h, v23.4h}, [%[a]], #32\n"
+ " subs %w[len], %w[len], #16\n"
+ " smull v0.4s, v16.4h, v20.4h\n"
+ " smlal v0.4s, v17.4h, v21.4h\n"
+ " smlal v0.4s, v18.4h, v22.4h\n"
+ " smlal v0.4s, v19.4h, v23.4h\n"
+ " b.eq 3f\n"
+ "2:"
+ " ld1 {v16.4h, v17.4h, v18.4h, v19.4h}, [%[b]], #32\n"
+ " ld1 {v20.4h, v21.4h, v22.4h, v23.4h}, [%[a]], #32\n"
+ " subs %w[len], %w[len], #16\n"
+ " smlal v0.4s, v16.4h, v20.4h\n"
+ " smlal v0.4s, v17.4h, v21.4h\n"
+ " smlal v0.4s, v18.4h, v22.4h\n"
+ " smlal v0.4s, v19.4h, v23.4h\n"
+ " b.ne 2b\n"
+ "3:"
+ " cmp %w[remainder], #0\n"
+ " b.eq 5f\n"
+ "4:"
+ " ld1 {v18.4h}, [%[b]], #8\n"
+ " ld1 {v22.4h}, [%[a]], #8\n"
+ " subs %w[remainder], %w[remainder], #4\n"
+ " smlal v0.4s, v18.4h, v22.4h\n"
+ " b.ne 4b\n"
+ "5:"
+ " saddlv d0, v0.4s\n"
+ " sqxtn s0, d0\n"
+ " sqrshrn h0, s0, #15\n"
+ " sxtl v0.4s, v0.4h\n"
+ " fmov %w[ret], s0\n"
+ : [ret] "=r" (ret), [a] "+r" (a), [b] "+r" (b),
+ [len] "+r" (len), [remainder] "+r" (remainder)
+ :
+ : "cc", "v0",
+ "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23");
+ return ret;
+}
+#else
+static inline int32_t inner_product_single(const int16_t *a, const int16_t *b, unsigned int len)
+{
+ int32_t ret;
+ uint32_t remainder = len % 16;
+ len = len - remainder;
+
+ asm volatile (" cmp %[len], #0\n"
+ " bne 1f\n"
+ " vld1.16 {d16}, [%[b]]!\n"
+ " vld1.16 {d20}, [%[a]]!\n"
+ " subs %[remainder], %[remainder], #4\n"
+ " vmull.s16 q0, d16, d20\n"
+ " beq 5f\n"
+ " b 4f\n"
+ "1:"
+ " vld1.16 {d16, d17, d18, d19}, [%[b]]!\n"
+ " vld1.16 {d20, d21, d22, d23}, [%[a]]!\n"
+ " subs %[len], %[len], #16\n"
+ " vmull.s16 q0, d16, d20\n"
+ " vmlal.s16 q0, d17, d21\n"
+ " vmlal.s16 q0, d18, d22\n"
+ " vmlal.s16 q0, d19, d23\n"
+ " beq 3f\n"
+ "2:"
+ " vld1.16 {d16, d17, d18, d19}, [%[b]]!\n"
+ " vld1.16 {d20, d21, d22, d23}, [%[a]]!\n"
+ " subs %[len], %[len], #16\n"
+ " vmlal.s16 q0, d16, d20\n"
+ " vmlal.s16 q0, d17, d21\n"
+ " vmlal.s16 q0, d18, d22\n"
+ " vmlal.s16 q0, d19, d23\n"
+ " bne 2b\n"
+ "3:"
+ " cmp %[remainder], #0\n"
+ " beq 5f\n"
+ "4:"
+ " vld1.16 {d16}, [%[b]]!\n"
+ " vld1.16 {d20}, [%[a]]!\n"
+ " subs %[remainder], %[remainder], #4\n"
+ " vmlal.s16 q0, d16, d20\n"
+ " bne 4b\n"
+ "5:"
+ " vaddl.s32 q0, d0, d1\n"
+ " vadd.s64 d0, d0, d1\n"
+ " vqmovn.s64 d0, q0\n"
+ " vqrshrn.s32 d0, q0, #15\n"
+ " vmov.s16 %[ret], d0[0]\n"
+ : [ret] "=r" (ret), [a] "+r" (a), [b] "+r" (b),
+ [len] "+r" (len), [remainder] "+r" (remainder)
+ :
+ : "cc", "q0",
+ "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23");
+
+ return ret;
+}
+#endif // !defined(__aarch64__)
+
+#elif defined(FLOATING_POINT)
+#if defined(__aarch64__)
+static inline int32_t saturate_float_to_16bit(float a) {
+ int32_t ret;
+ asm ("fcvtas s1, %s[a]\n"
+ "sqxtn h1, s1\n"
+ "sxtl v1.4s, v1.4h\n"
+ "fmov %w[ret], s1\n"
+ : [ret] "=r" (ret)
+ : [a] "w" (a)
+ : "v1");
+ return ret;
+}
+#else
+static inline int32_t saturate_float_to_16bit(float a) {
+ int32_t ret;
+ asm ("vmov.f32 d0[0], %[a]\n"
+ "vcvt.s32.f32 d0, d0, #15\n"
+ "vqrshrn.s32 d0, q0, #15\n"
+ "vmov.s16 %[ret], d0[0]\n"
+ : [ret] "=r" (ret)
+ : [a] "r" (a)
+ : "q0");
+ return ret;
+}
+#endif
+
+#undef WORD2INT
+#define WORD2INT(x) (saturate_float_to_16bit(x))
+
+#define OVERRIDE_INNER_PRODUCT_SINGLE
+/* Only works when len % 4 == 0 and len >= 4 */
+#if defined(__aarch64__)
+static inline float inner_product_single(const float *a, const float *b, unsigned int len)
+{
+ float ret;
+ uint32_t remainder = len % 16;
+ len = len - remainder;
+
+ asm volatile (" cmp %w[len], #0\n"
+ " b.ne 1f\n"
+ " ld1 {v16.4s}, [%[b]], #16\n"
+ " ld1 {v20.4s}, [%[a]], #16\n"
+ " subs %w[remainder], %w[remainder], #4\n"
+ " fmul v1.4s, v16.4s, v20.4s\n"
+ " b.ne 4f\n"
+ " b 5f\n"
+ "1:"
+ " ld1 {v16.4s, v17.4s, v18.4s, v19.4s}, [%[b]], #64\n"
+ " ld1 {v20.4s, v21.4s, v22.4s, v23.4s}, [%[a]], #64\n"
+ " subs %w[len], %w[len], #16\n"
+ " fmul v1.4s, v16.4s, v20.4s\n"
+ " fmul v2.4s, v17.4s, v21.4s\n"
+ " fmul v3.4s, v18.4s, v22.4s\n"
+ " fmul v4.4s, v19.4s, v23.4s\n"
+ " b.eq 3f\n"
+ "2:"
+ " ld1 {v16.4s, v17.4s, v18.4s, v19.4s}, [%[b]], #64\n"
+ " ld1 {v20.4s, v21.4s, v22.4s, v23.4s}, [%[a]], #64\n"
+ " subs %w[len], %w[len], #16\n"
+ " fmla v1.4s, v16.4s, v20.4s\n"
+ " fmla v2.4s, v17.4s, v21.4s\n"
+ " fmla v3.4s, v18.4s, v22.4s\n"
+ " fmla v4.4s, v19.4s, v23.4s\n"
+ " b.ne 2b\n"
+ "3:"
+ " fadd v16.4s, v1.4s, v2.4s\n"
+ " fadd v17.4s, v3.4s, v4.4s\n"
+ " cmp %w[remainder], #0\n"
+ " fadd v1.4s, v16.4s, v17.4s\n"
+ " b.eq 5f\n"
+ "4:"
+ " ld1 {v18.4s}, [%[b]], #16\n"
+ " ld1 {v22.4s}, [%[a]], #16\n"
+ " subs %w[remainder], %w[remainder], #4\n"
+ " fmla v1.4s, v18.4s, v22.4s\n"
+ " b.ne 4b\n"
+ "5:"
+ " faddp v1.4s, v1.4s, v1.4s\n"
+ " faddp %[ret].4s, v1.4s, v1.4s\n"
+ : [ret] "=w" (ret), [a] "+r" (a), [b] "+r" (b),
+ [len] "+r" (len), [remainder] "+r" (remainder)
+ :
+ : "cc", "v1", "v2", "v3", "v4",
+ "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23");
+ return ret;
+}
+#else
+static inline float inner_product_single(const float *a, const float *b, unsigned int len)
+{
+ float ret;
+ uint32_t remainder = len % 16;
+ len = len - remainder;
+
+ asm volatile (" cmp %[len], #0\n"
+ " bne 1f\n"
+ " vld1.32 {q4}, [%[b]]!\n"
+ " vld1.32 {q8}, [%[a]]!\n"
+ " subs %[remainder], %[remainder], #4\n"
+ " vmul.f32 q0, q4, q8\n"
+ " bne 4f\n"
+ " b 5f\n"
+ "1:"
+ " vld1.32 {q4, q5}, [%[b]]!\n"
+ " vld1.32 {q8, q9}, [%[a]]!\n"
+ " vld1.32 {q6, q7}, [%[b]]!\n"
+ " vld1.32 {q10, q11}, [%[a]]!\n"
+ " subs %[len], %[len], #16\n"
+ " vmul.f32 q0, q4, q8\n"
+ " vmul.f32 q1, q5, q9\n"
+ " vmul.f32 q2, q6, q10\n"
+ " vmul.f32 q3, q7, q11\n"
+ " beq 3f\n"
+ "2:"
+ " vld1.32 {q4, q5}, [%[b]]!\n"
+ " vld1.32 {q8, q9}, [%[a]]!\n"
+ " vld1.32 {q6, q7}, [%[b]]!\n"
+ " vld1.32 {q10, q11}, [%[a]]!\n"
+ " subs %[len], %[len], #16\n"
+ " vmla.f32 q0, q4, q8\n"
+ " vmla.f32 q1, q5, q9\n"
+ " vmla.f32 q2, q6, q10\n"
+ " vmla.f32 q3, q7, q11\n"
+ " bne 2b\n"
+ "3:"
+ " vadd.f32 q4, q0, q1\n"
+ " vadd.f32 q5, q2, q3\n"
+ " cmp %[remainder], #0\n"
+ " vadd.f32 q0, q4, q5\n"
+ " beq 5f\n"
+ "4:"
+ " vld1.32 {q6}, [%[b]]!\n"
+ " vld1.32 {q10}, [%[a]]!\n"
+ " subs %[remainder], %[remainder], #4\n"
+ " vmla.f32 q0, q6, q10\n"
+ " bne 4b\n"
+ "5:"
+ " vadd.f32 d0, d0, d1\n"
+ " vpadd.f32 d0, d0, d0\n"
+ " vmov.f32 %[ret], d0[0]\n"
+ : [ret] "=r" (ret), [a] "+r" (a), [b] "+r" (b),
+ [len] "+l" (len), [remainder] "+l" (remainder)
+ :
+ : "cc", "q0", "q1", "q2", "q3",
+ "q4", "q5", "q6", "q7", "q8", "q9", "q10", "q11");
+ return ret;
+}
+#endif // defined(__aarch64__)
+#endif
diff --git a/lib/speexdsp/libspeexdsp/resample_sse.h b/lib/speexdsp/libspeexdsp/resample_sse.h
new file mode 100644
index 00000000..00dc2946
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/resample_sse.h
@@ -0,0 +1,128 @@
+/* Copyright (C) 2007-2008 Jean-Marc Valin
+ * Copyright (C) 2008 Thorvald Natvig
+ */
+/**
+ @file resample_sse.h
+ @brief Resampler functions (SSE version)
+*/
+/*
+ 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 <xmmintrin.h>
+
+#define OVERRIDE_INNER_PRODUCT_SINGLE
+static inline float inner_product_single(const float *a, const float *b, unsigned int len)
+{
+ int i;
+ float ret;
+ __m128 sum = _mm_setzero_ps();
+ for (i=0;i<len;i+=8)
+ {
+ sum = _mm_add_ps(sum, _mm_mul_ps(_mm_loadu_ps(a+i), _mm_loadu_ps(b+i)));
+ sum = _mm_add_ps(sum, _mm_mul_ps(_mm_loadu_ps(a+i+4), _mm_loadu_ps(b+i+4)));
+ }
+ sum = _mm_add_ps(sum, _mm_movehl_ps(sum, sum));
+ sum = _mm_add_ss(sum, _mm_shuffle_ps(sum, sum, 0x55));
+ _mm_store_ss(&ret, sum);
+ return ret;
+}
+
+#define OVERRIDE_INTERPOLATE_PRODUCT_SINGLE
+static inline float interpolate_product_single(const float *a, const float *b, unsigned int len, const spx_uint32_t oversample, float *frac) {
+ int i;
+ float ret;
+ __m128 sum = _mm_setzero_ps();
+ __m128 f = _mm_loadu_ps(frac);
+ for(i=0;i<len;i+=2)
+ {
+ sum = _mm_add_ps(sum, _mm_mul_ps(_mm_load1_ps(a+i), _mm_loadu_ps(b+i*oversample)));
+ sum = _mm_add_ps(sum, _mm_mul_ps(_mm_load1_ps(a+i+1), _mm_loadu_ps(b+(i+1)*oversample)));
+ }
+ sum = _mm_mul_ps(f, sum);
+ sum = _mm_add_ps(sum, _mm_movehl_ps(sum, sum));
+ sum = _mm_add_ss(sum, _mm_shuffle_ps(sum, sum, 0x55));
+ _mm_store_ss(&ret, sum);
+ return ret;
+}
+
+#ifdef USE_SSE2
+#include <emmintrin.h>
+#define OVERRIDE_INNER_PRODUCT_DOUBLE
+
+static inline double inner_product_double(const float *a, const float *b, unsigned int len)
+{
+ int i;
+ double ret;
+ __m128d sum = _mm_setzero_pd();
+ __m128 t;
+ for (i=0;i<len;i+=8)
+ {
+ t = _mm_mul_ps(_mm_loadu_ps(a+i), _mm_loadu_ps(b+i));
+ sum = _mm_add_pd(sum, _mm_cvtps_pd(t));
+ sum = _mm_add_pd(sum, _mm_cvtps_pd(_mm_movehl_ps(t, t)));
+
+ t = _mm_mul_ps(_mm_loadu_ps(a+i+4), _mm_loadu_ps(b+i+4));
+ sum = _mm_add_pd(sum, _mm_cvtps_pd(t));
+ sum = _mm_add_pd(sum, _mm_cvtps_pd(_mm_movehl_ps(t, t)));
+ }
+ sum = _mm_add_sd(sum, _mm_unpackhi_pd(sum, sum));
+ _mm_store_sd(&ret, sum);
+ return ret;
+}
+
+#define OVERRIDE_INTERPOLATE_PRODUCT_DOUBLE
+static inline double interpolate_product_double(const float *a, const float *b, unsigned int len, const spx_uint32_t oversample, float *frac) {
+ int i;
+ double ret;
+ __m128d sum;
+ __m128d sum1 = _mm_setzero_pd();
+ __m128d sum2 = _mm_setzero_pd();
+ __m128 f = _mm_loadu_ps(frac);
+ __m128d f1 = _mm_cvtps_pd(f);
+ __m128d f2 = _mm_cvtps_pd(_mm_movehl_ps(f,f));
+ __m128 t;
+ for(i=0;i<len;i+=2)
+ {
+ t = _mm_mul_ps(_mm_load1_ps(a+i), _mm_loadu_ps(b+i*oversample));
+ sum1 = _mm_add_pd(sum1, _mm_cvtps_pd(t));
+ sum2 = _mm_add_pd(sum2, _mm_cvtps_pd(_mm_movehl_ps(t, t)));
+
+ t = _mm_mul_ps(_mm_load1_ps(a+i+1), _mm_loadu_ps(b+(i+1)*oversample));
+ sum1 = _mm_add_pd(sum1, _mm_cvtps_pd(t));
+ sum2 = _mm_add_pd(sum2, _mm_cvtps_pd(_mm_movehl_ps(t, t)));
+ }
+ sum1 = _mm_mul_pd(f1, sum1);
+ sum2 = _mm_mul_pd(f2, sum2);
+ sum = _mm_add_pd(sum1, sum2);
+ sum = _mm_add_sd(sum, _mm_unpackhi_pd(sum, sum));
+ _mm_store_sd(&ret, sum);
+ return ret;
+}
+
+#endif
diff --git a/lib/speexdsp/libspeexdsp/scal.c b/lib/speexdsp/libspeexdsp/scal.c
new file mode 100644
index 00000000..807cb512
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/scal.c
@@ -0,0 +1,293 @@
+/* Copyright (C) 2006-2008 CSIRO, Jean-Marc Valin, Xiph.Org Foundation
+
+ File: scal.c
+ Shaped comb-allpass filter for channel decorrelation
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. 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.
+
+ 3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 algorithm implemented here is described in:
+
+* J.-M. Valin, Perceptually-Motivated Nonlinear Channel Decorrelation For
+ Stereo Acoustic Echo Cancellation, Accepted for Joint Workshop on
+ Hands­free Speech Communication and Microphone Arrays (HSCMA), 2008.
+ http://people.xiph.org/~jm/papers/valin_hscma2008.pdf
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "speex/speex_echo.h"
+#include "vorbis_psy.h"
+#include "arch.h"
+#include "os_support.h"
+#include "smallft.h"
+#include <math.h>
+#include <stdlib.h>
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846 /* pi */
+#endif
+
+#define ALLPASS_ORDER 20
+
+struct SpeexDecorrState_ {
+ int rate;
+ int channels;
+ int frame_size;
+#ifdef VORBIS_PSYCHO
+ VorbisPsy *psy;
+ struct drft_lookup lookup;
+ float *wola_mem;
+ float *curve;
+#endif
+ float *vorbis_win;
+ int seed;
+ float *y;
+
+ /* Per-channel stuff */
+ float *buff;
+ float (*ring)[ALLPASS_ORDER];
+ int *ringID;
+ int *order;
+ float *alpha;
+};
+
+
+
+EXPORT SpeexDecorrState *speex_decorrelate_new(int rate, int channels, int frame_size)
+{
+ int i, ch;
+ SpeexDecorrState *st = speex_alloc(sizeof(SpeexDecorrState));
+ st->rate = rate;
+ st->channels = channels;
+ st->frame_size = frame_size;
+#ifdef VORBIS_PSYCHO
+ st->psy = vorbis_psy_init(rate, 2*frame_size);
+ spx_drft_init(&st->lookup, 2*frame_size);
+ st->wola_mem = speex_alloc(frame_size*sizeof(float));
+ st->curve = speex_alloc(frame_size*sizeof(float));
+#endif
+ st->y = speex_alloc(frame_size*sizeof(float));
+
+ st->buff = speex_alloc(channels*2*frame_size*sizeof(float));
+ st->ringID = speex_alloc(channels*sizeof(int));
+ st->order = speex_alloc(channels*sizeof(int));
+ st->alpha = speex_alloc(channels*sizeof(float));
+ st->ring = speex_alloc(channels*ALLPASS_ORDER*sizeof(float));
+
+ /*FIXME: The +20 is there only as a kludge for ALL_PASS_OLA*/
+ st->vorbis_win = speex_alloc((2*frame_size+20)*sizeof(float));
+ for (i=0;i<2*frame_size;i++)
+ st->vorbis_win[i] = sin(.5*M_PI* sin(M_PI*i/(2*frame_size))*sin(M_PI*i/(2*frame_size)) );
+ st->seed = rand();
+
+ for (ch=0;ch<channels;ch++)
+ {
+ for (i=0;i<ALLPASS_ORDER;i++)
+ st->ring[ch][i] = 0;
+ st->ringID[ch] = 0;
+ st->alpha[ch] = 0;
+ st->order[ch] = 10;
+ }
+ return st;
+}
+
+static float uni_rand(int *seed)
+{
+ const unsigned int jflone = 0x3f800000;
+ const unsigned int jflmsk = 0x007fffff;
+ union {int i; float f;} ran;
+ *seed = 1664525 * *seed + 1013904223;
+ ran.i = jflone | (jflmsk & *seed);
+ ran.f -= 1.5;
+ return 2*ran.f;
+}
+
+static unsigned int irand(int *seed)
+{
+ *seed = 1664525 * *seed + 1013904223;
+ return ((unsigned int)*seed)>>16;
+}
+
+
+EXPORT void speex_decorrelate(SpeexDecorrState *st, const spx_int16_t *in, spx_int16_t *out, int strength)
+{
+ int ch;
+ float amount;
+
+ if (strength<0)
+ strength = 0;
+ if (strength>100)
+ strength = 100;
+
+ amount = .01*strength;
+ for (ch=0;ch<st->channels;ch++)
+ {
+ int i;
+ int N=2*st->frame_size;
+ float beta, beta2;
+ float *x;
+ float max_alpha = 0;
+
+ float *buff;
+ float *ring;
+ int ringID;
+ int order;
+ float alpha;
+
+ buff = st->buff+ch*2*st->frame_size;
+ ring = st->ring[ch];
+ ringID = st->ringID[ch];
+ order = st->order[ch];
+ alpha = st->alpha[ch];
+
+ for (i=0;i<st->frame_size;i++)
+ buff[i] = buff[i+st->frame_size];
+ for (i=0;i<st->frame_size;i++)
+ buff[i+st->frame_size] = in[i*st->channels+ch];
+
+ x = buff+st->frame_size;
+
+ if (amount>1)
+ beta = 1-sqrt(.4*amount);
+ else
+ beta = 1-0.63246*amount;
+ if (beta<0)
+ beta = 0;
+
+ beta2 = beta;
+ for (i=0;i<st->frame_size;i++)
+ {
+ st->y[i] = alpha*(x[i-ALLPASS_ORDER+order]-beta*x[i-ALLPASS_ORDER+order-1])*st->vorbis_win[st->frame_size+i+order]
+ + x[i-ALLPASS_ORDER]*st->vorbis_win[st->frame_size+i]
+ - alpha*(ring[ringID]
+ - beta*ring[ringID+1>=order?0:ringID+1]);
+ ring[ringID++]=st->y[i];
+ st->y[i] *= st->vorbis_win[st->frame_size+i];
+ if (ringID>=order)
+ ringID=0;
+ }
+ order = order+(irand(&st->seed)%3)-1;
+ if (order < 5)
+ order = 5;
+ if (order > 10)
+ order = 10;
+ /*order = 5+(irand(&st->seed)%6);*/
+ max_alpha = pow(.96+.04*(amount-1),order);
+ if (max_alpha > .98/(1.+beta2))
+ max_alpha = .98/(1.+beta2);
+
+ alpha = alpha + .4*uni_rand(&st->seed);
+ if (alpha > max_alpha)
+ alpha = max_alpha;
+ if (alpha < -max_alpha)
+ alpha = -max_alpha;
+ for (i=0;i<ALLPASS_ORDER;i++)
+ ring[i] = 0;
+ ringID = 0;
+ for (i=0;i<st->frame_size;i++)
+ {
+ float tmp = alpha*(x[i-ALLPASS_ORDER+order]-beta*x[i-ALLPASS_ORDER+order-1])*st->vorbis_win[i+order]
+ + x[i-ALLPASS_ORDER]*st->vorbis_win[i]
+ - alpha*(ring[ringID]
+ - beta*ring[ringID+1>=order?0:ringID+1]);
+ ring[ringID++]=tmp;
+ tmp *= st->vorbis_win[i];
+ if (ringID>=order)
+ ringID=0;
+ st->y[i] += tmp;
+ }
+
+#ifdef VORBIS_PSYCHO
+ float frame[N];
+ float scale = 1./N;
+ for (i=0;i<2*st->frame_size;i++)
+ frame[i] = buff[i];
+ //float coef = .5*0.78130;
+ float coef = M_PI*0.075063 * 0.93763 * amount * .8 * 0.707;
+ compute_curve(st->psy, buff, st->curve);
+ for (i=1;i<st->frame_size;i++)
+ {
+ float x1,x2;
+ float gain;
+ do {
+ x1 = uni_rand(&st->seed);
+ x2 = uni_rand(&st->seed);
+ } while (x1*x1+x2*x2 > 1.);
+ gain = coef*sqrt(.1+st->curve[i]);
+ frame[2*i-1] = gain*x1;
+ frame[2*i] = gain*x2;
+ }
+ frame[0] = coef*uni_rand(&st->seed)*sqrt(.1+st->curve[0]);
+ frame[2*st->frame_size-1] = coef*uni_rand(&st->seed)*sqrt(.1+st->curve[st->frame_size-1]);
+ spx_drft_backward(&st->lookup,frame);
+ for (i=0;i<2*st->frame_size;i++)
+ frame[i] *= st->vorbis_win[i];
+#endif
+
+ for (i=0;i<st->frame_size;i++)
+ {
+#ifdef VORBIS_PSYCHO
+ float tmp = st->y[i] + frame[i] + st->wola_mem[i];
+ st->wola_mem[i] = frame[i+st->frame_size];
+#else
+ float tmp = st->y[i];
+#endif
+ if (tmp>32767)
+ tmp = 32767;
+ if (tmp < -32767)
+ tmp = -32767;
+ out[i*st->channels+ch] = tmp;
+ }
+
+ st->ringID[ch] = ringID;
+ st->order[ch] = order;
+ st->alpha[ch] = alpha;
+
+ }
+}
+
+EXPORT void speex_decorrelate_destroy(SpeexDecorrState *st)
+{
+#ifdef VORBIS_PSYCHO
+ vorbis_psy_destroy(st->psy);
+ speex_free(st->wola_mem);
+ speex_free(st->curve);
+#endif
+ speex_free(st->buff);
+ speex_free(st->ring);
+ speex_free(st->ringID);
+ speex_free(st->alpha);
+ speex_free(st->vorbis_win);
+ speex_free(st->order);
+ speex_free(st->y);
+ speex_free(st);
+}
diff --git a/lib/speexdsp/libspeexdsp/smallft.c b/lib/speexdsp/libspeexdsp/smallft.c
new file mode 100644
index 00000000..82c3b0a4
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/smallft.c
@@ -0,0 +1,1261 @@
+/********************************************************************
+ * *
+ * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
+ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
+ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
+ * *
+ * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001 *
+ * by the XIPHOPHORUS Company http://www.xiph.org/ *
+ * *
+ ********************************************************************
+
+ function: *unnormalized* fft transform
+ last mod: $Id: smallft.c,v 1.19 2003/10/08 05:12:37 jm Exp $
+
+ ********************************************************************/
+
+/* FFT implementation from OggSquish, minus cosine transforms,
+ * minus all but radix 2/4 case. In Vorbis we only need this
+ * cut-down version.
+ *
+ * To do more than just power-of-two sized vectors, see the full
+ * version I wrote for NetLib.
+ *
+ * Note that the packing is a little strange; rather than the FFT r/i
+ * packing following R_0, I_n, R_1, I_1, R_2, I_2 ... R_n-1, I_n-1,
+ * it follows R_0, R_1, I_1, R_2, I_2 ... R_n-1, I_n-1, I_n like the
+ * FORTRAN version
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <math.h>
+#include "smallft.h"
+#include "arch.h"
+#include "os_support.h"
+
+static void drfti1(int n, float *wa, int *ifac){
+ static int ntryh[4] = { 4,2,3,5 };
+ static float tpi = 6.28318530717958648f;
+ float arg,argh,argld,fi;
+ int ntry=0,i,j=-1;
+ int k1, l1, l2, ib;
+ int ld, ii, ip, is, nq, nr;
+ int ido, ipm, nfm1;
+ int nl=n;
+ int nf=0;
+
+ L101:
+ j++;
+ if (j < 4)
+ ntry=ntryh[j];
+ else
+ ntry+=2;
+
+ L104:
+ nq=nl/ntry;
+ nr=nl-ntry*nq;
+ if (nr!=0) goto L101;
+
+ nf++;
+ ifac[nf+1]=ntry;
+ nl=nq;
+ if(ntry!=2)goto L107;
+ if(nf==1)goto L107;
+
+ for (i=1;i<nf;i++){
+ ib=nf-i+1;
+ ifac[ib+1]=ifac[ib];
+ }
+ ifac[2] = 2;
+
+ L107:
+ if(nl!=1)goto L104;
+ ifac[0]=n;
+ ifac[1]=nf;
+ argh=tpi/n;
+ is=0;
+ nfm1=nf-1;
+ l1=1;
+
+ if(nfm1==0)return;
+
+ for (k1=0;k1<nfm1;k1++){
+ ip=ifac[k1+2];
+ ld=0;
+ l2=l1*ip;
+ ido=n/l2;
+ ipm=ip-1;
+
+ for (j=0;j<ipm;j++){
+ ld+=l1;
+ i=is;
+ argld=(float)ld*argh;
+ fi=0.f;
+ for (ii=2;ii<ido;ii+=2){
+ fi+=1.f;
+ arg=fi*argld;
+ wa[i++]=cos(arg);
+ wa[i++]=sin(arg);
+ }
+ is+=ido;
+ }
+ l1=l2;
+ }
+}
+
+static void fdrffti(int n, float *wsave, int *ifac){
+
+ if (n == 1) return;
+ drfti1(n, wsave+n, ifac);
+}
+
+static void dradf2(int ido,int l1,float *cc,float *ch,float *wa1){
+ int i,k;
+ float ti2,tr2;
+ int t0,t1,t2,t3,t4,t5,t6;
+
+ t1=0;
+ t0=(t2=l1*ido);
+ t3=ido<<1;
+ for(k=0;k<l1;k++){
+ ch[t1<<1]=cc[t1]+cc[t2];
+ ch[(t1<<1)+t3-1]=cc[t1]-cc[t2];
+ t1+=ido;
+ t2+=ido;
+ }
+
+ if(ido<2)return;
+ if(ido==2)goto L105;
+
+ t1=0;
+ t2=t0;
+ for(k=0;k<l1;k++){
+ t3=t2;
+ t4=(t1<<1)+(ido<<1);
+ t5=t1;
+ t6=t1+t1;
+ for(i=2;i<ido;i+=2){
+ t3+=2;
+ t4-=2;
+ t5+=2;
+ t6+=2;
+ tr2=wa1[i-2]*cc[t3-1]+wa1[i-1]*cc[t3];
+ ti2=wa1[i-2]*cc[t3]-wa1[i-1]*cc[t3-1];
+ ch[t6]=cc[t5]+ti2;
+ ch[t4]=ti2-cc[t5];
+ ch[t6-1]=cc[t5-1]+tr2;
+ ch[t4-1]=cc[t5-1]-tr2;
+ }
+ t1+=ido;
+ t2+=ido;
+ }
+
+ if(ido%2==1)return;
+
+ L105:
+ t3=(t2=(t1=ido)-1);
+ t2+=t0;
+ for(k=0;k<l1;k++){
+ ch[t1]=-cc[t2];
+ ch[t1-1]=cc[t3];
+ t1+=ido<<1;
+ t2+=ido;
+ t3+=ido;
+ }
+}
+
+static void dradf4(int ido,int l1,float *cc,float *ch,float *wa1,
+ float *wa2,float *wa3){
+ static float hsqt2 = .70710678118654752f;
+ int i,k,t0,t1,t2,t3,t4,t5,t6;
+ float ci2,ci3,ci4,cr2,cr3,cr4,ti1,ti2,ti3,ti4,tr1,tr2,tr3,tr4;
+ t0=l1*ido;
+
+ t1=t0;
+ t4=t1<<1;
+ t2=t1+(t1<<1);
+ t3=0;
+
+ for(k=0;k<l1;k++){
+ tr1=cc[t1]+cc[t2];
+ tr2=cc[t3]+cc[t4];
+
+ ch[t5=t3<<2]=tr1+tr2;
+ ch[(ido<<2)+t5-1]=tr2-tr1;
+ ch[(t5+=(ido<<1))-1]=cc[t3]-cc[t4];
+ ch[t5]=cc[t2]-cc[t1];
+
+ t1+=ido;
+ t2+=ido;
+ t3+=ido;
+ t4+=ido;
+ }
+
+ if(ido<2)return;
+ if(ido==2)goto L105;
+
+
+ t1=0;
+ for(k=0;k<l1;k++){
+ t2=t1;
+ t4=t1<<2;
+ t5=(t6=ido<<1)+t4;
+ for(i=2;i<ido;i+=2){
+ t3=(t2+=2);
+ t4+=2;
+ t5-=2;
+
+ t3+=t0;
+ cr2=wa1[i-2]*cc[t3-1]+wa1[i-1]*cc[t3];
+ ci2=wa1[i-2]*cc[t3]-wa1[i-1]*cc[t3-1];
+ t3+=t0;
+ cr3=wa2[i-2]*cc[t3-1]+wa2[i-1]*cc[t3];
+ ci3=wa2[i-2]*cc[t3]-wa2[i-1]*cc[t3-1];
+ t3+=t0;
+ cr4=wa3[i-2]*cc[t3-1]+wa3[i-1]*cc[t3];
+ ci4=wa3[i-2]*cc[t3]-wa3[i-1]*cc[t3-1];
+
+ tr1=cr2+cr4;
+ tr4=cr4-cr2;
+ ti1=ci2+ci4;
+ ti4=ci2-ci4;
+
+ ti2=cc[t2]+ci3;
+ ti3=cc[t2]-ci3;
+ tr2=cc[t2-1]+cr3;
+ tr3=cc[t2-1]-cr3;
+
+ ch[t4-1]=tr1+tr2;
+ ch[t4]=ti1+ti2;
+
+ ch[t5-1]=tr3-ti4;
+ ch[t5]=tr4-ti3;
+
+ ch[t4+t6-1]=ti4+tr3;
+ ch[t4+t6]=tr4+ti3;
+
+ ch[t5+t6-1]=tr2-tr1;
+ ch[t5+t6]=ti1-ti2;
+ }
+ t1+=ido;
+ }
+ if(ido&1)return;
+
+ L105:
+
+ t2=(t1=t0+ido-1)+(t0<<1);
+ t3=ido<<2;
+ t4=ido;
+ t5=ido<<1;
+ t6=ido;
+
+ for(k=0;k<l1;k++){
+ ti1=-hsqt2*(cc[t1]+cc[t2]);
+ tr1=hsqt2*(cc[t1]-cc[t2]);
+
+ ch[t4-1]=tr1+cc[t6-1];
+ ch[t4+t5-1]=cc[t6-1]-tr1;
+
+ ch[t4]=ti1-cc[t1+t0];
+ ch[t4+t5]=ti1+cc[t1+t0];
+
+ t1+=ido;
+ t2+=ido;
+ t4+=t3;
+ t6+=ido;
+ }
+}
+
+static void dradfg(int ido,int ip,int l1,int idl1,float *cc,float *c1,
+ float *c2,float *ch,float *ch2,float *wa){
+
+ static float tpi=6.283185307179586f;
+ int idij,ipph,i,j,k,l,ic,ik,is;
+ int t0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10;
+ float dc2,ai1,ai2,ar1,ar2,ds2;
+ int nbd;
+ float dcp,arg,dsp,ar1h,ar2h;
+ int idp2,ipp2;
+
+ arg=tpi/(float)ip;
+ dcp=cos(arg);
+ dsp=sin(arg);
+ ipph=(ip+1)>>1;
+ ipp2=ip;
+ idp2=ido;
+ nbd=(ido-1)>>1;
+ t0=l1*ido;
+ t10=ip*ido;
+
+ if(ido==1)goto L119;
+ for(ik=0;ik<idl1;ik++)ch2[ik]=c2[ik];
+
+ t1=0;
+ for(j=1;j<ip;j++){
+ t1+=t0;
+ t2=t1;
+ for(k=0;k<l1;k++){
+ ch[t2]=c1[t2];
+ t2+=ido;
+ }
+ }
+
+ is=-ido;
+ t1=0;
+ if(nbd>l1){
+ for(j=1;j<ip;j++){
+ t1+=t0;
+ is+=ido;
+ t2= -ido+t1;
+ for(k=0;k<l1;k++){
+ idij=is-1;
+ t2+=ido;
+ t3=t2;
+ for(i=2;i<ido;i+=2){
+ idij+=2;
+ t3+=2;
+ ch[t3-1]=wa[idij-1]*c1[t3-1]+wa[idij]*c1[t3];
+ ch[t3]=wa[idij-1]*c1[t3]-wa[idij]*c1[t3-1];
+ }
+ }
+ }
+ }else{
+
+ for(j=1;j<ip;j++){
+ is+=ido;
+ idij=is-1;
+ t1+=t0;
+ t2=t1;
+ for(i=2;i<ido;i+=2){
+ idij+=2;
+ t2+=2;
+ t3=t2;
+ for(k=0;k<l1;k++){
+ ch[t3-1]=wa[idij-1]*c1[t3-1]+wa[idij]*c1[t3];
+ ch[t3]=wa[idij-1]*c1[t3]-wa[idij]*c1[t3-1];
+ t3+=ido;
+ }
+ }
+ }
+ }
+
+ t1=0;
+ t2=ipp2*t0;
+ if(nbd<l1){
+ for(j=1;j<ipph;j++){
+ t1+=t0;
+ t2-=t0;
+ t3=t1;
+ t4=t2;
+ for(i=2;i<ido;i+=2){
+ t3+=2;
+ t4+=2;
+ t5=t3-ido;
+ t6=t4-ido;
+ for(k=0;k<l1;k++){
+ t5+=ido;
+ t6+=ido;
+ c1[t5-1]=ch[t5-1]+ch[t6-1];
+ c1[t6-1]=ch[t5]-ch[t6];
+ c1[t5]=ch[t5]+ch[t6];
+ c1[t6]=ch[t6-1]-ch[t5-1];
+ }
+ }
+ }
+ }else{
+ for(j=1;j<ipph;j++){
+ t1+=t0;
+ t2-=t0;
+ t3=t1;
+ t4=t2;
+ for(k=0;k<l1;k++){
+ t5=t3;
+ t6=t4;
+ for(i=2;i<ido;i+=2){
+ t5+=2;
+ t6+=2;
+ c1[t5-1]=ch[t5-1]+ch[t6-1];
+ c1[t6-1]=ch[t5]-ch[t6];
+ c1[t5]=ch[t5]+ch[t6];
+ c1[t6]=ch[t6-1]-ch[t5-1];
+ }
+ t3+=ido;
+ t4+=ido;
+ }
+ }
+ }
+
+L119:
+ for(ik=0;ik<idl1;ik++)c2[ik]=ch2[ik];
+
+ t1=0;
+ t2=ipp2*idl1;
+ for(j=1;j<ipph;j++){
+ t1+=t0;
+ t2-=t0;
+ t3=t1-ido;
+ t4=t2-ido;
+ for(k=0;k<l1;k++){
+ t3+=ido;
+ t4+=ido;
+ c1[t3]=ch[t3]+ch[t4];
+ c1[t4]=ch[t4]-ch[t3];
+ }
+ }
+
+ ar1=1.f;
+ ai1=0.f;
+ t1=0;
+ t2=ipp2*idl1;
+ t3=(ip-1)*idl1;
+ for(l=1;l<ipph;l++){
+ t1+=idl1;
+ t2-=idl1;
+ ar1h=dcp*ar1-dsp*ai1;
+ ai1=dcp*ai1+dsp*ar1;
+ ar1=ar1h;
+ t4=t1;
+ t5=t2;
+ t6=t3;
+ t7=idl1;
+
+ for(ik=0;ik<idl1;ik++){
+ ch2[t4++]=c2[ik]+ar1*c2[t7++];
+ ch2[t5++]=ai1*c2[t6++];
+ }
+
+ dc2=ar1;
+ ds2=ai1;
+ ar2=ar1;
+ ai2=ai1;
+
+ t4=idl1;
+ t5=(ipp2-1)*idl1;
+ for(j=2;j<ipph;j++){
+ t4+=idl1;
+ t5-=idl1;
+
+ ar2h=dc2*ar2-ds2*ai2;
+ ai2=dc2*ai2+ds2*ar2;
+ ar2=ar2h;
+
+ t6=t1;
+ t7=t2;
+ t8=t4;
+ t9=t5;
+ for(ik=0;ik<idl1;ik++){
+ ch2[t6++]+=ar2*c2[t8++];
+ ch2[t7++]+=ai2*c2[t9++];
+ }
+ }
+ }
+
+ t1=0;
+ for(j=1;j<ipph;j++){
+ t1+=idl1;
+ t2=t1;
+ for(ik=0;ik<idl1;ik++)ch2[ik]+=c2[t2++];
+ }
+
+ if(ido<l1)goto L132;
+
+ t1=0;
+ t2=0;
+ for(k=0;k<l1;k++){
+ t3=t1;
+ t4=t2;
+ for(i=0;i<ido;i++)cc[t4++]=ch[t3++];
+ t1+=ido;
+ t2+=t10;
+ }
+
+ goto L135;
+
+ L132:
+ for(i=0;i<ido;i++){
+ t1=i;
+ t2=i;
+ for(k=0;k<l1;k++){
+ cc[t2]=ch[t1];
+ t1+=ido;
+ t2+=t10;
+ }
+ }
+
+ L135:
+ t1=0;
+ t2=ido<<1;
+ t3=0;
+ t4=ipp2*t0;
+ for(j=1;j<ipph;j++){
+
+ t1+=t2;
+ t3+=t0;
+ t4-=t0;
+
+ t5=t1;
+ t6=t3;
+ t7=t4;
+
+ for(k=0;k<l1;k++){
+ cc[t5-1]=ch[t6];
+ cc[t5]=ch[t7];
+ t5+=t10;
+ t6+=ido;
+ t7+=ido;
+ }
+ }
+
+ if(ido==1)return;
+ if(nbd<l1)goto L141;
+
+ t1=-ido;
+ t3=0;
+ t4=0;
+ t5=ipp2*t0;
+ for(j=1;j<ipph;j++){
+ t1+=t2;
+ t3+=t2;
+ t4+=t0;
+ t5-=t0;
+ t6=t1;
+ t7=t3;
+ t8=t4;
+ t9=t5;
+ for(k=0;k<l1;k++){
+ for(i=2;i<ido;i+=2){
+ ic=idp2-i;
+ cc[i+t7-1]=ch[i+t8-1]+ch[i+t9-1];
+ cc[ic+t6-1]=ch[i+t8-1]-ch[i+t9-1];
+ cc[i+t7]=ch[i+t8]+ch[i+t9];
+ cc[ic+t6]=ch[i+t9]-ch[i+t8];
+ }
+ t6+=t10;
+ t7+=t10;
+ t8+=ido;
+ t9+=ido;
+ }
+ }
+ return;
+
+ L141:
+
+ t1=-ido;
+ t3=0;
+ t4=0;
+ t5=ipp2*t0;
+ for(j=1;j<ipph;j++){
+ t1+=t2;
+ t3+=t2;
+ t4+=t0;
+ t5-=t0;
+ for(i=2;i<ido;i+=2){
+ t6=idp2+t1-i;
+ t7=i+t3;
+ t8=i+t4;
+ t9=i+t5;
+ for(k=0;k<l1;k++){
+ cc[t7-1]=ch[t8-1]+ch[t9-1];
+ cc[t6-1]=ch[t8-1]-ch[t9-1];
+ cc[t7]=ch[t8]+ch[t9];
+ cc[t6]=ch[t9]-ch[t8];
+ t6+=t10;
+ t7+=t10;
+ t8+=ido;
+ t9+=ido;
+ }
+ }
+ }
+}
+
+static void drftf1(int n,float *c,float *ch,float *wa,int *ifac){
+ int i,k1,l1,l2;
+ int na,kh,nf;
+ int ip,iw,ido,idl1,ix2,ix3;
+
+ nf=ifac[1];
+ na=1;
+ l2=n;
+ iw=n;
+
+ for(k1=0;k1<nf;k1++){
+ kh=nf-k1;
+ ip=ifac[kh+1];
+ l1=l2/ip;
+ ido=n/l2;
+ idl1=ido*l1;
+ iw-=(ip-1)*ido;
+ na=1-na;
+
+ if(ip!=4)goto L102;
+
+ ix2=iw+ido;
+ ix3=ix2+ido;
+ if(na!=0)
+ dradf4(ido,l1,ch,c,wa+iw-1,wa+ix2-1,wa+ix3-1);
+ else
+ dradf4(ido,l1,c,ch,wa+iw-1,wa+ix2-1,wa+ix3-1);
+ goto L110;
+
+ L102:
+ if(ip!=2)goto L104;
+ if(na!=0)goto L103;
+
+ dradf2(ido,l1,c,ch,wa+iw-1);
+ goto L110;
+
+ L103:
+ dradf2(ido,l1,ch,c,wa+iw-1);
+ goto L110;
+
+ L104:
+ if(ido==1)na=1-na;
+ if(na!=0)goto L109;
+
+ dradfg(ido,ip,l1,idl1,c,c,c,ch,ch,wa+iw-1);
+ na=1;
+ goto L110;
+
+ L109:
+ dradfg(ido,ip,l1,idl1,ch,ch,ch,c,c,wa+iw-1);
+ na=0;
+
+ L110:
+ l2=l1;
+ }
+
+ if(na==1)return;
+
+ for(i=0;i<n;i++)c[i]=ch[i];
+}
+
+static void dradb2(int ido,int l1,float *cc,float *ch,float *wa1){
+ int i,k,t0,t1,t2,t3,t4,t5,t6;
+ float ti2,tr2;
+
+ t0=l1*ido;
+
+ t1=0;
+ t2=0;
+ t3=(ido<<1)-1;
+ for(k=0;k<l1;k++){
+ ch[t1]=cc[t2]+cc[t3+t2];
+ ch[t1+t0]=cc[t2]-cc[t3+t2];
+ t2=(t1+=ido)<<1;
+ }
+
+ if(ido<2)return;
+ if(ido==2)goto L105;
+
+ t1=0;
+ t2=0;
+ for(k=0;k<l1;k++){
+ t3=t1;
+ t5=(t4=t2)+(ido<<1);
+ t6=t0+t1;
+ for(i=2;i<ido;i+=2){
+ t3+=2;
+ t4+=2;
+ t5-=2;
+ t6+=2;
+ ch[t3-1]=cc[t4-1]+cc[t5-1];
+ tr2=cc[t4-1]-cc[t5-1];
+ ch[t3]=cc[t4]-cc[t5];
+ ti2=cc[t4]+cc[t5];
+ ch[t6-1]=wa1[i-2]*tr2-wa1[i-1]*ti2;
+ ch[t6]=wa1[i-2]*ti2+wa1[i-1]*tr2;
+ }
+ t2=(t1+=ido)<<1;
+ }
+
+ if(ido%2==1)return;
+
+L105:
+ t1=ido-1;
+ t2=ido-1;
+ for(k=0;k<l1;k++){
+ ch[t1]=cc[t2]+cc[t2];
+ ch[t1+t0]=-(cc[t2+1]+cc[t2+1]);
+ t1+=ido;
+ t2+=ido<<1;
+ }
+}
+
+static void dradb3(int ido,int l1,float *cc,float *ch,float *wa1,
+ float *wa2){
+ static float taur = -.5f;
+ static float taui = .8660254037844386f;
+ int i,k,t0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10;
+ float ci2,ci3,di2,di3,cr2,cr3,dr2,dr3,ti2,tr2;
+ t0=l1*ido;
+
+ t1=0;
+ t2=t0<<1;
+ t3=ido<<1;
+ t4=ido+(ido<<1);
+ t5=0;
+ for(k=0;k<l1;k++){
+ tr2=cc[t3-1]+cc[t3-1];
+ cr2=cc[t5]+(taur*tr2);
+ ch[t1]=cc[t5]+tr2;
+ ci3=taui*(cc[t3]+cc[t3]);
+ ch[t1+t0]=cr2-ci3;
+ ch[t1+t2]=cr2+ci3;
+ t1+=ido;
+ t3+=t4;
+ t5+=t4;
+ }
+
+ if(ido==1)return;
+
+ t1=0;
+ t3=ido<<1;
+ for(k=0;k<l1;k++){
+ t7=t1+(t1<<1);
+ t6=(t5=t7+t3);
+ t8=t1;
+ t10=(t9=t1+t0)+t0;
+
+ for(i=2;i<ido;i+=2){
+ t5+=2;
+ t6-=2;
+ t7+=2;
+ t8+=2;
+ t9+=2;
+ t10+=2;
+ tr2=cc[t5-1]+cc[t6-1];
+ cr2=cc[t7-1]+(taur*tr2);
+ ch[t8-1]=cc[t7-1]+tr2;
+ ti2=cc[t5]-cc[t6];
+ ci2=cc[t7]+(taur*ti2);
+ ch[t8]=cc[t7]+ti2;
+ cr3=taui*(cc[t5-1]-cc[t6-1]);
+ ci3=taui*(cc[t5]+cc[t6]);
+ dr2=cr2-ci3;
+ dr3=cr2+ci3;
+ di2=ci2+cr3;
+ di3=ci2-cr3;
+ ch[t9-1]=wa1[i-2]*dr2-wa1[i-1]*di2;
+ ch[t9]=wa1[i-2]*di2+wa1[i-1]*dr2;
+ ch[t10-1]=wa2[i-2]*dr3-wa2[i-1]*di3;
+ ch[t10]=wa2[i-2]*di3+wa2[i-1]*dr3;
+ }
+ t1+=ido;
+ }
+}
+
+static void dradb4(int ido,int l1,float *cc,float *ch,float *wa1,
+ float *wa2,float *wa3){
+ static float sqrt2=1.414213562373095f;
+ int i,k,t0,t1,t2,t3,t4,t5,t6,t7,t8;
+ float ci2,ci3,ci4,cr2,cr3,cr4,ti1,ti2,ti3,ti4,tr1,tr2,tr3,tr4;
+ t0=l1*ido;
+
+ t1=0;
+ t2=ido<<2;
+ t3=0;
+ t6=ido<<1;
+ for(k=0;k<l1;k++){
+ t4=t3+t6;
+ t5=t1;
+ tr3=cc[t4-1]+cc[t4-1];
+ tr4=cc[t4]+cc[t4];
+ tr1=cc[t3]-cc[(t4+=t6)-1];
+ tr2=cc[t3]+cc[t4-1];
+ ch[t5]=tr2+tr3;
+ ch[t5+=t0]=tr1-tr4;
+ ch[t5+=t0]=tr2-tr3;
+ ch[t5+=t0]=tr1+tr4;
+ t1+=ido;
+ t3+=t2;
+ }
+
+ if(ido<2)return;
+ if(ido==2)goto L105;
+
+ t1=0;
+ for(k=0;k<l1;k++){
+ t5=(t4=(t3=(t2=t1<<2)+t6))+t6;
+ t7=t1;
+ for(i=2;i<ido;i+=2){
+ t2+=2;
+ t3+=2;
+ t4-=2;
+ t5-=2;
+ t7+=2;
+ ti1=cc[t2]+cc[t5];
+ ti2=cc[t2]-cc[t5];
+ ti3=cc[t3]-cc[t4];
+ tr4=cc[t3]+cc[t4];
+ tr1=cc[t2-1]-cc[t5-1];
+ tr2=cc[t2-1]+cc[t5-1];
+ ti4=cc[t3-1]-cc[t4-1];
+ tr3=cc[t3-1]+cc[t4-1];
+ ch[t7-1]=tr2+tr3;
+ cr3=tr2-tr3;
+ ch[t7]=ti2+ti3;
+ ci3=ti2-ti3;
+ cr2=tr1-tr4;
+ cr4=tr1+tr4;
+ ci2=ti1+ti4;
+ ci4=ti1-ti4;
+
+ ch[(t8=t7+t0)-1]=wa1[i-2]*cr2-wa1[i-1]*ci2;
+ ch[t8]=wa1[i-2]*ci2+wa1[i-1]*cr2;
+ ch[(t8+=t0)-1]=wa2[i-2]*cr3-wa2[i-1]*ci3;
+ ch[t8]=wa2[i-2]*ci3+wa2[i-1]*cr3;
+ ch[(t8+=t0)-1]=wa3[i-2]*cr4-wa3[i-1]*ci4;
+ ch[t8]=wa3[i-2]*ci4+wa3[i-1]*cr4;
+ }
+ t1+=ido;
+ }
+
+ if(ido%2 == 1)return;
+
+ L105:
+
+ t1=ido;
+ t2=ido<<2;
+ t3=ido-1;
+ t4=ido+(ido<<1);
+ for(k=0;k<l1;k++){
+ t5=t3;
+ ti1=cc[t1]+cc[t4];
+ ti2=cc[t4]-cc[t1];
+ tr1=cc[t1-1]-cc[t4-1];
+ tr2=cc[t1-1]+cc[t4-1];
+ ch[t5]=tr2+tr2;
+ ch[t5+=t0]=sqrt2*(tr1-ti1);
+ ch[t5+=t0]=ti2+ti2;
+ ch[t5+=t0]=-sqrt2*(tr1+ti1);
+
+ t3+=ido;
+ t1+=t2;
+ t4+=t2;
+ }
+}
+
+static void dradbg(int ido,int ip,int l1,int idl1,float *cc,float *c1,
+ float *c2,float *ch,float *ch2,float *wa){
+ static float tpi=6.283185307179586f;
+ int idij,ipph,i,j,k,l,ik,is,t0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,
+ t11,t12;
+ float dc2,ai1,ai2,ar1,ar2,ds2;
+ int nbd;
+ float dcp,arg,dsp,ar1h,ar2h;
+ int ipp2;
+
+ t10=ip*ido;
+ t0=l1*ido;
+ arg=tpi/(float)ip;
+ dcp=cos(arg);
+ dsp=sin(arg);
+ nbd=(ido-1)>>1;
+ ipp2=ip;
+ ipph=(ip+1)>>1;
+ if(ido<l1)goto L103;
+
+ t1=0;
+ t2=0;
+ for(k=0;k<l1;k++){
+ t3=t1;
+ t4=t2;
+ for(i=0;i<ido;i++){
+ ch[t3]=cc[t4];
+ t3++;
+ t4++;
+ }
+ t1+=ido;
+ t2+=t10;
+ }
+ goto L106;
+
+ L103:
+ t1=0;
+ for(i=0;i<ido;i++){
+ t2=t1;
+ t3=t1;
+ for(k=0;k<l1;k++){
+ ch[t2]=cc[t3];
+ t2+=ido;
+ t3+=t10;
+ }
+ t1++;
+ }
+
+ L106:
+ t1=0;
+ t2=ipp2*t0;
+ t7=(t5=ido<<1);
+ for(j=1;j<ipph;j++){
+ t1+=t0;
+ t2-=t0;
+ t3=t1;
+ t4=t2;
+ t6=t5;
+ for(k=0;k<l1;k++){
+ ch[t3]=cc[t6-1]+cc[t6-1];
+ ch[t4]=cc[t6]+cc[t6];
+ t3+=ido;
+ t4+=ido;
+ t6+=t10;
+ }
+ t5+=t7;
+ }
+
+ if (ido == 1)goto L116;
+ if(nbd<l1)goto L112;
+
+ t1=0;
+ t2=ipp2*t0;
+ t7=0;
+ for(j=1;j<ipph;j++){
+ t1+=t0;
+ t2-=t0;
+ t3=t1;
+ t4=t2;
+
+ t7+=(ido<<1);
+ t8=t7;
+ for(k=0;k<l1;k++){
+ t5=t3;
+ t6=t4;
+ t9=t8;
+ t11=t8;
+ for(i=2;i<ido;i+=2){
+ t5+=2;
+ t6+=2;
+ t9+=2;
+ t11-=2;
+ ch[t5-1]=cc[t9-1]+cc[t11-1];
+ ch[t6-1]=cc[t9-1]-cc[t11-1];
+ ch[t5]=cc[t9]-cc[t11];
+ ch[t6]=cc[t9]+cc[t11];
+ }
+ t3+=ido;
+ t4+=ido;
+ t8+=t10;
+ }
+ }
+ goto L116;
+
+ L112:
+ t1=0;
+ t2=ipp2*t0;
+ t7=0;
+ for(j=1;j<ipph;j++){
+ t1+=t0;
+ t2-=t0;
+ t3=t1;
+ t4=t2;
+ t7+=(ido<<1);
+ t8=t7;
+ t9=t7;
+ for(i=2;i<ido;i+=2){
+ t3+=2;
+ t4+=2;
+ t8+=2;
+ t9-=2;
+ t5=t3;
+ t6=t4;
+ t11=t8;
+ t12=t9;
+ for(k=0;k<l1;k++){
+ ch[t5-1]=cc[t11-1]+cc[t12-1];
+ ch[t6-1]=cc[t11-1]-cc[t12-1];
+ ch[t5]=cc[t11]-cc[t12];
+ ch[t6]=cc[t11]+cc[t12];
+ t5+=ido;
+ t6+=ido;
+ t11+=t10;
+ t12+=t10;
+ }
+ }
+ }
+
+L116:
+ ar1=1.f;
+ ai1=0.f;
+ t1=0;
+ t9=(t2=ipp2*idl1);
+ t3=(ip-1)*idl1;
+ for(l=1;l<ipph;l++){
+ t1+=idl1;
+ t2-=idl1;
+
+ ar1h=dcp*ar1-dsp*ai1;
+ ai1=dcp*ai1+dsp*ar1;
+ ar1=ar1h;
+ t4=t1;
+ t5=t2;
+ t6=0;
+ t7=idl1;
+ t8=t3;
+ for(ik=0;ik<idl1;ik++){
+ c2[t4++]=ch2[t6++]+ar1*ch2[t7++];
+ c2[t5++]=ai1*ch2[t8++];
+ }
+ dc2=ar1;
+ ds2=ai1;
+ ar2=ar1;
+ ai2=ai1;
+
+ t6=idl1;
+ t7=t9-idl1;
+ for(j=2;j<ipph;j++){
+ t6+=idl1;
+ t7-=idl1;
+ ar2h=dc2*ar2-ds2*ai2;
+ ai2=dc2*ai2+ds2*ar2;
+ ar2=ar2h;
+ t4=t1;
+ t5=t2;
+ t11=t6;
+ t12=t7;
+ for(ik=0;ik<idl1;ik++){
+ c2[t4++]+=ar2*ch2[t11++];
+ c2[t5++]+=ai2*ch2[t12++];
+ }
+ }
+ }
+
+ t1=0;
+ for(j=1;j<ipph;j++){
+ t1+=idl1;
+ t2=t1;
+ for(ik=0;ik<idl1;ik++)ch2[ik]+=ch2[t2++];
+ }
+
+ t1=0;
+ t2=ipp2*t0;
+ for(j=1;j<ipph;j++){
+ t1+=t0;
+ t2-=t0;
+ t3=t1;
+ t4=t2;
+ for(k=0;k<l1;k++){
+ ch[t3]=c1[t3]-c1[t4];
+ ch[t4]=c1[t3]+c1[t4];
+ t3+=ido;
+ t4+=ido;
+ }
+ }
+
+ if(ido==1)goto L132;
+ if(nbd<l1)goto L128;
+
+ t1=0;
+ t2=ipp2*t0;
+ for(j=1;j<ipph;j++){
+ t1+=t0;
+ t2-=t0;
+ t3=t1;
+ t4=t2;
+ for(k=0;k<l1;k++){
+ t5=t3;
+ t6=t4;
+ for(i=2;i<ido;i+=2){
+ t5+=2;
+ t6+=2;
+ ch[t5-1]=c1[t5-1]-c1[t6];
+ ch[t6-1]=c1[t5-1]+c1[t6];
+ ch[t5]=c1[t5]+c1[t6-1];
+ ch[t6]=c1[t5]-c1[t6-1];
+ }
+ t3+=ido;
+ t4+=ido;
+ }
+ }
+ goto L132;
+
+ L128:
+ t1=0;
+ t2=ipp2*t0;
+ for(j=1;j<ipph;j++){
+ t1+=t0;
+ t2-=t0;
+ t3=t1;
+ t4=t2;
+ for(i=2;i<ido;i+=2){
+ t3+=2;
+ t4+=2;
+ t5=t3;
+ t6=t4;
+ for(k=0;k<l1;k++){
+ ch[t5-1]=c1[t5-1]-c1[t6];
+ ch[t6-1]=c1[t5-1]+c1[t6];
+ ch[t5]=c1[t5]+c1[t6-1];
+ ch[t6]=c1[t5]-c1[t6-1];
+ t5+=ido;
+ t6+=ido;
+ }
+ }
+ }
+
+L132:
+ if(ido==1)return;
+
+ for(ik=0;ik<idl1;ik++)c2[ik]=ch2[ik];
+
+ t1=0;
+ for(j=1;j<ip;j++){
+ t2=(t1+=t0);
+ for(k=0;k<l1;k++){
+ c1[t2]=ch[t2];
+ t2+=ido;
+ }
+ }
+
+ if(nbd>l1)goto L139;
+
+ is= -ido-1;
+ t1=0;
+ for(j=1;j<ip;j++){
+ is+=ido;
+ t1+=t0;
+ idij=is;
+ t2=t1;
+ for(i=2;i<ido;i+=2){
+ t2+=2;
+ idij+=2;
+ t3=t2;
+ for(k=0;k<l1;k++){
+ c1[t3-1]=wa[idij-1]*ch[t3-1]-wa[idij]*ch[t3];
+ c1[t3]=wa[idij-1]*ch[t3]+wa[idij]*ch[t3-1];
+ t3+=ido;
+ }
+ }
+ }
+ return;
+
+ L139:
+ is= -ido-1;
+ t1=0;
+ for(j=1;j<ip;j++){
+ is+=ido;
+ t1+=t0;
+ t2=t1;
+ for(k=0;k<l1;k++){
+ idij=is;
+ t3=t2;
+ for(i=2;i<ido;i+=2){
+ idij+=2;
+ t3+=2;
+ c1[t3-1]=wa[idij-1]*ch[t3-1]-wa[idij]*ch[t3];
+ c1[t3]=wa[idij-1]*ch[t3]+wa[idij]*ch[t3-1];
+ }
+ t2+=ido;
+ }
+ }
+}
+
+static void drftb1(int n, float *c, float *ch, float *wa, int *ifac){
+ int i,k1,l1,l2;
+ int na;
+ int nf,ip,iw,ix2,ix3,ido,idl1;
+
+ nf=ifac[1];
+ na=0;
+ l1=1;
+ iw=1;
+
+ for(k1=0;k1<nf;k1++){
+ ip=ifac[k1 + 2];
+ l2=ip*l1;
+ ido=n/l2;
+ idl1=ido*l1;
+ if(ip!=4)goto L103;
+ ix2=iw+ido;
+ ix3=ix2+ido;
+
+ if(na!=0)
+ dradb4(ido,l1,ch,c,wa+iw-1,wa+ix2-1,wa+ix3-1);
+ else
+ dradb4(ido,l1,c,ch,wa+iw-1,wa+ix2-1,wa+ix3-1);
+ na=1-na;
+ goto L115;
+
+ L103:
+ if(ip!=2)goto L106;
+
+ if(na!=0)
+ dradb2(ido,l1,ch,c,wa+iw-1);
+ else
+ dradb2(ido,l1,c,ch,wa+iw-1);
+ na=1-na;
+ goto L115;
+
+ L106:
+ if(ip!=3)goto L109;
+
+ ix2=iw+ido;
+ if(na!=0)
+ dradb3(ido,l1,ch,c,wa+iw-1,wa+ix2-1);
+ else
+ dradb3(ido,l1,c,ch,wa+iw-1,wa+ix2-1);
+ na=1-na;
+ goto L115;
+
+ L109:
+/* The radix five case can be translated later..... */
+/* if(ip!=5)goto L112;
+
+ ix2=iw+ido;
+ ix3=ix2+ido;
+ ix4=ix3+ido;
+ if(na!=0)
+ dradb5(ido,l1,ch,c,wa+iw-1,wa+ix2-1,wa+ix3-1,wa+ix4-1);
+ else
+ dradb5(ido,l1,c,ch,wa+iw-1,wa+ix2-1,wa+ix3-1,wa+ix4-1);
+ na=1-na;
+ goto L115;
+
+ L112:*/
+ if(na!=0)
+ dradbg(ido,ip,l1,idl1,ch,ch,ch,c,c,wa+iw-1);
+ else
+ dradbg(ido,ip,l1,idl1,c,c,c,ch,ch,wa+iw-1);
+ if(ido==1)na=1-na;
+
+ L115:
+ l1=l2;
+ iw+=(ip-1)*ido;
+ }
+
+ if(na==0)return;
+
+ for(i=0;i<n;i++)c[i]=ch[i];
+}
+
+void spx_drft_forward(struct drft_lookup *l,float *data){
+ if(l->n==1)return;
+ drftf1(l->n,data,l->trigcache,l->trigcache+l->n,l->splitcache);
+}
+
+void spx_drft_backward(struct drft_lookup *l,float *data){
+ if (l->n==1)return;
+ drftb1(l->n,data,l->trigcache,l->trigcache+l->n,l->splitcache);
+}
+
+void spx_drft_init(struct drft_lookup *l,int n)
+{
+ l->n=n;
+ l->trigcache=(float*)speex_alloc(3*n*sizeof(*l->trigcache));
+ l->splitcache=(int*)speex_alloc(32*sizeof(*l->splitcache));
+ fdrffti(n, l->trigcache, l->splitcache);
+}
+
+void spx_drft_clear(struct drft_lookup *l)
+{
+ if(l)
+ {
+ if(l->trigcache)
+ speex_free(l->trigcache);
+ if(l->splitcache)
+ speex_free(l->splitcache);
+ }
+}
diff --git a/lib/speexdsp/libspeexdsp/smallft.h b/lib/speexdsp/libspeexdsp/smallft.h
new file mode 100644
index 00000000..446e2f65
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/smallft.h
@@ -0,0 +1,46 @@
+/********************************************************************
+ * *
+ * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
+ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
+ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
+ * *
+ * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001 *
+ * by the XIPHOPHORUS Company http://www.xiph.org/ *
+ * *
+ ********************************************************************
+
+ function: fft transform
+ last mod: $Id: smallft.h,v 1.3 2003/09/16 18:35:45 jm Exp $
+
+ ********************************************************************/
+/**
+ @file smallft.h
+ @brief Discrete Rotational Fourier Transform (DRFT)
+*/
+
+#ifndef _V_SMFT_H_
+#define _V_SMFT_H_
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Discrete Rotational Fourier Transform lookup */
+struct drft_lookup{
+ int n;
+ float *trigcache;
+ int *splitcache;
+};
+
+extern void spx_drft_forward(struct drft_lookup *l,float *data);
+extern void spx_drft_backward(struct drft_lookup *l,float *data);
+extern void spx_drft_init(struct drft_lookup *l,int n);
+extern void spx_drft_clear(struct drft_lookup *l);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/speexdsp/libspeexdsp/testdenoise.c b/lib/speexdsp/libspeexdsp/testdenoise.c
new file mode 100644
index 00000000..adebe941
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/testdenoise.c
@@ -0,0 +1,44 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "speex/speex_preprocess.h"
+#include <stdio.h>
+
+#define NN 160
+
+int main()
+{
+ short in[NN];
+ int i;
+ SpeexPreprocessState *st;
+ int count=0;
+ float f;
+
+ st = speex_preprocess_state_init(NN, 8000);
+ i=1;
+ speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_DENOISE, &i);
+ i=0;
+ speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_AGC, &i);
+ i=8000;
+ speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_AGC_LEVEL, &i);
+ i=0;
+ speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_DEREVERB, &i);
+ f=.0;
+ speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_DEREVERB_DECAY, &f);
+ f=.0;
+ speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_DEREVERB_LEVEL, &f);
+ while (1)
+ {
+ int vad;
+ fread(in, sizeof(short), NN, stdin);
+ if (feof(stdin))
+ break;
+ vad = speex_preprocess_run(st, in);
+ /*fprintf (stderr, "%d\n", vad);*/
+ fwrite(in, sizeof(short), NN, stdout);
+ count++;
+ }
+ speex_preprocess_state_destroy(st);
+ return 0;
+}
diff --git a/lib/speexdsp/libspeexdsp/testecho.c b/lib/speexdsp/libspeexdsp/testecho.c
new file mode 100644
index 00000000..1624dc2a
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/testecho.c
@@ -0,0 +1,53 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "speex/speex_echo.h"
+#include "speex/speex_preprocess.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+
+#define NN 128
+#define TAIL 1024
+
+int main(int argc, char **argv)
+{
+ FILE *echo_fd, *ref_fd, *e_fd;
+ short echo_buf[NN], ref_buf[NN], e_buf[NN];
+ SpeexEchoState *st;
+ SpeexPreprocessState *den;
+ int sampleRate = 8000;
+
+ if (argc != 4)
+ {
+ fprintf(stderr, "testecho mic_signal.sw speaker_signal.sw output.sw\n");
+ exit(1);
+ }
+ echo_fd = fopen(argv[2], "rb");
+ ref_fd = fopen(argv[1], "rb");
+ e_fd = fopen(argv[3], "wb");
+
+ st = speex_echo_state_init(NN, TAIL);
+ den = speex_preprocess_state_init(NN, sampleRate);
+ speex_echo_ctl(st, SPEEX_ECHO_SET_SAMPLING_RATE, &sampleRate);
+ speex_preprocess_ctl(den, SPEEX_PREPROCESS_SET_ECHO_STATE, st);
+
+ while (!feof(ref_fd) && !feof(echo_fd))
+ {
+ fread(ref_buf, sizeof(short), NN, ref_fd);
+ fread(echo_buf, sizeof(short), NN, echo_fd);
+ speex_echo_cancellation(st, ref_buf, echo_buf, e_buf);
+ speex_preprocess_run(den, e_buf);
+ fwrite(e_buf, sizeof(short), NN, e_fd);
+ }
+ speex_echo_state_destroy(st);
+ speex_preprocess_state_destroy(den);
+ fclose(e_fd);
+ fclose(echo_fd);
+ fclose(ref_fd);
+ return 0;
+}
diff --git a/lib/speexdsp/libspeexdsp/testjitter.c b/lib/speexdsp/libspeexdsp/testjitter.c
new file mode 100644
index 00000000..399dfe95
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/testjitter.c
@@ -0,0 +1,75 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "speex/speex_jitter.h"
+#include <stdio.h>
+
+union jbpdata {
+ unsigned int idx;
+ unsigned char data[4];
+};
+
+void synthIn(JitterBufferPacket *in, int idx, int span) {
+ union jbpdata d;
+ d.idx = idx;
+
+ in->data = d.data;
+ in->len = sizeof(d);
+ in->timestamp = idx * 10;
+ in->span = span * 10;
+ in->sequence = idx;
+ in->user_data = 0;
+}
+
+void jitterFill(JitterBuffer *jb) {
+ char buffer[65536];
+ JitterBufferPacket in, out;
+ int i;
+
+ out.data = buffer;
+
+ jitter_buffer_reset(jb);
+
+ for(i=0;i<100;++i) {
+ synthIn(&in, i, 1);
+ jitter_buffer_put(jb, &in);
+
+ out.len = 65536;
+ if (jitter_buffer_get(jb, &out, 10, NULL) != JITTER_BUFFER_OK) {
+ printf("Fill test failed iteration %d\n", i);
+ }
+ if (out.timestamp != i * 10) {
+ printf("Fill test expected %d got %d\n", i*10, out.timestamp);
+ }
+ jitter_buffer_tick(jb);
+ }
+}
+
+int main()
+{
+ char buffer[65536];
+ JitterBufferPacket in, out;
+ int i;
+
+ JitterBuffer *jb = jitter_buffer_init(10);
+
+ out.data = buffer;
+
+ /* Frozen sender case */
+ jitterFill(jb);
+ for(i=0;i<100;++i) {
+ out.len = 65536;
+ jitter_buffer_get(jb, &out, 10, NULL);
+ jitter_buffer_tick(jb);
+ }
+ synthIn(&in, 100, 1);
+ jitter_buffer_put(jb, &in);
+ out.len = 65536;
+ if (jitter_buffer_get(jb, &out, 10, NULL) != JITTER_BUFFER_OK) {
+ printf("Failed frozen sender resynchronize\n");
+ } else {
+ printf("Frozen sender: Jitter %d\n", out.timestamp - 100*10);
+ }
+ return 0;
+}
diff --git a/lib/speexdsp/libspeexdsp/testresample.c b/lib/speexdsp/libspeexdsp/testresample.c
new file mode 100644
index 00000000..7ed86675
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/testresample.c
@@ -0,0 +1,86 @@
+/* Copyright (C) 2007 Jean-Marc Valin
+
+ File: testresample.c
+ Testing the resampling code
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. 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.
+
+ 3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "speex/speex_resampler.h"
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+
+#define NN 256
+
+int main()
+{
+ spx_uint32_t i;
+ short *in;
+ short *out;
+ float *fin, *fout;
+ int count = 0;
+ SpeexResamplerState *st = speex_resampler_init(1, 8000, 12000, 10, NULL);
+ speex_resampler_set_rate(st, 96000, 44100);
+ speex_resampler_skip_zeros(st);
+
+ in = malloc(NN*sizeof(short));
+ out = malloc(2*NN*sizeof(short));
+ fin = malloc(NN*sizeof(float));
+ fout = malloc(2*NN*sizeof(float));
+ while (1)
+ {
+ spx_uint32_t in_len;
+ spx_uint32_t out_len;
+ fread(in, sizeof(short), NN, stdin);
+ if (feof(stdin))
+ break;
+ for (i=0;i<NN;i++)
+ fin[i]=in[i];
+ in_len = NN;
+ out_len = 2*NN;
+ /*if (count==2)
+ speex_resampler_set_quality(st, 10);*/
+ speex_resampler_process_float(st, 0, fin, &in_len, fout, &out_len);
+ for (i=0;i<out_len;i++)
+ out[i]=floor(.5+fout[i]);
+ /*speex_warning_int("writing", out_len);*/
+ fwrite(out, sizeof(short), out_len, stdout);
+ count++;
+ }
+ speex_resampler_destroy(st);
+ free(in);
+ free(out);
+ free(fin);
+ free(fout);
+ return 0;
+}
+
diff --git a/lib/speexdsp/libspeexdsp/testresample2.c b/lib/speexdsp/libspeexdsp/testresample2.c
new file mode 100644
index 00000000..99a830da
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/testresample2.c
@@ -0,0 +1,93 @@
+/* Copyright (C) 2007 Jean-Marc Valin
+
+ File: testresample2.c
+ Testing the resampling code
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. 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.
+
+ 3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "speex/speex_resampler.h"
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+
+#define PERIOD 32
+#define INBLOCK 1024
+#define RATE 48000
+
+int main()
+{
+ spx_uint32_t i;
+ float *fin, *fout;
+ int rate = 1000, off = 0, avail = INBLOCK;
+ SpeexResamplerState *st = speex_resampler_init(1, RATE, RATE, 4, NULL);
+ speex_resampler_set_rate(st, RATE, rate);
+ speex_resampler_skip_zeros(st);
+
+ fin = malloc(INBLOCK*2*sizeof(float));
+ for (i=0; i<INBLOCK*2;i++)
+ fin[i] = sinf ((float)i/PERIOD * 2 * M_PI) * 0.9;
+
+ fout = malloc(INBLOCK*4*sizeof(float));
+
+ while (1)
+ {
+ spx_uint32_t in_len;
+ spx_uint32_t out_len;
+
+ in_len = avail;
+ out_len = (in_len * rate + RATE-1) / RATE;
+
+ fprintf (stderr, "%d %d %d %d -> ", rate, off, in_len, out_len);
+
+ speex_resampler_process_float(st, 0, fin + off, &in_len, fout, &out_len);
+
+ fprintf (stderr, "%d %d\n", in_len, out_len);
+ off += in_len;
+ avail = avail - in_len + INBLOCK;
+
+ if (off >= INBLOCK)
+ off -= INBLOCK;
+
+ fwrite(fout, sizeof(float), out_len, stdout);
+
+ rate += 100;
+ if (rate > 128000)
+ break;
+
+ speex_resampler_set_rate(st, RATE, rate);
+ }
+ speex_resampler_destroy(st);
+ free(fin);
+ free(fout);
+ return 0;
+}
+
diff --git a/lib/speexdsp/libspeexdsp/vorbis_psy.h b/lib/speexdsp/libspeexdsp/vorbis_psy.h
new file mode 100644
index 00000000..19ea9c3c
--- /dev/null
+++ b/lib/speexdsp/libspeexdsp/vorbis_psy.h
@@ -0,0 +1,97 @@
+/* Copyright (C) 2005 Jean-Marc Valin, CSIRO, Christopher Montgomery
+ File: vorbis_psy.h
+
+ 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.
+*/
+
+#ifndef VORBIS_PSY_H
+#define VORBIS_PSY_H
+
+#ifdef VORBIS_PSYCHO
+
+#include "smallft.h"
+#define P_BANDS 17 /* 62Hz to 16kHz */
+#define NOISE_COMPAND_LEVELS 40
+
+
+#define todB(x) ((x)>1e-13?log((x)*(x))*4.34294480f:-30)
+#define fromdB(x) (exp((x)*.11512925f))
+
+/* The bark scale equations are approximations, since the original
+ table was somewhat hand rolled. The below are chosen to have the
+ best possible fit to the rolled tables, thus their somewhat odd
+ appearance (these are more accurate and over a longer range than
+ the oft-quoted bark equations found in the texts I have). The
+ approximations are valid from 0 - 30kHz (nyquist) or so.
+
+ all f in Hz, z in Bark */
+
+#define toBARK(n) (13.1f*atan(.00074f*(n))+2.24f*atan((n)*(n)*1.85e-8f)+1e-4f*(n))
+#define fromBARK(z) (102.f*(z)-2.f*pow(z,2.f)+.4f*pow(z,3.f)+pow(1.46f,z)-1.f)
+
+/* Frequency to octave. We arbitrarily declare 63.5 Hz to be octave
+ 0.0 */
+
+#define toOC(n) (log(n)*1.442695f-5.965784f)
+#define fromOC(o) (exp(((o)+5.965784f)*.693147f))
+
+
+typedef struct {
+
+ float noisewindowlo;
+ float noisewindowhi;
+ int noisewindowlomin;
+ int noisewindowhimin;
+ int noisewindowfixed;
+ float noiseoff[P_BANDS];
+ float noisecompand[NOISE_COMPAND_LEVELS];
+
+} VorbisPsyInfo;
+
+
+
+typedef struct {
+ int n;
+ int rate;
+ struct drft_lookup lookup;
+ VorbisPsyInfo *vi;
+
+ float *window;
+ float *noiseoffset;
+ long *bark;
+
+} VorbisPsy;
+
+
+VorbisPsy *vorbis_psy_init(int rate, int size);
+void vorbis_psy_destroy(VorbisPsy *psy);
+void compute_curve(VorbisPsy *psy, float *audio, float *curve);
+void curve_to_lpc(VorbisPsy *psy, float *curve, float *awk1, float *awk2, int ord);
+
+#endif
+#endif