Merge "Added opus_enc_fuzzer and opus_multistream_enc_fuzzer"
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 88a1342..01b033f 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,3 +1,6 @@
+include:
+  - template: 'Workflows/Branch-Pipelines.gitlab-ci.yml'
+
 default:
   tags:
     - docker
@@ -6,8 +9,6 @@
 
 whitespace:
   stage: test
-  only:
-    - merge_requests
   script:
     - git diff-tree --check origin/master HEAD
 
@@ -19,7 +20,7 @@
   script:
     - ./autogen.sh
     - ./configure
-    - make
+    - make -j4
     - make distcheck
   cache:
     paths:
@@ -40,3 +41,21 @@
     - cmake -S . -B build -G "Ninja" -DCMAKE_BUILD_TYPE=Release -DOPUS_BUILD_TESTING=ON -DOPUS_BUILD_PROGRAMS=ON
     - cmake --build build
     - cd build && ctest --output-on-failure
+
+meson:
+  stage: build
+  before_script:
+    - apt-get update &&
+      apt-get install -y python3-pip ninja-build doxygen
+    - export XDG_CACHE_HOME=$PWD/pip-cache
+    - pip3 install --user meson
+  script:
+    - export PATH=$PATH:$HOME/.local/bin
+    - mkdir builddir
+    - meson setup --werror -Dtests=enabled -Ddocs=enabled -Dbuildtype=release builddir
+    - meson compile -C builddir
+    - meson test -C builddir
+    #- meson dist --no-tests -C builddir
+  cache:
+    paths:
+      - 'pip-cache/*'
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 244ad13..a28f441 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -55,6 +55,22 @@
 option(OPUS_FLOAT_APPROX ${OPUS_FLOAT_APPROX_HELP_STR} OFF)
 add_feature_info(OPUS_FLOAT_APPROX OPUS_FLOAT_APPROX ${OPUS_FLOAT_APPROX_HELP_STR})
 
+set(OPUS_ASSERTIONS_HELP_STR "additional software error checking.")
+option(OPUS_ASSERTIONS ${OPUS_ASSERTIONS_HELP_STR} OFF)
+add_feature_info(OPUS_ASSERTIONS OPUS_ASSERTIONS ${OPUS_ASSERTIONS_HELP_STR})
+
+set(OPUS_HARDENING_HELP_STR "run-time checks that are cheap and safe for use in production.")
+option(OPUS_HARDENING ${OPUS_HARDENING_HELP_STR} ON)
+add_feature_info(OPUS_HARDENING OPUS_HARDENING ${OPUS_HARDENING_HELP_STR})
+
+set(OPUS_FUZZING_HELP_STR "causes the encoder to make random decisions (do not use in production).")
+option(OPUS_FUZZING ${OPUS_FUZZING_HELP_STR} OFF)
+add_feature_info(OPUS_FUZZING OPUS_FUZZING ${OPUS_FUZZING_HELP_STR})
+
+set(OPUS_CHECK_ASM_HELP_STR "enable bit-exactness checks between optimized and c implementations.")
+option(OPUS_CHECK_ASM ${OPUS_CHECK_ASM_HELP_STR} OFF)
+add_feature_info(OPUS_CHECK_ASM OPUS_CHECK_ASM ${OPUS_CHECK_ASM_HELP_STR})
+
 set(OPUS_INSTALL_PKG_CONFIG_MODULE_HELP_STR "install pkg-config module.")
 option(OPUS_INSTALL_PKG_CONFIG_MODULE ${OPUS_INSTALL_PKG_CONFIG_MODULE_HELP_STR} ON)
 add_feature_info(OPUS_INSTALL_PKG_CONFIG_MODULE OPUS_INSTALL_PKG_CONFIG_MODULE ${OPUS_INSTALL_PKG_CONFIG_MODULE_HELP_STR})
@@ -69,6 +85,14 @@
   add_feature_info(OPUS_BUILD_FRAMEWORK OPUS_BUILD_FRAMEWORK ${OPUS_BUILD_FRAMEWORK_HELP_STR})
 endif()
 
+set(OPUS_FIXED_POINT_DEBUG_HELP_STR "debug fixed-point implementation.")
+cmake_dependent_option(OPUS_FIXED_POINT_DEBUG
+                      ${OPUS_FIXED_POINT_DEBUG_HELP_STR}
+                      ON
+                      "OPUS_FIXED_POINT; OPUS_FIXED_POINT_DEBUG"
+                      OFF)
+add_feature_info(OPUS_FIXED_POINT_DEBUG OPUS_FIXED_POINT_DEBUG ${OPUS_FIXED_POINT_DEBUG_HELP_STR})
+
 set(OPUS_VAR_ARRAYS_HELP_STR "use variable length arrays for stack arrays.")
 cmake_dependent_option(OPUS_VAR_ARRAYS
                       ${OPUS_VAR_ARRAYS_HELP_STR}
@@ -233,7 +257,7 @@
 add_library(Opus::opus ALIAS opus)
 
 get_library_version(OPUS_LIBRARY_VERSION OPUS_LIBRARY_VERSION_MAJOR)
-message(STATUS "Opus library version: ${OPUS_LIBRARY_VERSION}")
+message(DEBUG "Opus library version: ${OPUS_LIBRARY_VERSION}")
 
 set_target_properties(opus
                       PROPERTIES SOVERSION
@@ -254,7 +278,11 @@
           silk)
 
 target_link_libraries(opus PRIVATE ${OPUS_REQUIRED_LIBRARIES})
-target_compile_definitions(opus PRIVATE OPUS_BUILD ENABLE_HARDENING)
+target_compile_definitions(opus PRIVATE OPUS_BUILD)
+
+if(OPUS_FIXED_POINT_DEBUG)
+  target_compile_definitions(opus PRIVATE FIXED_DEBUG)
+endif()
 
 if(OPUS_FORTIFY_SOURCE AND NOT MSVC)
   target_compile_definitions(opus PRIVATE
@@ -265,6 +293,22 @@
   target_compile_definitions(opus PRIVATE FLOAT_APPROX)
 endif()
 
+if(OPUS_ASSERTIONS)
+  target_compile_definitions(opus PRIVATE ENABLE_ASSERTIONS)
+endif()
+
+if(OPUS_HARDENING)
+  target_compile_definitions(opus PRIVATE ENABLE_HARDENING)
+endif()
+
+if(OPUS_FUZZING)
+  target_compile_definitions(opus PRIVATE FUZZING)
+endif()
+
+if(OPUS_CHECK_ASM)
+  target_compile_definitions(opus PRIVATE OPUS_CHECK_ASM)
+endif()
+
 if(OPUS_VAR_ARRAYS)
   target_compile_definitions(opus PRIVATE VAR_ARRAYS)
 elseif(OPUS_USE_ALLOCA)
@@ -546,13 +590,13 @@
   if(OPUS_FIXED_POINT)
     target_compile_definitions(test_opus_decode PRIVATE DISABLE_FLOAT_API)
   endif()
-  add_test(test_opus_decode test_opus_decode)
+  add_test(NAME test_opus_decode COMMAND $<TARGET_FILE:test_opus_decode> WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
 
   add_executable(test_opus_padding ${test_opus_padding_sources})
   target_include_directories(test_opus_padding
                              PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
   target_link_libraries(test_opus_padding PRIVATE opus)
-  add_test(test_opus_padding test_opus_padding)
+  add_test(NAME test_opus_padding COMMAND $<TARGET_FILE:test_opus_padding> WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
 
   if(NOT BUILD_SHARED_LIBS)
     # disable tests that depends on private API when building shared lib
@@ -563,12 +607,12 @@
     if(OPUS_FIXED_POINT)
       target_compile_definitions(test_opus_api PRIVATE DISABLE_FLOAT_API)
     endif()
-    add_test(test_opus_api test_opus_api)
+    add_test(NAME test_opus_api COMMAND $<TARGET_FILE:test_opus_api> WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
 
     add_executable(test_opus_encode ${test_opus_encode_sources})
     target_include_directories(test_opus_encode
                                PRIVATE ${CMAKE_CURRENT_BINARY_DIR} celt)
     target_link_libraries(test_opus_encode PRIVATE opus)
-    add_test(test_opus_encode test_opus_encode)
+    add_test(NAME test_opus_encode COMMAND $<TARGET_FILE:test_opus_encode> WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
   endif()
 endif()
diff --git a/METADATA b/METADATA
index c5500a3..eb54eee 100644
--- a/METADATA
+++ b/METADATA
@@ -5,11 +5,11 @@
     type: GIT
     value: "https://gitlab.xiph.org/xiph/opus.git"
   }
-  version: "b83dd52868326a401c8578041e3dbea439d53f11"
+  version: "794392ecd77e6fc6aafa62c3f6002780abcc2c7c"
   license_type: NOTICE
   last_upgrade_date {
-    year: 2020
-    month: 9
-    day: 30
+    year: 2021
+    month: 1
+    day: 5
   }
 }
diff --git a/Makefile.am b/Makefile.am
index e521373..83beaa3 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -224,6 +224,18 @@
              cmake/OpusSources.cmake \
              cmake/config.h.cmake.in \
              cmake/vla.c \
+             meson/get-version.py \
+             meson/read-sources-list.py \
+             meson.build \
+             meson_options.txt \
+             include/meson.build \
+             celt/meson.build \
+             celt/tests/meson.build \
+             silk/meson.build \
+             silk/tests/meson.build \
+             src/meson.build \
+             tests/meson.build \
+             doc/meson.build \
              tests/run_vectors.sh \
              celt/arm/arm2gnu.pl \
              celt/arm/celt_pitch_xcorr_arm.s \
diff --git a/celt/fixed_generic.h b/celt/fixed_generic.h
index 5f4abda..0ecbb89 100644
--- a/celt/fixed_generic.h
+++ b/celt/fixed_generic.h
@@ -102,9 +102,9 @@
 
 #define SATURATE16(x) (EXTRACT16((x)>32767 ? 32767 : (x)<-32768 ? -32768 : (x)))
 
-/** Shift by a and round-to-neareast 32-bit value. Result is a 16-bit value */
+/** Shift by a and round-to-nearest 32-bit value. Result is a 16-bit value */
 #define ROUND16(x,a) (EXTRACT16(PSHR32((x),(a))))
-/** Shift by a and round-to-neareast 32-bit value. Result is a saturated 16-bit value */
+/** Shift by a and round-to-nearest 32-bit value. Result is a saturated 16-bit value */
 #define SROUND16(x,a) EXTRACT16(SATURATE(PSHR32(x,a), 32767));
 
 /** Divide by two */
diff --git a/celt/meson.build b/celt/meson.build
new file mode 100644
index 0000000..370ea1f
--- /dev/null
+++ b/celt/meson.build
@@ -0,0 +1,63 @@
+celt_sources = sources['CELT_SOURCES']
+
+celt_sse_sources = sources['CELT_SOURCES_SSE']
+
+celt_sse2_sources = sources['CELT_SOURCES_SSE2']
+
+celt_sse4_1_sources = sources['CELT_SOURCES_SSE4_1']
+
+celt_neon_intr_sources = sources['CELT_SOURCES_ARM_NEON_INTR']
+
+celt_static_libs = []
+
+foreach intr_name : ['sse', 'sse2', 'sse4_1', 'neon_intr']
+  have_intr = get_variable('have_' + intr_name)
+  if not have_intr
+    continue
+  endif
+
+  intr_sources = get_variable('celt_@0@_sources'.format(intr_name))
+  intr_args = get_variable('opus_@0@_args'.format(intr_name), [])
+  celt_static_libs += static_library('celt_' + intr_name, intr_sources,
+      c_args: intr_args,
+      include_directories: opus_includes,
+      install: false)
+endforeach
+
+have_arm_intrinsics_or_asm = have_arm_ne10
+if (intrinsics_support.length() + asm_optimization.length() + inline_optimization.length()) > 0
+  have_arm_intrinsics_or_asm = true
+endif
+
+if host_cpu_family in ['arm', 'aarch64'] and have_arm_intrinsics_or_asm
+  celt_sources +=  sources['CELT_SOURCES_ARM']
+  if have_arm_ne10
+    celt_sources += sources['CELT_SOURCES_ARM_NE10']
+  endif
+  if opus_arm_external_asm
+    arm2gnu = [find_program('arm/arm2gnu.pl')] + arm2gnu_args
+    celt_sources_arm_asm = configure_file(input: 'arm/celt_pitch_xcorr_arm.s',
+      output: '@BASENAME@-gnu.S',
+      command: arm2gnu + ['@INPUT@'],
+      capture: true)
+    celt_arm_armopts_s = configure_file(input: 'arm/armopts.s.in',
+      output: 'arm/armopts.s',
+      configuration: opus_conf)
+    celt_static_libs += static_library('celt-armasm',
+      celt_arm_armopts_s, celt_sources_arm_asm,
+      install: false)
+  endif
+endif
+
+celt_c_args = []
+if host_system == 'windows'
+  celt_c_args += ['-DDLL_EXPORT']
+endif
+
+celt_lib = static_library('opus-celt',
+  celt_sources,
+  c_args: celt_c_args,
+  include_directories: opus_includes,
+  link_whole: celt_static_libs,
+  dependencies: libm,
+  install: false)
diff --git a/celt/stack_alloc.h b/celt/stack_alloc.h
index 2b51c8d..ae40e2a 100644
--- a/celt/stack_alloc.h
+++ b/celt/stack_alloc.h
@@ -40,7 +40,7 @@
 #endif
 
 #ifdef USE_ALLOCA
-# ifdef WIN32
+# ifdef _WIN32
 #  include <malloc.h>
 # else
 #  ifdef HAVE_ALLOCA_H
@@ -102,7 +102,7 @@
 
 #define VARDECL(type, var) type *var
 
-# ifdef WIN32
+# ifdef _WIN32
 #  define ALLOC(var, size, type) var = ((type*)_alloca(sizeof(type)*(size)))
 # else
 #  define ALLOC(var, size, type) var = ((type*)alloca(sizeof(type)*(size)))
diff --git a/celt/tests/meson.build b/celt/tests/meson.build
new file mode 100644
index 0000000..0e6d2e6
--- /dev/null
+++ b/celt/tests/meson.build
@@ -0,0 +1,19 @@
+tests = [
+  'test_unit_types',
+  'test_unit_mathops',
+  'test_unit_entropy',
+  'test_unit_laplace',
+  'test_unit_dft',
+  'test_unit_mdct',
+  'test_unit_rotation',
+  'test_unit_cwrs32',
+]
+
+foreach test_name : tests
+  exe = executable(test_name, '@0@.c'.format(test_name),
+                   include_directories : opus_includes,
+                   link_with : [celt_lib, celt_static_libs],
+                   dependencies : libm,
+                   install : false)
+  test(test_name, exe)
+endforeach
diff --git a/celt/tests/test_unit_mathops.c b/celt/tests/test_unit_mathops.c
index 874e9ad..0615448 100644
--- a/celt/tests/test_unit_mathops.c
+++ b/celt/tests/test_unit_mathops.c
@@ -143,7 +143,7 @@
 void testlog2(void)
 {
    float x;
-   for (x=0.001;x<1677700.0;x+=(x/8.0))
+   for (x=0.001f;x<1677700.0;x+=(x/8.0))
    {
       float error = fabs((1.442695040888963387*log(x))-celt_log2(x));
       if (error>0.0009)
@@ -157,7 +157,7 @@
 void testexp2(void)
 {
    float x;
-   for (x=-11.0;x<24.0;x+=0.0007)
+   for (x=-11.0;x<24.0;x+=0.0007f)
    {
       float error = fabs(x-(1.442695040888963387*log(celt_exp2(x))));
       if (error>0.0002)
@@ -171,7 +171,7 @@
 void testexp2log2(void)
 {
    float x;
-   for (x=-11.0;x<24.0;x+=0.0007)
+   for (x=-11.0;x<24.0;x+=0.0007f)
    {
       float error = fabs(x-(celt_log2(celt_exp2(x))));
       if (error>0.001)
diff --git a/celt/x86/pitch_sse.h b/celt/x86/pitch_sse.h
index e5f87ab..f7a014b 100644
--- a/celt/x86/pitch_sse.h
+++ b/celt/x86/pitch_sse.h
@@ -91,7 +91,7 @@
     int               N);
 #endif
 
-#if defined(OPUS_X86_MAY_HAVE_SSE2) && !defined(FIXED_POINT)
+#if defined(OPUS_X86_MAY_HAVE_SSE) && !defined(FIXED_POINT)
 opus_val32 celt_inner_prod_sse(
     const opus_val16 *x,
     const opus_val16 *y,
diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in
index 6d25f1f..6eef650 100644
--- a/doc/Doxyfile.in
+++ b/doc/Doxyfile.in
@@ -335,3 +335,6 @@
 # configure time.
 #
 HAVE_DOT               = @HAVE_DOT@
+
+# move docs to the correct place
+OUTPUT_DIRECTORY       = @top_builddir@/doc
diff --git a/doc/meson.build b/doc/meson.build
new file mode 100644
index 0000000..ee42a2b
--- /dev/null
+++ b/doc/meson.build
@@ -0,0 +1,33 @@
+have_dot = find_program('dot', required: false).found()
+
+doxyfile_conf = configuration_data()
+doxyfile_conf.set('VERSION', opus_version)
+doxyfile_conf.set('HAVE_DOT', have_dot)
+doxyfile_conf.set('top_srcdir', top_srcdir)
+doxyfile_conf.set('top_builddir', top_builddir)
+
+doxyfile = configure_file(input: 'Doxyfile.in',
+  output: 'Doxyfile',
+  configuration: doxyfile_conf,
+  install: false)
+
+docdir = join_paths(get_option('datadir'), get_option('docdir'))
+
+doc_inputs = [
+  'customdoxygen.css',
+  'footer.html',
+  'header.html',
+  'opus_logo.svg',
+  top_srcdir + '/include/opus.h',
+  top_srcdir + '/include/opus_multistream.h',
+  top_srcdir + '/include/opus_defines.h',
+  top_srcdir + '/include/opus_types.h',
+  top_srcdir + '/include/opus_custom.h',
+]
+
+custom_target('doc',
+  input: [ doxyfile ] + doc_inputs,
+  output: [ 'html' ],
+  command: [ doxygen, doxyfile ],
+  install_dir: docdir,
+  install: true)
diff --git a/include/meson.build b/include/meson.build
new file mode 100644
index 0000000..c1fb0b7
--- /dev/null
+++ b/include/meson.build
@@ -0,0 +1,13 @@
+opus_headers = [
+  'opus.h',
+  'opus_multistream.h',
+  'opus_projection.h',
+  'opus_types.h',
+  'opus_defines.h',
+]
+
+if opt_custom_modes
+  opus_headers += ['opus_custom.h']
+endif
+
+install_headers(opus_headers, subdir: 'opus')
diff --git a/include/opus_defines.h b/include/opus_defines.h
index d141418..ceee5b8 100644
--- a/include/opus_defines.h
+++ b/include/opus_defines.h
@@ -64,7 +64,7 @@
 /**Export control for opus functions */
 
 #ifndef OPUS_EXPORT
-# if defined(WIN32)
+# if defined(_WIN32)
 #  if defined(OPUS_BUILD) && defined(DLL_EXPORT)
 #   define OPUS_EXPORT __declspec(dllexport)
 #  else
diff --git a/meson.build b/meson.build
new file mode 100644
index 0000000..41f6935
--- /dev/null
+++ b/meson.build
@@ -0,0 +1,675 @@
+project('opus', 'c',
+  version: run_command('meson/get-version.py', '--package-version', check: true).stdout().strip(),
+  meson_version: '>=0.54.0',
+  default_options: ['warning_level=2',
+                    'c_std=gnu99',
+                    'buildtype=debugoptimized'])
+
+libversion = run_command('meson/get-version.py', '--libtool-version', check: true).stdout().strip()
+macosversion = run_command('meson/get-version.py', '--darwin-version', check: true).stdout().strip()
+
+cc = meson.get_compiler('c')
+host_system = host_machine.system()
+host_cpu_family = host_machine.cpu_family()
+top_srcdir = meson.current_source_dir()
+top_builddir = meson.current_build_dir()
+
+opus_includes = include_directories('.', 'include', 'celt', 'silk')
+opus_public_includes = include_directories('include')
+
+add_project_arguments('-DOPUS_BUILD', language: 'c')
+add_project_arguments('-DHAVE_CONFIG_H', language: 'c')
+
+if host_system == 'windows'
+  if cc.get_argument_syntax() == 'msvc'
+    add_project_arguments('-D_CRT_SECURE_NO_WARNINGS', language: 'c')
+  endif
+endif
+
+if cc.get_argument_syntax() == 'gnu'
+  add_project_arguments('-D_FORTIFY_SOURCE=2', language: 'c')
+endif
+
+# Check for extra compiler args
+additional_c_args = []
+if cc.get_argument_syntax() != 'msvc'
+  additional_c_args += [
+    '-fvisibility=hidden',
+    '-Wcast-align',
+    '-Wnested-externs',
+    '-Wshadow',
+    '-Wstrict-prototypes',
+  ]
+
+  # On Windows, -fstack-protector-strong adds a libssp-0.dll dependency and
+  # prevents static linking
+  if host_system != 'windows'
+    additional_c_args += ['-fstack-protector-strong']
+  endif
+endif
+
+foreach arg : additional_c_args
+  if cc.has_argument(arg)
+    add_project_arguments(arg, language: 'c')
+  endif
+endforeach
+
+# Windows MSVC warnings
+if cc.get_id() == 'msvc'
+  # Ignore several spurious warnings.
+  # If a warning is completely useless and spammy, use '/wdXXXX' to suppress it
+  # If a warning is harmless but hard to fix, use '/woXXXX' so it's shown once
+  # NOTE: Only add warnings here if you are sure they're spurious
+  add_project_arguments('/wd4035', '/wd4715', '/wd4116', '/wd4046', '/wd4068',
+    '/wd4820', '/wd4244', '/wd4255', '/wd4668',
+    language : 'c')
+endif
+
+opus_version = meson.project_version()
+
+opus_conf = configuration_data()
+opus_conf.set('PACKAGE_BUGREPORT', '"opus@xiph.org"')
+opus_conf.set('PACKAGE_NAME', '"opus"')
+opus_conf.set('PACKAGE_STRING', '"opus @0@"'.format(opus_version))
+opus_conf.set('PACKAGE_TARNAME', '"opus"')
+opus_conf.set('PACKAGE_URL', '""')
+opus_conf.set('PACKAGE_VERSION', '"@0@"'.format(opus_version))
+
+# FIXME: optional Ne10 dependency
+have_arm_ne10 = false
+
+libm = cc.find_library('m', required : false)
+
+opus_conf.set('HAVE_LRINTF', cc.has_function('lrintf', prefix: '#include <math.h>', dependencies: libm))
+opus_conf.set('HAVE_LRINT', cc.has_function('lrint', prefix: '#include <math.h>', dependencies: libm))
+opus_conf.set('HAVE___MALLOC_HOOK', cc.has_function('__malloc_hook', prefix: '#include <malloc.h>'))
+opus_conf.set('HAVE_STDINT_H', cc.check_header('stdint.h'))
+
+# Check for restrict keyword
+restrict_tmpl = '''
+typedef int * int_ptr;
+int foo (int_ptr @0@ ip, int * @0@ baz[]) {
+  return ip[0];
+}
+int main (int argc, char ** argv) {
+  int s[1];
+  int * @0@ t = s;
+  t[0] = 0;
+  return foo(t, (void *)0);
+}'''
+# Define restrict to the equivalent of the C99 restrict keyword, or to
+# nothing if this is not supported.  Do not define if restrict is
+# supported directly.
+if not cc.compiles(restrict_tmpl.format('restrict'), name : 'restrict keyword')
+  if cc.compiles(restrict_tmpl.format('__restrict'), name : '__restrict')
+    opus_conf.set('restrict', '__restrict')
+  elif cc.compiles(restrict_tmpl.format('__restrict__'), name : '__restrict__')
+    opus_conf.set('restrict', '__restrict')
+  elif cc.compiles(restrict_tmpl.format('_Restrict'), name : '_Restrict')
+    opus_conf.set('restrict', '_Restrict')
+  else
+    opus_conf.set('restrict', '/**/')
+  endif
+endif
+
+# Check for C99 variable-size arrays, or alloca() as fallback
+msg_use_alloca = false
+if cc.compiles('''static int x;
+                  char some_func (void) {
+                    char a[++x];
+                    a[sizeof a - 1] = 0;
+                    int N;
+                    return a[0];
+                  }''', name : 'C99 variable-size arrays')
+  opus_conf.set('VAR_ARRAYS', 1)
+  msg_use_alloca = 'NO (using C99 variable-size arrays instead)'
+elif cc.compiles('''#include <alloca.h>
+                    void some_func (void) {
+                      int foo=10;
+                      int * array = alloca(foo);
+                    }''', name : 'alloca (alloca.h)')
+  opus_conf.set('USE_ALLOCA', true)
+  opus_conf.set('HAVE_ALLOCA_H', true)
+  msg_use_alloca = true
+elif cc.compiles('''#include <malloc.h>
+                    #include <stdlib.h>
+                    void some_func (void) {
+                      int foo=10;
+                      int * array = alloca(foo);
+                    }''', name : 'alloca (std)')
+  opus_conf.set('USE_ALLOCA', true)
+  msg_use_alloca = true
+endif
+
+opts = [
+  [ 'fixed-point', 'FIXED_POINT' ],
+  [ 'fixed-point-debug', 'FIXED_DEBUG' ],
+  [ 'custom-modes', 'CUSTOM_MODES' ],
+  [ 'float-approx', 'FLOAT_APPROX' ],
+  [ 'assertions', 'ENABLE_ASSERTIONS' ],
+  [ 'hardening', 'ENABLE_HARDENING' ],
+  [ 'fuzzing', 'FUZZING' ],
+  [ 'check-asm', 'OPUS_CHECK_ASM' ],
+]
+
+foreach opt : opts
+  # we assume these are all boolean options
+  opt_foo = get_option(opt[0])
+  if opt_foo
+    opus_conf.set(opt[1], 1)
+  endif
+  set_variable('opt_' + opt[0].underscorify(), opt_foo)
+endforeach
+
+opt_asm = get_option('asm')
+opt_rtcd = get_option('rtcd')
+opt_intrinsics = get_option('intrinsics')
+extra_programs = get_option('extra-programs')
+opt_tests = get_option('tests')
+
+disable_float_api = not get_option('float-api')
+if disable_float_api
+  opus_conf.set('DISABLE_FLOAT_API', 1)
+endif
+
+# This is for the description in the pkg-config .pc file
+if opt_fixed_point
+  pc_build = 'fixed-point'
+else
+  pc_build = 'floating-point'
+endif
+if opt_custom_modes
+  pc_build = pc_build + ', custom modes'
+endif
+
+rtcd_support = []
+# With GCC, Clang, ICC, etc, we differentiate between 'may support this SIMD'
+# and 'presume we have this SIMD' by checking whether the SIMD / intrinsics can
+# be compiled by the compiler as-is (presume) or with SIMD cflags (may have).
+# With MSVC, the compiler will always build SIMD/intrinsics targeting all
+# specific instruction sets supported by that version of the compiler. No
+# special arguments are ever needed. If runtime CPU detection is not disabled,
+# we must always assume that we only 'may have' it.
+opus_can_presume_simd = true
+if cc.get_argument_syntax() == 'msvc'
+  if opt_rtcd.disabled()
+    warning('Building with an MSVC-like compiler and runtime CPU detection is disabled. Outputs may not run on all @0@ CPUs.'.format(host_cpu_family))
+  else
+    opus_can_presume_simd = false
+  endif
+endif
+
+opus_arm_external_asm = false
+
+asm_tmpl = '''
+int main (int argc, char ** argv) {
+  __asm__("@0@");
+  return 0;
+}'''
+
+asm_optimization = []
+inline_optimization = []
+if not opt_asm.disabled()
+  # Currently we only have inline asm for fixed-point
+  if host_cpu_family == 'arm' and opt_fixed_point
+    opus_conf.set('OPUS_ARM_ASM', true)
+
+    # Check if compiler supports gcc-style inline assembly
+    if cc.compiles('''#ifdef __GNUC_MINOR__
+                      #if (__GNUC__ * 1000 + __GNUC_MINOR__) < 3004
+                      #error GCC before 3.4 has critical bugs compiling inline assembly
+                      #endif
+                      #endif
+                      __asm__ (""::)''',
+                   name : 'compiler supports gcc-style inline assembly')
+
+      opus_conf.set('OPUS_ARM_INLINE_ASM', 1)
+
+      # AS_ASM_ARM_EDSP
+      if cc.compiles(asm_tmpl.format('qadd r3,r3,r3'),
+                     name : 'assembler supports EDSP instructions on ARM')
+        opus_conf.set('OPUS_ARM_INLINE_EDSP', 1)
+        inline_optimization += ['ESDP']
+      endif
+
+      # AS_ASM_ARM_MEDIA
+      if cc.compiles(asm_tmpl.format('shadd8 r3,r3,r3'),
+                     name : 'assembler supports ARMv6 media instructions on ARM')
+        opus_conf.set('OPUS_ARM_INLINE_MEDIA', 1)
+        inline_optimization += ['Media']
+      endif
+
+      # AS_ASM_ARM_NEON
+      if cc.compiles(asm_tmpl.format('vorr d0,d0,d0'),
+                     name : 'assembler supports NEON instructions on ARM')
+        opus_conf.set('OPUS_ARM_INLINE_NEON', 1)
+        inline_optimization += ['NEON']
+      endif
+    endif
+
+    # We need Perl to translate RVCT-syntax asm to gas syntax
+    perl = find_program('perl', required: get_option('asm'))
+    if perl.found()
+      opus_arm_external_asm = true
+      # opus_arm_presume_* mean we can and will use those instructions
+      # directly without doing runtime CPU detection.
+      # opus_arm_may_have_* mean we can emit those instructions, but we can
+      # only use them after runtime detection.
+      # The same rules apply for x86 assembly and intrinsics.
+
+      opus_arm_may_have_edsp = opus_conf.has('OPUS_ARM_INLINE_EDSP')
+      opus_arm_presume_edsp = opus_arm_may_have_edsp and opus_can_presume_simd
+
+      opus_arm_may_have_media = opus_conf.has('OPUS_ARM_INLINE_MEDIA')
+      opus_arm_presume_media = opus_arm_may_have_media and opus_can_presume_simd
+
+      opus_arm_may_have_neon = opus_conf.has('OPUS_ARM_INLINE_NEON')
+      opus_arm_presume_neon = opus_arm_may_have_neon and opus_can_presume_simd
+
+      if not opt_rtcd.disabled()
+        if not opus_arm_may_have_edsp
+          message('Trying to force-enable armv5e EDSP instructions...')
+          # AS_ASM_ARM_EDSP_FORCE
+          opus_arm_may_have_edsp = cc.compiles(asm_tmpl.format('.arch armv5te\n.object_arch armv4t\nqadd r3,r3,r3'),
+                                               name : 'Assembler supports EDSP instructions on ARM (forced)')
+        endif
+        if not opus_arm_may_have_media
+          message('Trying to force-enable ARMv6 media instructions...')
+          opus_arm_may_have_media = cc.compiles(asm_tmpl.format('.arch armv6\n.object_arch armv4t\nshadd8 r3,r3,r3'),
+                                                name : 'Assembler supports ARMv6 media instructions on ARM (forced)')
+        endif
+        if not opus_arm_may_have_neon
+          message('Trying to force-enable NEON instructions...')
+          opus_arm_may_have_neon = cc.compiles(asm_tmpl.format('.arch armv7-a\n.fpu neon\n.object_arch armv4t\nvorr d0,d0,d0'),
+                                               name : 'Assembler supports NEON instructions on ARM (forced)')
+        endif
+      endif
+
+      if opus_arm_may_have_edsp
+        opus_conf.set('OPUS_ARM_MAY_HAVE_EDSP', 1)
+        if opus_arm_presume_edsp
+          opus_conf.set('OPUS_ARM_PRESUME_EDSP', 1)
+          asm_optimization += ['EDSP']
+        else
+          rtcd_support += ['EDSP']
+        endif
+      endif
+      if opus_arm_may_have_media
+        opus_conf.set('OPUS_ARM_MAY_HAVE_MEDIA', 1)
+        if opus_arm_presume_media
+          opus_conf.set('OPUS_ARM_PRESUME_MEDIA', 1)
+          asm_optimization += ['Media']
+        else
+          rtcd_support += ['Media']
+        endif
+      endif
+      if opus_arm_may_have_neon
+        opus_conf.set('OPUS_ARM_MAY_HAVE_NEON', 1)
+        if opus_arm_presume_neon
+          opus_conf.set('OPUS_ARM_PRESUME_NEON', 1)
+          asm_optimization += ['NEON']
+        else
+          rtcd_support += ['NEON']
+        endif
+      endif
+
+      if cc.get_define('__APPLE__')
+        arm2gnu_args = ['--apple']
+      else
+        arm2gnu_args = []
+      endif
+    endif # found perl
+  else # arm + enable fixed point
+    if opt_asm.enabled()
+      error('asm option is enabled, but no assembly support for ' + host_cpu_family)
+    endif
+  endif
+endif # enable asm
+
+# Check whether we require assembly and we support assembly on this arch,
+# but none were detected. Can happen because of incorrect compiler flags, such
+# as missing -mfloat-abi=softfp on ARM32 softfp architectures.
+if opt_asm.enabled() and (asm_optimization.length() + inline_optimization.length()) == 0
+  error('asm option was enabled, but no assembly support was detected')
+endif
+
+# XXX: NEON has hardfp vs softfp compiler configuration issues
+# When targeting ARM32 softfp, we sometimes need to explicitly pass
+# -mfloat-abi=softfp to enable NEON. F.ex., on Android. It should
+# be set in the cross file.
+arm_neon_intr_link_args = ['-mfpu=neon']
+
+have_sse = false
+have_sse2 = false
+have_sse4_1 = false
+have_avx = false # no avx opus code yet
+have_neon_intr = false
+
+intrinsics_support = []
+if not opt_intrinsics.disabled()
+  if host_cpu_family in ['arm', 'aarch64']
+    # Check for ARMv7/AArch64 neon intrinsics
+    intrin_check = '''
+    #include <arm_neon.h>
+      int main (void) {
+        static float32x4_t A0, A1, SUMM;
+        SUMM = vmlaq_f32(SUMM, A0, A1);
+        return (int)vgetq_lane_f32(SUMM, 0);
+      }'''
+    intrin_name = 'ARMv7/AArch64 NEON'
+    if cc.links(intrin_check,
+                name: 'compiler supports @0@ intrinsics'.format(intrin_name))
+      opus_arm_presume_neon_intr = opus_can_presume_simd
+      opus_arm_may_have_neon_intr = true
+    else
+      opus_arm_presume_neon_intr = false
+      if cc.links(intrin_check,
+                  args: arm_neon_intr_link_args,
+                  name: 'compiler supports @0@ intrinsics with @1@'.format(intrin_name, ' '.join(arm_neon_intr_link_args)))
+        opus_arm_may_have_neon_intr = true
+      else
+        opus_arm_may_have_neon_intr = false
+      endif
+    endif
+
+    if opus_arm_may_have_neon_intr
+      have_neon_intr = true
+      intrinsics_support += [intrin_name]
+      opus_conf.set('OPUS_ARM_MAY_HAVE_NEON_INTR', 1)
+      if opus_arm_presume_neon_intr
+        opus_conf.set('OPUS_ARM_PRESUME_NEON_INTR', 1)
+      else
+        rtcd_support += [intrin_name]
+        opus_neon_intr_args = arm_neon_intr_link_args
+      endif
+    else
+      message('Compiler does not support @0@ intrinsics'.format(intrin_name))
+    endif
+
+    # Check for aarch64 neon intrinsics
+    intrin_check = '''
+    #include <arm_neon.h>
+      int main (void) {
+        static int32_t IN;
+        static int16_t OUT;
+        OUT = vqmovns_s32(IN);
+      }'''
+    intrin_name = 'AArch64 NEON'
+    if cc.links(intrin_check,
+                name: 'compiler supports @0@ intrinsics'.format(intrin_name))
+      opus_arm_presume_aarch64_neon_intr = opus_can_presume_simd
+      opus_arm_may_have_aarch64_neon_intr = true
+    else
+      opus_arm_presume_aarch64_neon_intr = false
+      if cc.links(intrin_check,
+                  args: arm_neon_intr_link_args,
+                  name: 'compiler supports @0@ intrinsics with @1@'.format(intrin_name, ' '.join(arm_neon_intr_link_args)))
+        opus_arm_may_have_aarch64_neon_intr = true
+      else
+        opus_arm_may_have_aarch64_neon_intr = false
+      endif
+    endif
+
+    if opus_arm_may_have_aarch64_neon_intr
+      intrinsics_support += [intrin_name]
+      opus_conf.set('OPUS_X86_MAY_HAVE_AARCH64_NEON_INTR', 1)
+      if opus_arm_presume_aarch64_neon_intr
+        opus_conf.set('OPUS_X86_PRESUME_AARCH64_NEON_INTR', 1)
+      endif
+    else
+      message('Compiler does not support @0@ intrinsics'.format(intrin_name))
+    endif
+  elif host_cpu_family in ['x86', 'x86_64']
+    # XXX: allow external override/specification of the flags
+    x86_intrinsics = [
+      [ 'SSE', 'xmmintrin.h', '__m128', '_mm_setzero_ps()', ['-msse'] ],
+      [ 'SSE2', 'emmintrin.h', '__m128i', '_mm_setzero_si128()', ['-msse2'] ],
+      [ 'SSE4.1', 'smmintrin.h', '__m128i', '_mm_setzero_si128(); mtest = _mm_cmpeq_epi64(mtest, mtest)', ['-msse4.1'] ],
+      [ 'AVX', 'immintrin.h', '__m256', '_mm256_setzero_ps()', ['-mavx'] ],
+    ]
+
+    foreach intrin : x86_intrinsics
+      intrin_check = '''#include <@0@>
+                        int main (int argc, char ** argv) {
+                          static @1@ mtest;
+                          mtest = @2@;
+                          return *((unsigned char *) &mtest) != 0;
+                        }'''.format(intrin[1],intrin[2],intrin[3])
+      intrin_name = intrin[0]
+      # Intrinsics arguments are not available with MSVC-like compilers
+      intrin_args = cc.get_argument_syntax() == 'msvc' ? [] : intrin[4]
+      if cc.links(intrin_check, name : 'compiler supports @0@ intrinsics'.format(intrin_name))
+        may_have_intrin = true
+        presume_intrin = opus_can_presume_simd
+      elif intrin_args.length() > 0
+        presume_intrin = false
+        if cc.links(intrin_check,
+                    args : intrin_args,
+                    name : 'compiler supports @0@ intrinsics with @1@'.format(intrin_name, ' '.join(intrin_args)))
+          may_have_intrin = true
+        else
+          may_have_intrin = false
+        endif
+      endif
+      if may_have_intrin
+        intrinsics_support += [intrin_name]
+        intrin_lower_name = intrin_name.to_lower().underscorify()
+        set_variable('have_' + intrin_lower_name, true)
+        opus_conf.set('OPUS_X86_MAY_HAVE_' + intrin_name.underscorify(), 1)
+        if presume_intrin
+          opus_conf.set('OPUS_X86_PRESUME_' + intrin_name.underscorify(), 1)
+        else
+          rtcd_support += [intrin_name]
+          set_variable('opus_@0@_args'.format(intrin_lower_name), intrin_args)
+        endif
+      else
+        message('Compiler does not support @0@ intrinsics'.format(intrin_name))
+      endif
+    endforeach
+
+    if not opt_rtcd.disabled()
+      get_cpuid_by_asm = false
+      cpuid_asm_code = '''
+        #include <stdio.h>
+        int main (int argc, char ** argv) {
+                 unsigned int CPUInfo0;
+                 unsigned int CPUInfo1;
+                 unsigned int CPUInfo2;
+                 unsigned int CPUInfo3;
+                 unsigned int InfoType;
+                #if defined(__i386__) && defined(__PIC__)
+                 __asm__ __volatile__ (
+                 "xchg %%ebx, %1\n"
+                 "cpuid\n"
+                 "xchg %%ebx, %1\n":
+                 "=a" (CPUInfo0),
+                 "=r" (CPUInfo1),
+                 "=c" (CPUInfo2),
+                 "=d" (CPUInfo3) :
+                 "a" (InfoType), "c" (0)
+                );
+               #else
+                 __asm__ __volatile__ (
+                 "cpuid":
+                 "=a" (CPUInfo0),
+                 "=b" (CPUInfo1),
+                 "=c" (CPUInfo2),
+                 "=d" (CPUInfo3) :
+                 "a" (InfoType), "c" (0)
+                );
+               #endif
+          return 0;
+        }'''
+      cpuid_c_code = '''
+        #include <cpuid.h>
+        int main (int argc, char ** argv) {
+          unsigned int CPUInfo0;
+          unsigned int CPUInfo1;
+          unsigned int CPUInfo2;
+          unsigned int CPUInfo3;
+          unsigned int InfoType;
+          __get_cpuid(InfoType, &CPUInfo0, &CPUInfo1, &CPUInfo2, &CPUInfo3);
+          return 0;
+        }'''
+      cpuid_msvc_code = '''
+        #include <intrin.h>
+        int main (void) {
+          int CPUInfo, InfoType;
+          __cpuid(&CPUInfo, InfoType);
+        }'''
+      if cc.links(cpuid_asm_code, name : 'Get X86 CPU info via inline assembly')
+        opus_conf.set('CPU_INFO_BY_ASM', 1)
+      elif cc.links(cpuid_c_code, name : 'Get X86 CPU info via C method')
+        opus_conf.set('CPU_INFO_BY_C', 1)
+      elif cc.get_define('_MSC_VER') != '' and cc.links(cpuid_msvc_code)
+        message('Getting X86 CPU info via __cpuid')
+      else
+        if opt_intrinsics.enabled() and opt_rtcd.enabled()
+          error('intrinsics and rtcd options are enabled, but no Get CPU Info method detected')
+        endif
+        warning('Get CPU Info method not detected, no rtcd for intrinsics')
+      endif
+    endif # opt_rtcd
+  else
+    if opt_intrinsics.enabled()
+      error('intrinsics option enabled, but no intrinsics support for ' + host_machine.get_cpu())
+    endif
+    warning('No intrinsics support for ' + host_machine.get_cpu())
+  endif
+endif
+
+# Check whether we require intrinsics and we support intrinsics on this arch,
+# but none were detected. Can happen because of incorrect compiler flags, such
+# as missing -mfloat-abi=softfp on ARM32 softfp architectures.
+if opt_intrinsics.enabled() and intrinsics_support.length() == 0
+  error('intrinsics option was enabled, but none were detected')
+endif
+
+if opt_rtcd.disabled()
+  rtcd_support = 'disabled'
+else
+  if rtcd_support.length() > 0
+    opus_conf.set('OPUS_HAVE_RTCD', 1)
+  else
+    if intrinsics_support.length() == 0
+      rtcd_support = 'none'
+      if opt_rtcd.enabled()
+        error('rtcd option is enabled, but no support for intrinsics or assembly is available')
+      endif
+    else
+      rtcd_support = 'not needed'
+    endif
+  endif
+endif
+
+# extract source file lists from .mk files
+mk_files = ['silk_sources.mk', 'opus_headers.mk', 'opus_sources.mk', 'silk_headers.mk', 'celt_sources.mk', 'celt_headers.mk']
+lines = run_command('meson/read-sources-list.py', mk_files, check: true).stdout().strip().split('\n')
+sources = {}
+foreach l : lines
+  a = l.split(' = ')
+  var_name = a[0]
+  file_list = a[1].split()
+  sources += {var_name: files(file_list)}
+endforeach
+
+subdir('include')
+subdir('silk')
+subdir('celt')
+subdir('src')
+
+configure_file(output: 'config.h', configuration: opus_conf)
+
+if not opt_tests.disabled()
+  subdir('celt/tests')
+  subdir('silk/tests')
+  subdir('tests')
+endif
+
+# pkg-config files (not using pkg module so we can use the existing .pc.in file)
+pkgconf = configuration_data()
+
+pkgconf.set('prefix', join_paths(get_option('prefix')))
+pkgconf.set('exec_prefix', '${prefix}')
+pkgconf.set('libdir', '${prefix}/@0@'.format(get_option('libdir')))
+pkgconf.set('includedir', '${prefix}/@0@'.format(get_option('includedir')))
+pkgconf.set('VERSION', opus_version)
+pkgconf.set('PC_BUILD', pc_build)
+pkgconf.set('LIBM', libm.found() ? '-lm' : '')
+
+pkg_install_dir = '@0@/pkgconfig'.format(get_option('libdir'))
+
+configure_file(input : 'opus.pc.in',
+  output : 'opus.pc',
+  configuration : pkgconf,
+  install_dir : pkg_install_dir)
+
+# The uninstalled one has hardcoded libtool + static lib stuff, skip it for now
+#configure_file(input : 'opus-uninstalled.pc.in',
+#  output : 'opus-uninstalled.pc',
+#  configuration : pkgconf,
+#  install : false)
+
+doxygen = find_program('doxygen', required: get_option('docs'))
+if doxygen.found()
+  subdir('doc')
+endif
+
+summary(
+  {
+    'C99 var arrays': opus_conf.has('VAR_ARRAYS'),
+    'C99 lrintf': opus_conf.has('HAVE_LRINTF'),
+    'Use alloca': msg_use_alloca,
+  },
+  section: 'Compiler support',
+  bool_yn: true,
+  list_sep: ', ',
+)
+
+# Parse optimization status
+foreach status : [['inline_optimization', opt_asm],
+                  ['asm_optimization', opt_asm],
+                  ['intrinsics_support', opt_intrinsics]]
+  res = status[0]
+  opt = status[1]
+  resval = get_variable(res)
+  if opt.disabled()
+    set_variable(res, 'disabled')
+  elif resval.length() == 0
+    if host_cpu_family not in ['arm', 'aarch64', 'x86', 'x86_64']
+      set_variable(res, 'No optimizations for your platform, please send patches')
+    else
+      set_variable(res, 'none')
+    endif
+  endif
+endforeach
+
+summary(
+  {
+    'Floating point support': not opt_fixed_point,
+    'Fast float approximations': opt_float_approx,
+    'Fixed point debugging': opt_fixed_point_debug,
+    'Inline assembly optimizations': inline_optimization,
+    'External assembly optimizations': asm_optimization,
+    'Intrinsics optimizations': intrinsics_support,
+    'Run-time CPU detection': rtcd_support,
+  },
+  section: 'Optimizations',
+  bool_yn: true,
+  list_sep: ', ',
+)
+summary(
+  {
+    'Custom modes': opt_custom_modes,
+    'Assertions': opt_assertions,
+    'Hardening': opt_hardening,
+    'Fuzzing': opt_fuzzing,
+    'Check ASM': opt_check_asm,
+    'API documentation': doxygen.found(),
+    'Extra programs': not extra_programs.disabled(),
+    'Tests': not opt_tests.disabled(),
+  },
+  section: 'General configuration',
+  bool_yn: true,
+  list_sep: ', ',
+)
diff --git a/meson/get-version.py b/meson/get-version.py
new file mode 100755
index 0000000..0e8b862
--- /dev/null
+++ b/meson/get-version.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python3
+#
+# Opus get-version.py
+#
+# Extracts versions for build:
+#  - Opus package version based on 'git describe' or $srcroot/package_version
+#  - libtool version based on configure.ac
+#  - macos lib version based on configure.ac
+#
+# Usage:
+# get-version.py [--package-version | --libtool-version | --darwin-version]
+import argparse
+import subprocess
+import os
+import sys
+import shutil
+
+if __name__ == '__main__':
+    arg_parser = argparse.ArgumentParser(description='Extract Opus package version or libtool version')
+    group = arg_parser.add_mutually_exclusive_group(required=True)
+    group.add_argument('--libtool-version', action='store_true')
+    group.add_argument('--package-version', action='store_true')
+    group.add_argument('--darwin-version', action='store_true')
+    args = arg_parser.parse_args()
+
+    srcroot = os.path.normpath(os.path.join(os.path.dirname(__file__), '..'))
+
+    # package version
+    if args.package_version:
+        package_version = None
+
+        # check if git checkout
+        git_dir = os.path.join(srcroot, '.git')
+        is_git = os.path.isdir(git_dir)
+        have_git = shutil.which('git') is not None
+
+        if is_git and have_git:
+            git_cmd = subprocess.run(['git', '--git-dir=' + git_dir, 'describe', 'HEAD'], stdout=subprocess.PIPE)
+            if git_cmd.returncode:
+                print('ERROR: Could not extract package version via `git describe` in', srcroot, file=sys.stderr)
+                sys.exit(-1)
+            package_version = git_cmd.stdout.decode('ascii').strip().lstrip('v')
+        else:
+            with open(os.path.join(srcroot, 'package_version'), 'r') as f:
+                for line in f:
+                    if line.startswith('PACKAGE_VERSION="'):
+                        package_version = line[17:].strip().lstrip('v').rstrip('"')
+                    if package_version:
+                        break
+
+        if not package_version:
+            print('ERROR: Could not extract package version from package_version file in', srcroot, file=sys.stderr)
+            sys.exit(-1)
+
+        print(package_version)
+        sys.exit(0)
+
+    # libtool version + darwin version
+    elif args.libtool_version or args.darwin_version:
+        opus_lt_cur = None
+        opus_lt_rev = None
+        opus_lt_age = None
+
+        with open(os.path.join(srcroot, 'configure.ac'), 'r') as f:
+            for line in f:
+                if line.strip().startswith('OPUS_LT_CURRENT='):
+                    opus_lt_cur = line[16:].strip()
+                elif line.strip().startswith('OPUS_LT_REVISION='):
+                    opus_lt_rev = line[17:].strip()
+                elif line.strip().startswith('OPUS_LT_AGE='):
+                    opus_lt_age = line[12:].strip()
+
+        if opus_lt_cur and opus_lt_rev and opus_lt_age:
+            opus_lt_cur = int(opus_lt_cur)
+            opus_lt_rev = int(opus_lt_rev)
+            opus_lt_age = int(opus_lt_age)
+            if args.libtool_version:
+              print('{}.{}.{}'.format(opus_lt_cur - opus_lt_age, opus_lt_age, opus_lt_rev))
+            elif args.darwin_version:
+              print('{}.{}.{}'.format(opus_lt_cur + 1, 0, 0))
+            sys.exit(0)
+        else:
+            print('ERROR: Could not extract libtool version from configure.ac file in', srcroot, file=sys.stderr)
+            sys.exit(-1)
+    else:
+        sys.exit(-1)
diff --git a/meson/read-sources-list.py b/meson/read-sources-list.py
new file mode 100755
index 0000000..fcbec50
--- /dev/null
+++ b/meson/read-sources-list.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python3
+#
+# opus/read-sources-list.py
+#
+# Parses .mk files and extracts list of source files.
+# Prints one line per source file list, with filenames space-separated.
+
+import sys
+
+if len(sys.argv) < 2:
+  sys.exit('Usage: {} sources_foo.mk [sources_bar.mk...]'.format(sys.argv[0]))
+
+for input_fn in sys.argv[1:]:
+  with open(input_fn, 'r', encoding='utf8') as f:
+    text = f.read()
+    text = text.replace('\\\n', '')
+
+    # Remove empty lines
+    lines = [line for line in text.split('\n') if line.strip()]
+
+    # Print SOURCES_XYZ = file1.c file2.c
+    for line in lines:
+      values = line.strip().split('=', maxsplit=2)
+      if len(values) != 2:
+        raise RuntimeError('Unable to parse line "{}" from file "{}"'.format(line, input_fn))
+      var, files = values
+      sources_list = [f for f in files.split(' ') if f]
+      print(var.strip(), '=', ' '.join(sources_list))
diff --git a/meson_options.txt b/meson_options.txt
new file mode 100644
index 0000000..360ff26
--- /dev/null
+++ b/meson_options.txt
@@ -0,0 +1,22 @@
+# Optimizations
+option('fixed-point', type : 'boolean', value : false, description : 'Compile without floating point (for machines without a fast enough FPU')
+option('fixed-point-debug', type : 'boolean', value : false, description : 'Debug fixed-point implementation')
+option('float-api', type : 'boolean', value : true, description : 'Compile with or without the floating point API (for machines with no float library')
+option('float-approx', type : 'boolean', value : false, description : 'Enable fast approximations for floating point (not supported on all platforms)')
+option('rtcd', type : 'feature', value : 'auto', description : 'Run-time CPU capabilities detection')
+option('asm', type : 'feature', value : 'auto', description : 'Assembly optimizations for ARM (fixed-point)')
+option('intrinsics', type : 'feature', value : 'auto', description : 'Intrinsics optimizations for ARM NEON or x86')
+
+option('custom-modes', type : 'boolean', value : false, description : 'Enable non-Opus modes, e.g. 44.1 kHz & 2^n frames')
+option('extra-programs', type : 'feature', value : 'auto', description : 'Extra programs (demo and tests)')
+option('assertions', type : 'boolean', value : false, description : 'Additional software error checking')
+option('hardening', type : 'boolean', value : true, description : 'Run-time checks that are cheap and safe for use in production')
+option('fuzzing', type : 'boolean', value : false, description : 'Causes the encoder to make random decisions')
+option('check-asm', type : 'boolean', value : false, description : 'Run bit-exactness checks between optimized and c implementations')
+
+# common feature options
+option('tests', type : 'feature', value : 'auto', description : 'Build tests')
+option('docs', type: 'feature', value: 'auto', description: 'Build API documentation')
+
+# other options
+option('docdir', type: 'string', value: 'doc/opus', description: 'Directory to install documentation into (default: DATADIR/doc/opus')
diff --git a/silk/debug.c b/silk/debug.c
index 71e69cc..eb0c36e 100644
--- a/silk/debug.c
+++ b/silk/debug.c
@@ -37,14 +37,13 @@
 
 #if SILK_TIC_TOC
 
-#ifdef _WIN32
-
 #if (defined(_WIN32) || defined(_WINCE))
 #include <windows.h>    /* timer */
 #else   /* Linux or Mac*/
 #include <sys/time.h>
 #endif
 
+#ifdef _WIN32
 unsigned long silk_GetHighResolutionTime(void) /* O  time in usec*/
 {
     /* Returns a time counter in microsec   */
@@ -68,7 +67,7 @@
 int           silk_Timer_nTimers = 0;
 int           silk_Timer_depth_ctr = 0;
 char          silk_Timer_tags[silk_NUM_TIMERS_MAX][silk_NUM_TIMERS_MAX_TAG_LEN];
-#ifdef WIN32
+#ifdef _WIN32
 LARGE_INTEGER silk_Timer_start[silk_NUM_TIMERS_MAX];
 #else
 unsigned long silk_Timer_start[silk_NUM_TIMERS_MAX];
@@ -79,7 +78,7 @@
 opus_int64     silk_Timer_max[silk_NUM_TIMERS_MAX];
 opus_int64     silk_Timer_depth[silk_NUM_TIMERS_MAX];
 
-#ifdef WIN32
+#ifdef _WIN32
 void silk_TimerSave(char *file_name)
 {
     if( silk_Timer_nTimers > 0 )
diff --git a/silk/meson.build b/silk/meson.build
new file mode 100644
index 0000000..7069237
--- /dev/null
+++ b/silk/meson.build
@@ -0,0 +1,53 @@
+silk_sources = sources['SILK_SOURCES']
+
+silk_sources_sse4_1 = sources['SILK_SOURCES_SSE4_1']
+
+silk_sources_neon_intr = sources['SILK_SOURCES_ARM_NEON_INTR']
+
+silk_sources_fixed_neon_intr = sources['SILK_SOURCES_FIXED_ARM_NEON_INTR']
+
+silk_sources_fixed = sources['SILK_SOURCES_FIXED']
+
+silk_sources_fixed_sse4_1 = sources['SILK_SOURCES_FIXED_SSE4_1']
+
+silk_sources_float = sources['SILK_SOURCES_FLOAT']
+
+if opt_fixed_point
+  silk_sources += silk_sources_fixed
+else
+  silk_sources += silk_sources_float
+endif
+
+silk_includes = [opus_includes, include_directories('float', 'fixed')]
+silk_static_libs = []
+
+foreach intr_name : ['sse4_1', 'neon_intr']
+  have_intr = get_variable('have_' + intr_name)
+  if not have_intr
+    continue
+  endif
+
+  intr_sources = get_variable('silk_sources_' + intr_name)
+  if opt_fixed_point
+    intr_sources += get_variable('silk_sources_fixed_' + intr_name)
+  endif
+
+  intr_args = get_variable('opus_@0@_args'.format(intr_name), [])
+  silk_static_libs += static_library('silk_' + intr_name, intr_sources,
+      c_args: intr_args,
+      include_directories: silk_includes,
+      install: false)
+endforeach
+
+silk_c_args = []
+if host_machine.system() == 'windows'
+  silk_c_args += ['-DDLL_EXPORT']
+endif
+
+silk_lib = static_library('opus-silk',
+  silk_sources,
+  c_args: silk_c_args,
+  include_directories: silk_includes,
+  link_whole: silk_static_libs,
+  dependencies: libm,
+  install: false)
diff --git a/silk/tests/meson.build b/silk/tests/meson.build
new file mode 100644
index 0000000..b7c70f7
--- /dev/null
+++ b/silk/tests/meson.build
@@ -0,0 +1,8 @@
+exe = executable('test_unit_LPC_inv_pred_gain',
+  'test_unit_LPC_inv_pred_gain.c', '../LPC_inv_pred_gain.c',
+  include_directories: opus_includes,
+  link_with: [celt_lib, celt_static_libs, silk_lib, silk_static_libs],
+  dependencies: libm,
+  install: false)
+
+test(test_name, exe)
diff --git a/src/meson.build b/src/meson.build
new file mode 100644
index 0000000..cc07ff0
--- /dev/null
+++ b/src/meson.build
@@ -0,0 +1,45 @@
+opus_sources = sources['OPUS_SOURCES']
+
+opus_sources_float = sources['OPUS_SOURCES_FLOAT']
+
+if not disable_float_api
+  opus_sources += opus_sources_float
+endif
+
+opus_lib_c_args = []
+if host_machine.system() == 'windows'
+  opus_lib_c_args += ['-DDLL_EXPORT']
+endif
+
+opus_lib = library('opus',
+  opus_sources,
+  version: libversion,
+  darwin_versions: macosversion,
+  c_args: opus_lib_c_args,
+  include_directories: opus_includes,
+  link_with: [celt_lib, silk_lib],
+  dependencies: libm,
+  install: true)
+
+opus_dep = declare_dependency(link_with: opus_lib,
+  include_directories: opus_public_includes)
+
+# Extra uninstalled Opus programs
+if not extra_programs.disabled()
+  foreach prog : ['opus_compare', 'opus_demo', 'repacketizer_demo']
+    executable(prog, '@0@.c'.format(prog),
+               include_directories: opus_includes,
+               link_with: opus_lib,
+               dependencies: libm,
+               install: false)
+  endforeach
+
+  if opt_custom_modes
+    executable('opus_custom_demo', '../celt/opus_custom_demo.c',
+               include_directories: opus_includes,
+               link_with: opus_lib,
+               dependencies: libm,
+               install: false)
+  endif
+
+endif
diff --git a/src/repacketizer_demo.c b/src/repacketizer_demo.c
index dc05c1b..43de701 100644
--- a/src/repacketizer_demo.c
+++ b/src/repacketizer_demo.c
@@ -119,7 +119,19 @@
       for (i=0;i<nb_packets;i++)
       {
          unsigned char ch[4];
-         err = fread(ch, 1, 4, fin);
+         if (fread(ch, 1, 4, fin)!=4)
+         {
+             if (feof(fin))
+             {
+                eof = 1;
+             } else {
+                fprintf(stderr, "Error reading payload length.\n");
+                fclose(fin);
+                fclose(fout);
+                return EXIT_FAILURE;
+             }
+             break;
+         }
          len[i] = char_to_int(ch);
          /*fprintf(stderr, "in len = %d\n", len[i]);*/
          if (len[i]>1500 || len[i]<0)
@@ -135,13 +147,31 @@
              }
              break;
          }
-         err = fread(ch, 1, 4, fin);
-         rng[i] = char_to_int(ch);
-         err = fread(packets[i], 1, len[i], fin);
-         if (feof(fin))
+         if (fread(ch, 1, 4, fin)!=4)
          {
-            eof = 1;
-            break;
+             if (feof(fin))
+             {
+                eof = 1;
+             } else {
+                fprintf(stderr, "Error reading.\n");
+                fclose(fin);
+                fclose(fout);
+                return EXIT_FAILURE;
+             }
+             break;
+         }
+         rng[i] = char_to_int(ch);
+         if (fread(packets[i], len[i], 1, fin)!=1) {
+             if (feof(fin))
+             {
+                eof = 1;
+             } else {
+                fprintf(stderr, "Error reading packet of %u bytes.\n", len[i]);
+                fclose(fin);
+                fclose(fout);
+                return EXIT_FAILURE;
+             }
+             break;
          }
          err = opus_repacketizer_cat(rp, packets[i], len[i]);
          if (err!=OPUS_OK)
diff --git a/tests/meson.build b/tests/meson.build
new file mode 100644
index 0000000..5f3ac9d
--- /dev/null
+++ b/tests/meson.build
@@ -0,0 +1,34 @@
+# Tests that link to libopus
+opus_tests = [
+  ['test_opus_api'],
+  ['test_opus_decode', [], 60],
+  ['test_opus_encode', 'opus_encode_regressions.c', 120],
+  ['test_opus_padding'],
+  ['test_opus_projection'],
+]
+
+foreach t : opus_tests
+  test_name = t.get(0)
+  extra_srcs = t.get(1, [])
+
+  test_kwargs = {}
+  if t.length() > 2
+    test_kwargs += {'timeout': t[2]}
+  endif
+
+  exe_kwargs = {}
+  # This test uses private symbols
+  if test_name == 'test_opus_projection'
+    exe_kwargs = {
+      'link_with': [celt_lib, silk_lib],
+      'objects': opus_lib.extract_all_objects(),
+    }
+  endif
+
+  exe = executable(test_name, '@0@.c'.format(test_name), extra_srcs,
+    include_directories: opus_includes,
+    dependencies: [libm, opus_dep],
+    install: false,
+    kwargs: exe_kwargs)
+  test(test_name, exe, kwargs: test_kwargs)
+endforeach
diff --git a/tests/opus_decode_fuzzer.c b/tests/opus_decode_fuzzer.c
index 20fa1e5..ea6ec4f 100644
--- a/tests/opus_decode_fuzzer.c
+++ b/tests/opus_decode_fuzzer.c
@@ -40,6 +40,8 @@
 /* 4 bytes: packet length, 4 bytes: encoder final range */
 #define SETUP_BYTE_COUNT 8
 
+#define MAX_DECODES 12
+
 typedef struct {
     int fs;
     int channels;
@@ -66,6 +68,7 @@
     TocInfo toc;
     int i = 0;
     int err = OPUS_OK;
+    int num_decodes = 0;
 
     /* Not enough data to setup the decoder (+1 for the ToC) */
     if (size < SETUP_BYTE_COUNT + 1) {
@@ -82,7 +85,7 @@
 
     pcm = (opus_int16*) malloc(sizeof(*pcm) * MAX_FRAME_SAMP * toc.channels);
 
-    while (i + SETUP_BYTE_COUNT < size) {
+    while (i + SETUP_BYTE_COUNT < size && num_decodes++ < MAX_DECODES) {
         int len, fec;
 
         len = (opus_uint32) data[i    ] << 24 |
diff --git a/tests/test_opus_decode.c b/tests/test_opus_decode.c
index 5197fa1..2d1e2d4 100644
--- a/tests/test_opus_decode.c
+++ b/tests/test_opus_decode.c
@@ -36,7 +36,7 @@
 #include <math.h>
 #include <string.h>
 #include <time.h>
-#if (!defined WIN32 && !defined _WIN32) || defined(__MINGW32__)
+#ifndef _WIN32
 #include <unistd.h>
 #else
 #include <process.h>
diff --git a/win32/VS2015/common.props b/win32/VS2015/common.props
index 6c757d8..f6e920b 100644
--- a/win32/VS2015/common.props
+++ b/win32/VS2015/common.props
@@ -23,7 +23,7 @@
       <CompileAsManaged>false</CompileAsManaged>
       <CompileAsWinRT>false</CompileAsWinRT>
       <AdditionalIncludeDirectories>..\..;..\..\include;..\..\silk;..\..\celt;..\..\win32;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
-      <PreprocessorDefinitions>HAVE_CONFIG_H;WIN32;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <RuntimeTypeInfo>false</RuntimeTypeInfo>
       <OpenMPSupport>false</OpenMPSupport>
     </ClCompile>