diff --git a/dep/cubeb/AUTHORS b/dep/cubeb/AUTHORS
new file mode 100644
index 000000000..f0f959522
--- /dev/null
+++ b/dep/cubeb/AUTHORS
@@ -0,0 +1,16 @@
+Matthew Gregan <kinetik@flim.org>
+Alexandre Ratchov <alex@caoua.org>
+Michael Wu <mwu@mozilla.com>
+Paul Adenot <paul@paul.cx>
+David Richards <drichards@mozilla.com>
+Sebastien Alaiwan <sebastien.alaiwan@gmail.com>
+KO Myung-Hun <komh@chollian.net>
+Haakon Sporsheim <haakon.sporsheim@telenordigital.com>
+Alex Chronopoulos <achronop@gmail.com>
+Jan Beich <jbeich@FreeBSD.org>
+Vito Caputo <vito.caputo@coreos.com>
+Landry Breuil <landry@openbsd.org>
+Jacek Caban <jacek@codeweavers.com>
+Paul Hancock <Paul.Hancock.17041993@live.com>
+Ted Mielczarek <ted@mielczarek.org>
+Chun-Min Chang <chun.m.chang@gmail.com>
diff --git a/dep/cubeb/CMakeLists.txt b/dep/cubeb/CMakeLists.txt
new file mode 100644
index 000000000..c88579391
--- /dev/null
+++ b/dep/cubeb/CMakeLists.txt
@@ -0,0 +1,332 @@
+# TODO
+# - backend selection via command line, rather than simply detecting headers.
+
+cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
+project(cubeb
+  VERSION 0.0.0)
+
+option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
+option(BUILD_TESTS "Build tests" ON)
+option(BUILD_RUST_LIBS "Build rust backends" OFF)
+option(BUILD_TOOLS "Build tools" ON)
+
+if(NOT CMAKE_BUILD_TYPE)
+  set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING
+      "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
+endif()
+
+if(POLICY CMP0063)
+  cmake_policy(SET CMP0063 NEW)
+endif()
+set(CMAKE_C_STANDARD 99)
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+if(NOT COMMAND add_sanitizers)
+  list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/sanitizers-cmake/cmake")
+  find_package(Sanitizers)
+  if(NOT COMMAND add_sanitizers)
+    message(FATAL_ERROR "Could not find sanitizers-cmake: run\n\tgit submodule update --init --recursive\nin base git checkout")
+  endif()
+endif()
+
+if(BUILD_TESTS)
+  if(NOT TARGET gtest_main)
+    if(NOT EXISTS "${PROJECT_SOURCE_DIR}/googletest/CMakeLists.txt")
+      message(FATAL_ERROR "Could not find googletest: run\n\tgit submodule update --init --recursive\nin base git checkout")
+    endif()
+    add_definitions(-DGTEST_HAS_TR1_TUPLE=0)
+    set(gtest_force_shared_crt ON CACHE BOOL "")
+    add_subdirectory(googletest)
+  endif()
+endif()
+
+if (BUILD_RUST_LIBS)
+  if(EXISTS "${PROJECT_SOURCE_DIR}/src/cubeb-pulse-rs")
+    set(USE_PULSE_RUST 1)
+  endif()
+  if(EXISTS "${PROJECT_SOURCE_DIR}/src/cubeb-coreaudio-rs")
+    set(USE_AUDIOUNIT_RUST 1)
+  endif()
+endif()
+
+# On OS/2, visibility attribute is not supported.
+if(NOT OS2)
+  set(CMAKE_C_VISIBILITY_PRESET hidden)
+  set(CMAKE_CXX_VISIBILITY_PRESET hidden)
+  set(CMAKE_VISIBILITY_INLINES_HIDDEN 1)
+endif()
+
+set(CMAKE_CXX_WARNING_LEVEL 4)
+if(NOT MSVC)
+  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wno-unused-parameter")
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused-parameter")
+endif()
+
+add_library(cubeb
+  src/cubeb.c
+  src/cubeb_mixer.cpp
+  src/cubeb_resampler.cpp
+  src/cubeb_log.cpp
+  src/cubeb_strings.c
+  src/cubeb_utils.cpp
+   $<TARGET_OBJECTS:speex>)
+target_include_directories(cubeb
+  PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<INSTALL_INTERFACE:include>
+)
+target_include_directories(cubeb PRIVATE src)
+target_compile_definitions(cubeb PRIVATE OUTSIDE_SPEEX)
+target_compile_definitions(cubeb PRIVATE FLOATING_POINT)
+target_compile_definitions(cubeb PRIVATE EXPORT=)
+target_compile_definitions(cubeb PRIVATE RANDOM_PREFIX=speex)
+
+add_sanitizers(cubeb)
+
+include(GenerateExportHeader)
+generate_export_header(cubeb EXPORT_FILE_NAME ${CMAKE_BINARY_DIR}/exports/cubeb_export.h)
+target_include_directories(cubeb
+  PUBLIC $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/exports>
+)
+
+if(UNIX)
+  include(GNUInstallDirs)
+else()
+  set(CMAKE_INSTALL_LIBDIR "lib")
+  set(CMAKE_INSTALL_BINDIR "bin")
+  set(CMAKE_INSTALL_DATADIR "share")
+  set(CMAKE_INSTALL_DOCDIR "${CMAKE_INSTALL_DATADIR}/doc")
+  set(CMAKE_INSTALL_INCLUDEDIR "include")
+endif()
+
+install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
+install(DIRECTORY ${CMAKE_BINARY_DIR}/exports/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME})
+
+include(CMakePackageConfigHelpers)
+write_basic_package_version_file(
+  "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
+  COMPATIBILITY SameMajorVersion
+)
+
+configure_package_config_file(
+  "Config.cmake.in"
+  "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
+  INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}"
+)
+
+install(TARGETS cubeb
+  EXPORT "${PROJECT_NAME}Targets"
+  DESTINATION ${CMAKE_INSTALL_PREFIX}
+  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+  INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
+)
+install(
+  FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
+  DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}"
+)
+install(
+  EXPORT "${PROJECT_NAME}Targets"
+  NAMESPACE "${PROJECT_NAME}::"
+  DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}"
+)
+
+add_library(speex OBJECT
+  src/speex/resample.c)
+set_target_properties(speex PROPERTIES POSITION_INDEPENDENT_CODE TRUE)
+target_compile_definitions(speex PRIVATE OUTSIDE_SPEEX)
+target_compile_definitions(speex PRIVATE FLOATING_POINT)
+target_compile_definitions(speex PRIVATE EXPORT=)
+target_compile_definitions(speex PRIVATE RANDOM_PREFIX=speex)
+
+include(CheckIncludeFiles)
+
+check_include_files(AudioUnit/AudioUnit.h USE_AUDIOUNIT)
+if(USE_AUDIOUNIT)
+  target_sources(cubeb PRIVATE
+    src/cubeb_audiounit.cpp
+    src/cubeb_osx_run_loop.cpp)
+  target_compile_definitions(cubeb PRIVATE USE_AUDIOUNIT)
+  target_link_libraries(cubeb PRIVATE "-framework AudioUnit" "-framework CoreAudio" "-framework CoreServices")
+endif()
+
+check_include_files(pulse/pulseaudio.h USE_PULSE)
+if(USE_PULSE)
+  target_sources(cubeb PRIVATE
+    src/cubeb_pulse.c)
+  target_compile_definitions(cubeb PRIVATE USE_PULSE)
+  target_link_libraries(cubeb PRIVATE pthread ${CMAKE_DL_LIBS})
+endif()
+
+check_include_files(alsa/asoundlib.h USE_ALSA)
+if(USE_ALSA)
+  target_sources(cubeb PRIVATE
+    src/cubeb_alsa.c)
+  target_compile_definitions(cubeb PRIVATE USE_ALSA)
+  target_link_libraries(cubeb PRIVATE pthread ${CMAKE_DL_LIBS})
+endif()
+
+check_include_files(jack/jack.h USE_JACK)
+if(USE_JACK)
+  target_sources(cubeb PRIVATE
+    src/cubeb_jack.cpp)
+  target_compile_definitions(cubeb PRIVATE USE_JACK)
+  target_link_libraries(cubeb PRIVATE pthread ${CMAKE_DL_LIBS})
+endif()
+
+check_include_files(audioclient.h USE_WASAPI)
+if(USE_WASAPI)
+  target_sources(cubeb PRIVATE
+    src/cubeb_wasapi.cpp)
+  target_compile_definitions(cubeb PRIVATE USE_WASAPI)
+  target_link_libraries(cubeb PRIVATE avrt ole32)
+endif()
+
+check_include_files("windows.h;mmsystem.h" USE_WINMM)
+if(USE_WINMM)
+  target_sources(cubeb PRIVATE
+    src/cubeb_winmm.c)
+  target_compile_definitions(cubeb PRIVATE USE_WINMM)
+  target_link_libraries(cubeb PRIVATE winmm)
+endif()
+
+check_include_files(SLES/OpenSLES.h USE_OPENSL)
+if(USE_OPENSL)
+  target_sources(cubeb PRIVATE
+    src/cubeb_opensl.c
+    src/cubeb-jni.cpp)
+  target_compile_definitions(cubeb PRIVATE USE_OPENSL)
+  target_link_libraries(cubeb PRIVATE OpenSLES)
+endif()
+
+check_include_files(android/log.h USE_AUDIOTRACK)
+if(USE_AUDIOTRACK)
+  target_sources(cubeb PRIVATE
+    src/cubeb_audiotrack.c)
+  target_compile_definitions(cubeb PRIVATE USE_AUDIOTRACK)
+  target_link_libraries(cubeb PRIVATE log)
+endif()
+
+check_include_files(sndio.h USE_SNDIO)
+if(USE_SNDIO)
+  target_sources(cubeb PRIVATE
+    src/cubeb_sndio.c)
+  target_compile_definitions(cubeb PRIVATE USE_SNDIO)
+  target_link_libraries(cubeb PRIVATE pthread ${CMAKE_DL_LIBS})
+endif()
+
+check_include_files(sys/audioio.h USE_SUN)
+if(USE_SUN)
+  target_sources(cubeb PRIVATE
+    src/cubeb_sun.c)
+  target_compile_definitions(cubeb PRIVATE USE_SUN)
+  target_link_libraries(cubeb PRIVATE pthread)
+endif()
+
+check_include_files(kai.h USE_KAI)
+if(USE_KAI)
+  target_sources(cubeb PRIVATE
+    src/cubeb_kai.c)
+  target_compile_definitions(cubeb PRIVATE USE_KAI)
+  target_link_libraries(cubeb PRIVATE kai)
+endif()
+
+if(USE_PULSE_RUST)
+  include(ExternalProject)
+  set_directory_properties(PROPERTIES EP_PREFIX ${CMAKE_BINARY_DIR}/rust)
+  ExternalProject_Add(
+    cubeb_pulse_rs
+    DOWNLOAD_COMMAND ""
+    CONFIGURE_COMMAND ""
+    BUILD_COMMAND cargo build COMMAND cargo build --release
+    BINARY_DIR "${CMAKE_SOURCE_DIR}/src/cubeb-pulse-rs"
+    INSTALL_COMMAND ""
+    LOG_BUILD ON)
+  add_dependencies(cubeb cubeb_pulse_rs)
+  target_compile_definitions(cubeb PRIVATE USE_PULSE_RUST)
+  target_link_libraries(cubeb PRIVATE
+    debug "${CMAKE_SOURCE_DIR}/src/cubeb-pulse-rs/target/debug/libcubeb_pulse.a"
+    optimized "${CMAKE_SOURCE_DIR}/src/cubeb-pulse-rs/target/release/libcubeb_pulse.a" pulse)
+endif()
+
+if(USE_AUDIOUNIT_RUST)
+  include(ExternalProject)
+  set_directory_properties(PROPERTIES EP_PREFIX ${CMAKE_BINARY_DIR}/rust)
+  ExternalProject_Add(
+    cubeb_coreaudio_rs
+    DOWNLOAD_COMMAND ""
+    CONFIGURE_COMMAND ""
+    BUILD_COMMAND cargo build COMMAND cargo build --release
+    BINARY_DIR "${CMAKE_SOURCE_DIR}/src/cubeb-coreaudio-rs"
+    INSTALL_COMMAND ""
+    LOG_BUILD ON)
+  add_dependencies(cubeb cubeb_coreaudio_rs)
+  target_compile_definitions(cubeb PRIVATE USE_AUDIOUNIT_RUST)
+  target_link_libraries(cubeb PRIVATE
+    debug "${CMAKE_SOURCE_DIR}/src/cubeb-coreaudio-rs/target/debug/libcubeb_coreaudio.a"
+    optimized "${CMAKE_SOURCE_DIR}/src/cubeb-coreaudio-rs/target/release/libcubeb_coreaudio.a")
+endif()
+
+find_package(Doxygen)
+if(DOXYGEN_FOUND)
+  configure_file(${CMAKE_CURRENT_SOURCE_DIR}/docs/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/docs/Doxyfile @ONLY)
+  add_custom_target(doc ALL
+    ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/docs/Doxyfile
+    WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/docs
+    COMMENT "Generating API documentation with Doxygen" VERBATIM)
+endif()
+
+if(BUILD_TESTS)
+  enable_testing()
+
+  macro(cubeb_add_test NAME)
+    add_executable(test_${NAME} test/test_${NAME}.cpp)
+    target_include_directories(test_${NAME} PRIVATE ${gtest_SOURCE_DIR}/include)
+    target_include_directories(test_${NAME} PRIVATE src)
+    target_link_libraries(test_${NAME} PRIVATE cubeb gtest_main)
+    add_test(${NAME} test_${NAME})
+    add_sanitizers(test_${NAME})
+    install(TARGETS test_${NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR})
+  endmacro(cubeb_add_test)
+
+  cubeb_add_test(sanity)
+  cubeb_add_test(tone)
+  cubeb_add_test(audio)
+  cubeb_add_test(record)
+  cubeb_add_test(devices)
+  cubeb_add_test(callback_ret)
+
+  add_executable(test_resampler test/test_resampler.cpp src/cubeb_resampler.cpp $<TARGET_OBJECTS:speex>)
+  target_include_directories(test_resampler PRIVATE ${gtest_SOURCE_DIR}/include)
+  target_include_directories(test_resampler PRIVATE src)
+  target_compile_definitions(test_resampler PRIVATE OUTSIDE_SPEEX)
+  target_compile_definitions(test_resampler PRIVATE FLOATING_POINT)
+  target_compile_definitions(test_resampler PRIVATE EXPORT=)
+  target_compile_definitions(test_resampler PRIVATE RANDOM_PREFIX=speex)
+  target_link_libraries(test_resampler PRIVATE cubeb gtest_main)
+  add_test(resampler test_resampler)
+  add_sanitizers(test_resampler)
+  install(TARGETS test_resampler DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR})
+
+  cubeb_add_test(duplex)
+
+  if (USE_WASAPI)
+    cubeb_add_test(overload_callback)
+    cubeb_add_test(loopback)
+  endif()
+
+  cubeb_add_test(latency test_latency)
+  cubeb_add_test(ring_array)
+
+  cubeb_add_test(utils)
+  cubeb_add_test(ring_buffer)
+  cubeb_add_test(device_changed_callback)
+endif()
+
+if(BUILD_TOOLS)
+  add_executable(cubeb-test tools/cubeb-test.cpp)
+  target_include_directories(cubeb-test PRIVATE src)
+  target_link_libraries(cubeb-test PRIVATE cubeb)
+  add_sanitizers(cubeb-test)
+  install(TARGETS cubeb-test DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR})
+endif()
diff --git a/dep/cubeb/Config.cmake.in b/dep/cubeb/Config.cmake.in
new file mode 100644
index 000000000..c5326ef33
--- /dev/null
+++ b/dep/cubeb/Config.cmake.in
@@ -0,0 +1,4 @@
+@PACKAGE_INIT@
+
+include("${CMAKE_CURRENT_LIST_DIR}/cubebTargets.cmake")
+check_required_components(cubeb)
\ No newline at end of file
diff --git a/dep/cubeb/INSTALL.md b/dep/cubeb/INSTALL.md
new file mode 100644
index 000000000..f8492243a
--- /dev/null
+++ b/dep/cubeb/INSTALL.md
@@ -0,0 +1,46 @@
+# Build instructions for libcubeb
+
+You must have CMake v3.1 or later installed.
+
+1. `git clone --recursive https://github.com/kinetiknz/cubeb.git`
+2. `mkdir cubeb-build`
+3. `cd cubeb-build`
+3. `cmake ../cubeb`
+4. `cmake --build .`
+5. `ctest`
+
+# Windows build notes
+
+Windows builds can use Microsoft Visual Studio 2015, Microsoft Visual Studio
+2017, or MinGW-w64 with Win32 threads (by passing `cmake -G` to generate the
+appropriate build configuration).
+
+## Microsoft Visual Studio 2015 or 2017 Command Line
+
+CMake can be used from the command line by following the build steps at the top
+of this file. CMake will select a default generator based on the environment,
+or one can be specified with the `-G` argument.
+
+## Microsoft Visual Studio 2017 IDE
+
+Visual Studio 2017 adds in built support for CMake. CMake can be used from
+within the IDE via the following steps:
+
+- Navigate to `File -> Open -> Cmake...`
+- Open `CMakeLists.txt` file in the root of the project.
+
+Note, to generate the build in the cubeb dir CMake settings need to be updated
+via: `CMake -> Change CMake Settings -> CMakeLists.txt`. The default
+configuration used by Visual Studio will place the build in a different location
+than the steps detailed at the top of this file.
+
+## MinGW-w64
+
+To build with MinGW-w64, install the following items:
+
+- Download and install MinGW-w64 with Win32 threads.
+- Download and install CMake.
+- Run MinGW-w64 Terminal from the Start Menu.
+- Follow the build steps at the top of this file, but at step 3 run:
+  `cmake -G "MinGW Makefiles" ..`
+- Continue the build steps at the top of this file.
diff --git a/dep/cubeb/LICENSE b/dep/cubeb/LICENSE
new file mode 100644
index 000000000..fffc9dc40
--- /dev/null
+++ b/dep/cubeb/LICENSE
@@ -0,0 +1,13 @@
+Copyright © 2011 Mozilla Foundation
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/dep/cubeb/README.md b/dep/cubeb/README.md
new file mode 100644
index 000000000..d26b3b645
--- /dev/null
+++ b/dep/cubeb/README.md
@@ -0,0 +1,6 @@
+[![Build Status](https://travis-ci.org/kinetiknz/cubeb.svg?branch=master)](https://travis-ci.org/kinetiknz/cubeb)
+[![Build status](https://ci.appveyor.com/api/projects/status/osv2r0m1j1nt9csr/branch/master?svg=true)](https://ci.appveyor.com/project/kinetiknz/cubeb/branch/master)
+
+See INSTALL.md for build instructions.
+
+Licensed under an ISC-style license.  See LICENSE for details.
diff --git a/dep/cubeb/TODO b/dep/cubeb/TODO
new file mode 100644
index 000000000..57288a42e
--- /dev/null
+++ b/dep/cubeb/TODO
@@ -0,0 +1,41 @@
+TODO:
+- directsound: incomplete and somewhat broken
+- osx: understand why AudioQueueGetCurrentTime can return negative mSampleTime
+- test (and fix) sub-prefill size data playback
+- report stream delay instead of position; leave position calculation to user
+- capture support
+- capture and output enumeration and configuration
+  - also expose default hardware config to allow decisions on speaker layout
+- prefill occurs at different times in each backend:
+  - pulse prefills async off worker thread after init
+  - coreaudio prefills during init
+  - alsa prefills async after start
+- expose configured prefill size; may differ from requested latency
+  - solved by exposing stream delay
+- xruns may occur in user callback but also in audio hardware
+  may need to expose details of hardware xruns to user api
+- document thread safety
+- document which calls may block, and when effects take effect
+- document what's permissible inside callbacks
+- implement basic channel mapping for surround
+  - vorbis has documented mapping based on channel count (if mapping type ==
+    0) -- http://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-800004.3.9
+    1 -> M
+    2 -> L, R
+    3 -> L, C, R
+    4 -> L, R, RL, RR
+    5 -> L, C, R, RL, RR
+    6 -> L, C, R, RL, RR, LFE
+    7 -> L, C, R, SL, SR, RC, LFE
+    8 -> L, C, R, SL, SR, RL, RR, LFE
+   >8 -> application defined
+  - wave files with channel count only
+    3 -> L, R, C
+    4 -> L, R, RL, RR
+    5 -> L, R, C, RL, RR
+    6 -> L, R, C, LFE, RL, RR
+    7 -> L, R, C, LFE, RC, SL, SR
+    8 -> L, R, C, LFE, RL, RR, SL, SR
+  - wave files with WAVE_FORMAT_EXTENSIBLE have explicitly mappings, can
+    extract these
+- implement configurable channel mapping
diff --git a/dep/cubeb/cubeb.supp b/dep/cubeb/cubeb.supp
new file mode 100644
index 000000000..0012ea51e
--- /dev/null
+++ b/dep/cubeb/cubeb.supp
@@ -0,0 +1,36 @@
+{
+  snd_config_update-malloc
+  Memcheck:Leak
+  fun:malloc
+  ...
+  fun:snd_config_update_r
+}
+{
+  snd1_dlobj_cache_get-malloc
+  Memcheck:Leak
+  fun:malloc
+  ...
+  fun:snd1_dlobj_cache_get
+}
+{
+  parse_defs-malloc
+  Memcheck:Leak
+  fun:malloc
+  ...
+  fun:parse_defs
+}
+{
+  parse_defs-calloc
+  Memcheck:Leak
+  fun:calloc
+  ...
+  fun:parse_defs
+}
+{
+  pa_client_conf_from_x11-malloc
+  Memcheck:Leak
+  fun:malloc
+  ...
+  fun:pa_client_conf_from_x11
+}
+
diff --git a/dep/cubeb/cubeb.vcxproj b/dep/cubeb/cubeb.vcxproj
new file mode 100644
index 000000000..fb8847881
--- /dev/null
+++ b/dep/cubeb/cubeb.vcxproj
@@ -0,0 +1,406 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="DebugFast|Win32">
+      <Configuration>DebugFast</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="DebugFast|x64">
+      <Configuration>DebugFast</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="ReleaseLTCG|Win32">
+      <Configuration>ReleaseLTCG</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="ReleaseLTCG|x64">
+      <Configuration>ReleaseLTCG</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="include\cubeb\cubeb.h" />
+    <ClInclude Include="include\cubeb\cubeb_export.h" />
+    <ClInclude Include="src\cubeb-internal.h" />
+    <ClInclude Include="src\cubeb-speex-resampler.h" />
+    <ClInclude Include="src\cubeb_array_queue.h" />
+    <ClInclude Include="src\cubeb_assert.h" />
+    <ClInclude Include="src\cubeb_log.h" />
+    <ClInclude Include="src\cubeb_mixer.h" />
+    <ClInclude Include="src\cubeb_resampler.h" />
+    <ClInclude Include="src\cubeb_resampler_internal.h" />
+    <ClInclude Include="src\cubeb_ringbuffer.h" />
+    <ClInclude Include="src\cubeb_ring_array.h" />
+    <ClInclude Include="src\cubeb_strings.h" />
+    <ClInclude Include="src\cubeb_utils.h" />
+    <ClInclude Include="src\cubeb_utils_win.h" />
+    <ClInclude Include="src\speex\arch.h" />
+    <ClInclude Include="src\speex\fixed_generic.h" />
+    <ClInclude Include="src\speex\resample_neon.h" />
+    <ClInclude Include="src\speex\resample_sse.h" />
+    <ClInclude Include="src\speex\speex_config_types.h" />
+    <ClInclude Include="src\speex\speex_resampler.h" />
+    <ClInclude Include="src\speex\stack_alloc.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="src\cubeb.c" />
+    <ClCompile Include="src\cubeb_log.cpp" />
+    <ClCompile Include="src\cubeb_mixer.cpp" />
+    <ClCompile Include="src\cubeb_resampler.cpp" />
+    <ClCompile Include="src\cubeb_strings.c" />
+    <ClCompile Include="src\cubeb_utils.cpp" />
+    <ClCompile Include="src\cubeb_wasapi.cpp" />
+    <ClCompile Include="src\cubeb_winmm.c" />
+    <ClCompile Include="src\speex\resample.c" />
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{72F9423C-91EE-4487-AAC6-555ED6F61AA1}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>inih</RootNamespace>
+    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <CharacterSet>NotSet</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <CharacterSet>NotSet</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <CharacterSet>NotSet</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <CharacterSet>NotSet</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>NotSet</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|Win32'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>NotSet</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>NotSet</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|x64'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>NotSet</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+    <OutDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</OutDir>
+    <IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
+    <TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
+    <TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
+    <LinkIncremental>true</LinkIncremental>
+    <OutDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</OutDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+    <OutDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</OutDir>
+    <IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
+    <TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'">
+    <IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
+    <TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
+    <LinkIncremental>true</LinkIncremental>
+    <OutDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</OutDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>false</LinkIncremental>
+    <OutDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</OutDir>
+    <IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
+    <TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|Win32'">
+    <LinkIncremental>false</LinkIncremental>
+    <OutDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</OutDir>
+    <IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
+    <TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
+    <TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
+    <LinkIncremental>false</LinkIncremental>
+    <OutDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</OutDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|x64'">
+    <IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
+    <TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
+    <LinkIncremental>false</LinkIncremental>
+    <OutDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</OutDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>TurnOffAllWarnings</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>USE_WASAPI;USE_WINMM;OUTSIDE_SPEEX;FLOATING_POINT;RANDOM_PREFIX=speex;EXPORT=;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <AdditionalIncludeDirectories>$(ProjectDir)include;$(ProjectDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <MinimalRebuild>false</MinimalRebuild>
+      <LanguageStandard>stdcpp17</LanguageStandard>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <ConformanceMode>true</ConformanceMode>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>SDL2.lib;SDL2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(SolutionDir)dep\lib32-debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+    </Link>
+    <Lib />
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>TurnOffAllWarnings</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>USE_WASAPI;USE_WINMM;OUTSIDE_SPEEX;FLOATING_POINT;RANDOM_PREFIX=speex;EXPORT=;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <AdditionalIncludeDirectories>$(ProjectDir)include;$(ProjectDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <MinimalRebuild>false</MinimalRebuild>
+      <LanguageStandard>stdcpp17</LanguageStandard>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <ConformanceMode>true</ConformanceMode>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>SDL2.lib;SDL2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(SolutionDir)dep\lib64-debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+    </Link>
+    <Lib />
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>TurnOffAllWarnings</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>USE_WASAPI;USE_WINMM;OUTSIDE_SPEEX;FLOATING_POINT;RANDOM_PREFIX=speex;EXPORT=;_CRT_NONSTDC_NO_DEPRECATE;_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <AdditionalIncludeDirectories>$(ProjectDir)include;$(ProjectDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <BasicRuntimeChecks>Default</BasicRuntimeChecks>
+      <MinimalRebuild>false</MinimalRebuild>
+      <LanguageStandard>stdcpp17</LanguageStandard>
+      <SupportJustMyCode>false</SupportJustMyCode>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+      <ConformanceMode>true</ConformanceMode>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>SDL2.lib;SDL2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(SolutionDir)dep\lib32-debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+    </Link>
+    <Lib />
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'">
+    <ClCompile>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>TurnOffAllWarnings</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>USE_WASAPI;USE_WINMM;OUTSIDE_SPEEX;FLOATING_POINT;RANDOM_PREFIX=speex;EXPORT=;_CRT_NONSTDC_NO_DEPRECATE;_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <AdditionalIncludeDirectories>$(ProjectDir)include;$(ProjectDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <BasicRuntimeChecks>Default</BasicRuntimeChecks>
+      <MinimalRebuild>false</MinimalRebuild>
+      <LanguageStandard>stdcpp17</LanguageStandard>
+      <SupportJustMyCode>false</SupportJustMyCode>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+      <ConformanceMode>true</ConformanceMode>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>SDL2.lib;SDL2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(SolutionDir)dep\lib64-debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+    </Link>
+    <Lib />
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>TurnOffAllWarnings</WarningLevel>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <Optimization>MaxSpeed</Optimization>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>USE_WASAPI;USE_WINMM;OUTSIDE_SPEEX;FLOATING_POINT;RANDOM_PREFIX=speex;EXPORT=;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>$(ProjectDir)include;$(ProjectDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <WholeProgramOptimization>false</WholeProgramOptimization>
+      <LanguageStandard>stdcpp17</LanguageStandard>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <ConformanceMode>true</ConformanceMode>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalDependencies>SDL2.lib;SDL2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(SolutionDir)dep\lib32;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+    </Link>
+    <Lib />
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|Win32'">
+    <ClCompile>
+      <WarningLevel>TurnOffAllWarnings</WarningLevel>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <Optimization>MaxSpeed</Optimization>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>USE_WASAPI;USE_WINMM;OUTSIDE_SPEEX;FLOATING_POINT;RANDOM_PREFIX=speex;EXPORT=;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>$(ProjectDir)include;$(ProjectDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <WholeProgramOptimization>true</WholeProgramOptimization>
+      <LanguageStandard>stdcpp17</LanguageStandard>
+      <OmitFramePointers>true</OmitFramePointers>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <ConformanceMode>true</ConformanceMode>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalDependencies>SDL2.lib;SDL2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(SolutionDir)dep\lib32;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+    </Link>
+    <Lib />
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <WarningLevel>TurnOffAllWarnings</WarningLevel>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <Optimization>MaxSpeed</Optimization>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>USE_WASAPI;USE_WINMM;OUTSIDE_SPEEX;FLOATING_POINT;RANDOM_PREFIX=speex;EXPORT=;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>$(ProjectDir)include;$(ProjectDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <WholeProgramOptimization>false</WholeProgramOptimization>
+      <LanguageStandard>stdcpp17</LanguageStandard>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <ConformanceMode>true</ConformanceMode>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalDependencies>SDL2.lib;SDL2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(SolutionDir)dep\lib64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+    </Link>
+    <Lib />
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|x64'">
+    <ClCompile>
+      <WarningLevel>TurnOffAllWarnings</WarningLevel>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <Optimization>MaxSpeed</Optimization>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>USE_WASAPI;USE_WINMM;OUTSIDE_SPEEX;FLOATING_POINT;RANDOM_PREFIX=speex;EXPORT=;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>$(ProjectDir)include;$(ProjectDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <WholeProgramOptimization>true</WholeProgramOptimization>
+      <LanguageStandard>stdcpp17</LanguageStandard>
+      <OmitFramePointers>true</OmitFramePointers>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <ConformanceMode>true</ConformanceMode>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalDependencies>SDL2.lib;SDL2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(SolutionDir)dep\lib64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+    </Link>
+    <Lib />
+  </ItemDefinitionGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/dep/cubeb/cubeb.vcxproj.filters b/dep/cubeb/cubeb.vcxproj.filters
new file mode 100644
index 000000000..3d0a6d76c
--- /dev/null
+++ b/dep/cubeb/cubeb.vcxproj.filters
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <ClInclude Include="include\cubeb\cubeb.h" />
+    <ClInclude Include="src\speex\fixed_generic.h">
+      <Filter>speex</Filter>
+    </ClInclude>
+    <ClInclude Include="src\speex\resample_neon.h">
+      <Filter>speex</Filter>
+    </ClInclude>
+    <ClInclude Include="src\speex\resample_sse.h">
+      <Filter>speex</Filter>
+    </ClInclude>
+    <ClInclude Include="src\speex\speex_config_types.h">
+      <Filter>speex</Filter>
+    </ClInclude>
+    <ClInclude Include="src\speex\speex_resampler.h">
+      <Filter>speex</Filter>
+    </ClInclude>
+    <ClInclude Include="src\speex\stack_alloc.h">
+      <Filter>speex</Filter>
+    </ClInclude>
+    <ClInclude Include="src\speex\arch.h">
+      <Filter>speex</Filter>
+    </ClInclude>
+    <ClInclude Include="include\cubeb\cubeb_export.h" />
+    <ClInclude Include="src\cubeb-internal.h" />
+    <ClInclude Include="src\cubeb-speex-resampler.h" />
+    <ClInclude Include="src\cubeb_array_queue.h" />
+    <ClInclude Include="src\cubeb_assert.h" />
+    <ClInclude Include="src\cubeb_log.h" />
+    <ClInclude Include="src\cubeb_mixer.h" />
+    <ClInclude Include="src\cubeb_resampler.h" />
+    <ClInclude Include="src\cubeb_resampler_internal.h" />
+    <ClInclude Include="src\cubeb_ring_array.h" />
+    <ClInclude Include="src\cubeb_ringbuffer.h" />
+    <ClInclude Include="src\cubeb_strings.h" />
+    <ClInclude Include="src\cubeb_utils.h" />
+    <ClInclude Include="src\cubeb_utils_win.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <Filter Include="speex">
+      <UniqueIdentifier>{a22ed8dc-2384-4a96-bd3b-2370d6d7cd62}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="src\speex\resample.c">
+      <Filter>speex</Filter>
+    </ClCompile>
+    <ClCompile Include="src\cubeb_wasapi.cpp" />
+    <ClCompile Include="src\cubeb_winmm.c" />
+    <ClCompile Include="src\cubeb.c" />
+    <ClCompile Include="src\cubeb_log.cpp" />
+    <ClCompile Include="src\cubeb_mixer.cpp" />
+    <ClCompile Include="src\cubeb_resampler.cpp" />
+    <ClCompile Include="src\cubeb_strings.c" />
+    <ClCompile Include="src\cubeb_utils.cpp" />
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/dep/cubeb/include/cubeb/cubeb.h b/dep/cubeb/include/cubeb/cubeb.h
new file mode 100644
index 000000000..b3fc56d2c
--- /dev/null
+++ b/dep/cubeb/include/cubeb/cubeb.h
@@ -0,0 +1,658 @@
+/*
+ * Copyright © 2011 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+#if !defined(CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382)
+#define CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382
+
+#include <stdint.h>
+#include <stdlib.h>
+#include "cubeb_export.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/** @mainpage
+
+    @section intro Introduction
+
+    This is the documentation for the <tt>libcubeb</tt> C API.
+    <tt>libcubeb</tt> is a callback-based audio API library allowing the
+    authoring of portable multiplatform audio playback and recording.
+
+    @section example Example code
+
+    This example shows how to create a duplex stream that pipes the microphone
+    to the speakers, with minimal latency and the proper sample-rate for the
+    platform.
+
+    @code
+    cubeb * app_ctx;
+    cubeb_init(&app_ctx, "Example Application", NULL);
+    int rv;
+    uint32_t rate;
+    uint32_t latency_frames;
+    uint64_t ts;
+
+    rv = cubeb_get_preferred_sample_rate(app_ctx, &rate);
+    if (rv != CUBEB_OK) {
+      fprintf(stderr, "Could not get preferred sample-rate");
+      return rv;
+    }
+
+    cubeb_stream_params output_params;
+    output_params.format = CUBEB_SAMPLE_FLOAT32NE;
+    output_params.rate = rate;
+    output_params.channels = 2;
+    output_params.layout = CUBEB_LAYOUT_UNDEFINED;
+    output_params.prefs = CUBEB_STREAM_PREF_NONE;
+
+    rv = cubeb_get_min_latency(app_ctx, &output_params, &latency_frames);
+    if (rv != CUBEB_OK) {
+      fprintf(stderr, "Could not get minimum latency");
+      return rv;
+    }
+
+    cubeb_stream_params input_params;
+    input_params.format = CUBEB_SAMPLE_FLOAT32NE;
+    input_params.rate = rate;
+    input_params.channels = 1;
+    input_params.layout = CUBEB_LAYOUT_UNDEFINED;
+    input_params.prefs = CUBEB_STREAM_PREF_NONE;
+
+    cubeb_stream * stm;
+    rv = cubeb_stream_init(app_ctx, &stm, "Example Stream 1",
+                           NULL, &input_params,
+                           NULL, &output_params,
+                           latency_frames,
+                           data_cb, state_cb,
+                           NULL);
+    if (rv != CUBEB_OK) {
+      fprintf(stderr, "Could not open the stream");
+      return rv;
+    }
+
+    rv = cubeb_stream_start(stm);
+    if (rv != CUBEB_OK) {
+      fprintf(stderr, "Could not start the stream");
+      return rv;
+    }
+    for (;;) {
+      cubeb_stream_get_position(stm, &ts);
+      printf("time=%llu\n", ts);
+      sleep(1);
+    }
+    rv = cubeb_stream_stop(stm);
+    if (rv != CUBEB_OK) {
+      fprintf(stderr, "Could not stop the stream");
+      return rv;
+    }
+
+    cubeb_stream_destroy(stm);
+    cubeb_destroy(app_ctx);
+    @endcode
+
+    @code
+    long data_cb(cubeb_stream * stm, void * user,
+                 const void * input_buffer, void * output_buffer, long nframes)
+    {
+      const float * in  = input_buffer;
+      float * out = output_buffer;
+
+      for (int i = 0; i < nframes; ++i) {
+        for (int c = 0; c < 2; ++c) {
+          out[2 * i + c] = in[i];
+        }
+      }
+      return nframes;
+    }
+    @endcode
+
+    @code
+    void state_cb(cubeb_stream * stm, void * user, cubeb_state state)
+    {
+      printf("state=%d\n", state);
+    }
+    @endcode
+*/
+
+/** @file
+    The <tt>libcubeb</tt> C API. */
+
+typedef struct cubeb cubeb;               /**< Opaque handle referencing the application state. */
+typedef struct cubeb_stream cubeb_stream; /**< Opaque handle referencing the stream state. */
+
+/** Sample format enumeration. */
+typedef enum {
+  /**< Little endian 16-bit signed PCM. */
+  CUBEB_SAMPLE_S16LE,
+  /**< Big endian 16-bit signed PCM. */
+  CUBEB_SAMPLE_S16BE,
+  /**< Little endian 32-bit IEEE floating point PCM. */
+  CUBEB_SAMPLE_FLOAT32LE,
+  /**< Big endian 32-bit IEEE floating point PCM. */
+  CUBEB_SAMPLE_FLOAT32BE,
+#if defined(WORDS_BIGENDIAN) || defined(__BIG_ENDIAN__)
+  /**< Native endian 16-bit signed PCM. */
+  CUBEB_SAMPLE_S16NE = CUBEB_SAMPLE_S16BE,
+  /**< Native endian 32-bit IEEE floating point PCM. */
+  CUBEB_SAMPLE_FLOAT32NE = CUBEB_SAMPLE_FLOAT32BE
+#else
+  /**< Native endian 16-bit signed PCM. */
+  CUBEB_SAMPLE_S16NE = CUBEB_SAMPLE_S16LE,
+  /**< Native endian 32-bit IEEE floating point PCM. */
+  CUBEB_SAMPLE_FLOAT32NE = CUBEB_SAMPLE_FLOAT32LE
+#endif
+} cubeb_sample_format;
+
+/** An opaque handle used to refer a particular input or output device
+ *  across calls. */
+typedef void const * cubeb_devid;
+
+/** Level (verbosity) of logging for a particular cubeb context. */
+typedef enum {
+  CUBEB_LOG_DISABLED = 0, /** < Logging disabled */
+  CUBEB_LOG_NORMAL = 1, /**< Logging lifetime operation (creation/destruction). */
+  CUBEB_LOG_VERBOSE = 2, /**< Verbose logging of callbacks, can have performance implications. */
+} cubeb_log_level;
+
+typedef enum {
+  CHANNEL_UNKNOWN = 0,
+  CHANNEL_FRONT_LEFT = 1 << 0,
+  CHANNEL_FRONT_RIGHT = 1 << 1,
+  CHANNEL_FRONT_CENTER = 1 << 2,
+  CHANNEL_LOW_FREQUENCY = 1 << 3,
+  CHANNEL_BACK_LEFT = 1 << 4,
+  CHANNEL_BACK_RIGHT = 1 << 5,
+  CHANNEL_FRONT_LEFT_OF_CENTER = 1 << 6,
+  CHANNEL_FRONT_RIGHT_OF_CENTER = 1 << 7,
+  CHANNEL_BACK_CENTER = 1 << 8,
+  CHANNEL_SIDE_LEFT = 1 << 9,
+  CHANNEL_SIDE_RIGHT = 1 << 10,
+  CHANNEL_TOP_CENTER = 1 << 11,
+  CHANNEL_TOP_FRONT_LEFT = 1 << 12,
+  CHANNEL_TOP_FRONT_CENTER = 1 << 13,
+  CHANNEL_TOP_FRONT_RIGHT = 1 << 14,
+  CHANNEL_TOP_BACK_LEFT = 1 << 15,
+  CHANNEL_TOP_BACK_CENTER = 1 << 16,
+  CHANNEL_TOP_BACK_RIGHT = 1 << 17
+} cubeb_channel;
+
+typedef uint32_t cubeb_channel_layout;
+// Some common layout definitions.
+enum {
+  CUBEB_LAYOUT_UNDEFINED = 0, // Indicate the speaker's layout is undefined.
+  CUBEB_LAYOUT_MONO = CHANNEL_FRONT_CENTER,
+  CUBEB_LAYOUT_MONO_LFE = CUBEB_LAYOUT_MONO | CHANNEL_LOW_FREQUENCY,
+  CUBEB_LAYOUT_STEREO = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT,
+  CUBEB_LAYOUT_STEREO_LFE = CUBEB_LAYOUT_STEREO | CHANNEL_LOW_FREQUENCY,
+  CUBEB_LAYOUT_3F =
+    CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT | CHANNEL_FRONT_CENTER,
+  CUBEB_LAYOUT_3F_LFE = CUBEB_LAYOUT_3F | CHANNEL_LOW_FREQUENCY,
+  CUBEB_LAYOUT_2F1 =
+    CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT | CHANNEL_BACK_CENTER,
+  CUBEB_LAYOUT_2F1_LFE = CUBEB_LAYOUT_2F1 | CHANNEL_LOW_FREQUENCY,
+  CUBEB_LAYOUT_3F1 = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
+                     CHANNEL_FRONT_CENTER | CHANNEL_BACK_CENTER,
+  CUBEB_LAYOUT_3F1_LFE = CUBEB_LAYOUT_3F1 | CHANNEL_LOW_FREQUENCY,
+  CUBEB_LAYOUT_2F2 = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
+                     CHANNEL_SIDE_LEFT | CHANNEL_SIDE_RIGHT,
+  CUBEB_LAYOUT_2F2_LFE = CUBEB_LAYOUT_2F2 | CHANNEL_LOW_FREQUENCY,
+  CUBEB_LAYOUT_QUAD = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
+                      CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT,
+  CUBEB_LAYOUT_QUAD_LFE = CUBEB_LAYOUT_QUAD | CHANNEL_LOW_FREQUENCY,
+  CUBEB_LAYOUT_3F2 = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
+                     CHANNEL_FRONT_CENTER | CHANNEL_SIDE_LEFT |
+                     CHANNEL_SIDE_RIGHT,
+  CUBEB_LAYOUT_3F2_LFE = CUBEB_LAYOUT_3F2 | CHANNEL_LOW_FREQUENCY,
+  CUBEB_LAYOUT_3F2_BACK = CUBEB_LAYOUT_QUAD | CHANNEL_FRONT_CENTER,
+  CUBEB_LAYOUT_3F2_LFE_BACK = CUBEB_LAYOUT_3F2_BACK | CHANNEL_LOW_FREQUENCY,
+  CUBEB_LAYOUT_3F3R_LFE = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
+                          CHANNEL_FRONT_CENTER | CHANNEL_LOW_FREQUENCY |
+                          CHANNEL_BACK_CENTER | CHANNEL_SIDE_LEFT |
+                          CHANNEL_SIDE_RIGHT,
+  CUBEB_LAYOUT_3F4_LFE = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
+                         CHANNEL_FRONT_CENTER | CHANNEL_LOW_FREQUENCY |
+                         CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT |
+                         CHANNEL_SIDE_LEFT | CHANNEL_SIDE_RIGHT,
+};
+
+/** Miscellaneous stream preferences. */
+typedef enum {
+  CUBEB_STREAM_PREF_NONE     = 0x00, /**< No stream preferences are requested. */
+  CUBEB_STREAM_PREF_LOOPBACK = 0x01, /**< Request a loopback stream. Should be
+                                         specified on the input params and an
+                                         output device to loopback from should
+                                         be passed in place of an input device. */
+  CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING = 0x02, /**< Disable switching
+                                                          default device on OS
+                                                          changes. */
+  CUBEB_STREAM_PREF_VOICE = 0x04  /**< This stream is going to transport voice data.
+                                       Depending on the backend and platform, this can
+                                       change the audio input or output devices
+                                       selected, as well as the quality of the stream,
+                                       for example to accomodate bluetooth SCO modes on
+                                       bluetooth devices. */
+} cubeb_stream_prefs;
+
+/** Stream format initialization parameters. */
+typedef struct {
+  cubeb_sample_format format;   /**< Requested sample format.  One of
+                                     #cubeb_sample_format. */
+  uint32_t rate;                /**< Requested sample rate.  Valid range is [1000, 192000]. */
+  uint32_t channels;            /**< Requested channel count.  Valid range is [1, 8]. */
+  cubeb_channel_layout layout;  /**< Requested channel layout. This must be consistent with the provided channels. CUBEB_LAYOUT_UNDEFINED if unknown */
+  cubeb_stream_prefs prefs;     /**< Requested preferences. */
+} cubeb_stream_params;
+
+/** Audio device description */
+typedef struct {
+  char * output_name; /**< The name of the output device */
+  char * input_name; /**< The name of the input device */
+} cubeb_device;
+
+/** Stream states signaled via state_callback. */
+typedef enum {
+  CUBEB_STATE_STARTED, /**< Stream started. */
+  CUBEB_STATE_STOPPED, /**< Stream stopped. */
+  CUBEB_STATE_DRAINED, /**< Stream drained. */
+  CUBEB_STATE_ERROR    /**< Stream disabled due to error. */
+} cubeb_state;
+
+/** Result code enumeration. */
+enum {
+  CUBEB_OK = 0,                       /**< Success. */
+  CUBEB_ERROR = -1,                   /**< Unclassified error. */
+  CUBEB_ERROR_INVALID_FORMAT = -2,    /**< Unsupported #cubeb_stream_params requested. */
+  CUBEB_ERROR_INVALID_PARAMETER = -3, /**< Invalid parameter specified. */
+  CUBEB_ERROR_NOT_SUPPORTED = -4,     /**< Optional function not implemented in current backend. */
+  CUBEB_ERROR_DEVICE_UNAVAILABLE = -5 /**< Device specified by #cubeb_devid not available. */
+};
+
+/**
+ * Whether a particular device is an input device (e.g. a microphone), or an
+ * output device (e.g. headphones). */
+typedef enum {
+  CUBEB_DEVICE_TYPE_UNKNOWN,
+  CUBEB_DEVICE_TYPE_INPUT,
+  CUBEB_DEVICE_TYPE_OUTPUT
+} cubeb_device_type;
+
+/**
+ * The state of a device.
+ */
+typedef enum {
+  CUBEB_DEVICE_STATE_DISABLED, /**< The device has been disabled at the system level. */
+  CUBEB_DEVICE_STATE_UNPLUGGED, /**< The device is enabled, but nothing is plugged into it. */
+  CUBEB_DEVICE_STATE_ENABLED /**< The device is enabled. */
+} cubeb_device_state;
+
+/**
+ * Architecture specific sample type.
+ */
+typedef enum {
+  CUBEB_DEVICE_FMT_S16LE          = 0x0010, /**< 16-bit integers, Little Endian. */
+  CUBEB_DEVICE_FMT_S16BE          = 0x0020, /**< 16-bit integers, Big Endian. */
+  CUBEB_DEVICE_FMT_F32LE          = 0x1000, /**< 32-bit floating point, Little Endian. */
+  CUBEB_DEVICE_FMT_F32BE          = 0x2000  /**< 32-bit floating point, Big Endian. */
+} cubeb_device_fmt;
+
+#if defined(WORDS_BIGENDIAN) || defined(__BIG_ENDIAN__)
+/** 16-bit integers, native endianess, when on a Big Endian environment. */
+#define CUBEB_DEVICE_FMT_S16NE     CUBEB_DEVICE_FMT_S16BE
+/** 32-bit floating points, native endianess, when on a Big Endian environment. */
+#define CUBEB_DEVICE_FMT_F32NE     CUBEB_DEVICE_FMT_F32BE
+#else
+/** 16-bit integers, native endianess, when on a Little Endian environment. */
+#define CUBEB_DEVICE_FMT_S16NE     CUBEB_DEVICE_FMT_S16LE
+/** 32-bit floating points, native endianess, when on a Little Endian
+ *  environment. */
+#define CUBEB_DEVICE_FMT_F32NE     CUBEB_DEVICE_FMT_F32LE
+#endif
+/** All the 16-bit integers types. */
+#define CUBEB_DEVICE_FMT_S16_MASK  (CUBEB_DEVICE_FMT_S16LE | CUBEB_DEVICE_FMT_S16BE)
+/** All the 32-bit floating points types. */
+#define CUBEB_DEVICE_FMT_F32_MASK  (CUBEB_DEVICE_FMT_F32LE | CUBEB_DEVICE_FMT_F32BE)
+/** All the device formats types. */
+#define CUBEB_DEVICE_FMT_ALL       (CUBEB_DEVICE_FMT_S16_MASK | CUBEB_DEVICE_FMT_F32_MASK)
+
+/** Channel type for a `cubeb_stream`. Depending on the backend and platform
+ * used, this can control inter-stream interruption, ducking, and volume
+ * control.
+ */
+typedef enum {
+  CUBEB_DEVICE_PREF_NONE          = 0x00,
+  CUBEB_DEVICE_PREF_MULTIMEDIA    = 0x01,
+  CUBEB_DEVICE_PREF_VOICE         = 0x02,
+  CUBEB_DEVICE_PREF_NOTIFICATION  = 0x04,
+  CUBEB_DEVICE_PREF_ALL           = 0x0F
+} cubeb_device_pref;
+
+/** This structure holds the characteristics
+ *  of an input or output audio device. It is obtained using
+ *  `cubeb_enumerate_devices`, which returns these structures via
+ *  `cubeb_device_collection` and must be destroyed via
+ *  `cubeb_device_collection_destroy`. */
+typedef struct {
+  cubeb_devid devid;          /**< Device identifier handle. */
+  char const * device_id;     /**< Device identifier which might be presented in a UI. */
+  char const * friendly_name; /**< Friendly device name which might be presented in a UI. */
+  char const * group_id;      /**< Two devices have the same group identifier if they belong to the same physical device; for example a headset and microphone. */
+  char const * vendor_name;   /**< Optional vendor name, may be NULL. */
+
+  cubeb_device_type type;     /**< Type of device (Input/Output). */
+  cubeb_device_state state;   /**< State of device disabled/enabled/unplugged. */
+  cubeb_device_pref preferred;/**< Preferred device. */
+
+  cubeb_device_fmt format;    /**< Sample format supported. */
+  cubeb_device_fmt default_format; /**< The default sample format for this device. */
+  uint32_t max_channels;      /**< Channels. */
+  uint32_t default_rate;      /**< Default/Preferred sample rate. */
+  uint32_t max_rate;          /**< Maximum sample rate supported. */
+  uint32_t min_rate;          /**< Minimum sample rate supported. */
+
+  uint32_t latency_lo;        /**< Lowest possible latency in frames. */
+  uint32_t latency_hi;        /**< Higest possible latency in frames. */
+} cubeb_device_info;
+
+/** Device collection.
+ *  Returned by `cubeb_enumerate_devices` and destroyed by
+ *  `cubeb_device_collection_destroy`. */
+typedef struct {
+  cubeb_device_info * device; /**< Array of pointers to device info. */
+  size_t count;               /**< Device count in collection. */
+} cubeb_device_collection;
+
+/** User supplied data callback.
+    - Calling other cubeb functions from this callback is unsafe.
+    - The code in the callback should be non-blocking.
+    - Returning less than the number of frames this callback asks for or
+      provides puts the stream in drain mode. This callback will not be called
+      again, and the state callback will be called with CUBEB_STATE_DRAINED when
+      all the frames have been output.
+    @param stream The stream for which this callback fired.
+    @param user_ptr The pointer passed to cubeb_stream_init.
+    @param input_buffer A pointer containing the input data, or nullptr
+                        if this is an output-only stream.
+    @param output_buffer A pointer to a buffer to be filled with audio samples,
+                         or nullptr if this is an input-only stream.
+    @param nframes The number of frames of the two buffer.
+    @retval If the stream has output, this is the number of frames written to
+            the output buffer. In this case, if this number is less than
+            nframes then the stream will start to drain. If the stream is
+            input only, then returning nframes indicates data has been read.
+            In this case, a value less than nframes will result in the stream
+            being stopped.
+    @retval CUBEB_ERROR on error, in which case the data callback will stop
+            and the stream will enter a shutdown state. */
+typedef long (* cubeb_data_callback)(cubeb_stream * stream,
+                                     void * user_ptr,
+                                     void const * input_buffer,
+                                     void * output_buffer,
+                                     long nframes);
+
+/** User supplied state callback.
+    @param stream The stream for this this callback fired.
+    @param user_ptr The pointer passed to cubeb_stream_init.
+    @param state The new state of the stream. */
+typedef void (* cubeb_state_callback)(cubeb_stream * stream,
+                                      void * user_ptr,
+                                      cubeb_state state);
+
+/**
+ * User supplied callback called when the underlying device changed.
+ * @param user The pointer passed to cubeb_stream_init. */
+typedef void (* cubeb_device_changed_callback)(void * user_ptr);
+
+/**
+ * User supplied callback called when the underlying device collection changed.
+ * @param context A pointer to the cubeb context.
+ * @param user_ptr The pointer passed to cubeb_register_device_collection_changed. */
+typedef void (* cubeb_device_collection_changed_callback)(cubeb * context,
+                                                          void * user_ptr);
+
+/** User supplied callback called when a message needs logging. */
+typedef void (* cubeb_log_callback)(char const * fmt, ...);
+
+/** Initialize an application context.  This will perform any library or
+    application scoped initialization.
+
+    Note: On Windows platforms, COM must be initialized in MTA mode on
+    any thread that will call the cubeb API.
+
+    @param context A out param where an opaque pointer to the application
+                   context will be returned.
+    @param context_name A name for the context. Depending on the platform this
+                        can appear in different locations.
+    @param backend_name The name of the cubeb backend user desires to select.
+                        Accepted values self-documented in cubeb.c: init_oneshot
+                        If NULL, a default ordering is used for backend choice.
+                        A valid choice overrides all other possible backends,
+                        so long as the backend was included at compile time.
+    @retval CUBEB_OK in case of success.
+    @retval CUBEB_ERROR in case of error, for example because the host
+                        has no audio hardware. */
+CUBEB_EXPORT int cubeb_init(cubeb ** context, char const * context_name,
+                                              char const * backend_name);
+
+/** Get a read-only string identifying this context's current backend.
+    @param context A pointer to the cubeb context.
+    @retval Read-only string identifying current backend. */
+CUBEB_EXPORT char const * cubeb_get_backend_id(cubeb * context);
+
+/** Get the maximum possible number of channels.
+    @param context A pointer to the cubeb context.
+    @param max_channels The maximum number of channels.
+    @retval CUBEB_OK
+    @retval CUBEB_ERROR_INVALID_PARAMETER
+    @retval CUBEB_ERROR_NOT_SUPPORTED
+    @retval CUBEB_ERROR */
+CUBEB_EXPORT int cubeb_get_max_channel_count(cubeb * context, uint32_t * max_channels);
+
+/** Get the minimal latency value, in frames, that is guaranteed to work
+    when creating a stream for the specified sample rate. This is platform,
+    hardware and backend dependent.
+    @param context A pointer to the cubeb context.
+    @param params On some backends, the minimum achievable latency depends on
+                  the characteristics of the stream.
+    @param latency_frames The latency value, in frames, to pass to
+                          cubeb_stream_init.
+    @retval CUBEB_OK
+    @retval CUBEB_ERROR_INVALID_PARAMETER
+    @retval CUBEB_ERROR_NOT_SUPPORTED */
+CUBEB_EXPORT int cubeb_get_min_latency(cubeb * context,
+                                       cubeb_stream_params * params,
+                                       uint32_t * latency_frames);
+
+/** Get the preferred sample rate for this backend: this is hardware and
+    platform dependent, and can avoid resampling, and/or trigger fastpaths.
+    @param context A pointer to the cubeb context.
+    @param rate The samplerate (in Hz) the current configuration prefers.
+    @retval CUBEB_OK
+    @retval CUBEB_ERROR_INVALID_PARAMETER
+    @retval CUBEB_ERROR_NOT_SUPPORTED */
+CUBEB_EXPORT int cubeb_get_preferred_sample_rate(cubeb * context, uint32_t * rate);
+
+/** Destroy an application context. This must be called after all stream have
+ *  been destroyed.
+    @param context A pointer to the cubeb context.*/
+CUBEB_EXPORT void cubeb_destroy(cubeb * context);
+
+/** Initialize a stream associated with the supplied application context.
+    @param context A pointer to the cubeb context.
+    @param stream An out parameter to be filled with the an opaque pointer to a
+                  cubeb stream.
+    @param stream_name A name for this stream.
+    @param input_device Device for the input side of the stream. If NULL the
+                        default input device is used.
+    @param input_stream_params Parameters for the input side of the stream, or
+                               NULL if this stream is output only.
+    @param output_device Device for the output side of the stream. If NULL the
+                         default output device is used.
+    @param output_stream_params Parameters for the output side of the stream, or
+                                NULL if this stream is input only.
+    @param latency_frames Stream latency in frames.  Valid range
+                          is [1, 96000].
+    @param data_callback Will be called to preroll data before playback is
+                         started by cubeb_stream_start.
+    @param state_callback A pointer to a state callback.
+    @param user_ptr A pointer that will be passed to the callbacks. This pointer
+                    must outlive the life time of the stream.
+    @retval CUBEB_OK
+    @retval CUBEB_ERROR
+    @retval CUBEB_ERROR_INVALID_FORMAT
+    @retval CUBEB_ERROR_DEVICE_UNAVAILABLE */
+CUBEB_EXPORT int cubeb_stream_init(cubeb * context,
+                                   cubeb_stream ** stream,
+                                   char const * stream_name,
+                                   cubeb_devid input_device,
+                                   cubeb_stream_params * input_stream_params,
+                                   cubeb_devid output_device,
+                                   cubeb_stream_params * output_stream_params,
+                                   uint32_t latency_frames,
+                                   cubeb_data_callback data_callback,
+                                   cubeb_state_callback state_callback,
+                                   void * user_ptr);
+
+/** Destroy a stream. `cubeb_stream_stop` MUST be called before destroying a
+    stream.
+    @param stream The stream to destroy. */
+CUBEB_EXPORT void cubeb_stream_destroy(cubeb_stream * stream);
+
+/** Start playback.
+    @param stream
+    @retval CUBEB_OK
+    @retval CUBEB_ERROR */
+CUBEB_EXPORT int cubeb_stream_start(cubeb_stream * stream);
+
+/** Stop playback.
+    @param stream
+    @retval CUBEB_OK
+    @retval CUBEB_ERROR */
+CUBEB_EXPORT int cubeb_stream_stop(cubeb_stream * stream);
+
+/** Reset stream to the default device.
+    @param stream
+    @retval CUBEB_OK
+    @retval CUBEB_ERROR_INVALID_PARAMETER
+    @retval CUBEB_ERROR_NOT_SUPPORTED
+    @retval CUBEB_ERROR */
+CUBEB_EXPORT int cubeb_stream_reset_default_device(cubeb_stream * stream);
+
+/** Get the current stream playback position.
+    @param stream
+    @param position Playback position in frames.
+    @retval CUBEB_OK
+    @retval CUBEB_ERROR */
+CUBEB_EXPORT int cubeb_stream_get_position(cubeb_stream * stream, uint64_t * position);
+
+/** Get the latency for this stream, in frames. This is the number of frames
+    between the time cubeb acquires the data in the callback and the listener
+    can hear the sound.
+    @param stream
+    @param latency Current approximate stream latency in frames.
+    @retval CUBEB_OK
+    @retval CUBEB_ERROR_NOT_SUPPORTED
+    @retval CUBEB_ERROR */
+CUBEB_EXPORT int cubeb_stream_get_latency(cubeb_stream * stream, uint32_t * latency);
+
+/** Set the volume for a stream.
+    @param stream the stream for which to adjust the volume.
+    @param volume a float between 0.0 (muted) and 1.0 (maximum volume)
+    @retval CUBEB_OK
+    @retval CUBEB_ERROR_INVALID_PARAMETER volume is outside [0.0, 1.0] or
+            stream is an invalid pointer
+    @retval CUBEB_ERROR_NOT_SUPPORTED */
+CUBEB_EXPORT int cubeb_stream_set_volume(cubeb_stream * stream, float volume);
+
+/** Get the current output device for this stream.
+    @param stm the stream for which to query the current output device
+    @param device a pointer in which the current output device will be stored.
+    @retval CUBEB_OK in case of success
+    @retval CUBEB_ERROR_INVALID_PARAMETER if either stm, device or count are
+            invalid pointers
+    @retval CUBEB_ERROR_NOT_SUPPORTED */
+CUBEB_EXPORT int cubeb_stream_get_current_device(cubeb_stream * stm,
+                                                 cubeb_device ** const device);
+
+/** Destroy a cubeb_device structure.
+    @param stream the stream passed in cubeb_stream_get_current_device
+    @param devices the devices to destroy
+    @retval CUBEB_OK in case of success
+    @retval CUBEB_ERROR_INVALID_PARAMETER if devices is an invalid pointer
+    @retval CUBEB_ERROR_NOT_SUPPORTED */
+CUBEB_EXPORT int cubeb_stream_device_destroy(cubeb_stream * stream,
+                                             cubeb_device * devices);
+
+/** Set a callback to be notified when the output device changes.
+    @param stream the stream for which to set the callback.
+    @param device_changed_callback a function called whenever the device has
+           changed. Passing NULL allow to unregister a function
+    @retval CUBEB_OK
+    @retval CUBEB_ERROR_INVALID_PARAMETER if either stream or
+            device_changed_callback are invalid pointers.
+    @retval CUBEB_ERROR_NOT_SUPPORTED */
+CUBEB_EXPORT int cubeb_stream_register_device_changed_callback(cubeb_stream * stream,
+                                                               cubeb_device_changed_callback device_changed_callback);
+
+/** Return the user data pointer registered with the stream with cubeb_stream_init.
+    @param stream the stream for which to retrieve user data pointer.
+    @retval user data pointer */
+CUBEB_EXPORT void * cubeb_stream_user_ptr(cubeb_stream * stream);
+
+/** Returns enumerated devices.
+    @param context
+    @param devtype device type to include
+    @param collection output collection. Must be destroyed with cubeb_device_collection_destroy
+    @retval CUBEB_OK in case of success
+    @retval CUBEB_ERROR_INVALID_PARAMETER if collection is an invalid pointer
+    @retval CUBEB_ERROR_NOT_SUPPORTED */
+CUBEB_EXPORT int cubeb_enumerate_devices(cubeb * context,
+                                         cubeb_device_type devtype,
+                                         cubeb_device_collection * collection);
+
+/** Destroy a cubeb_device_collection, and its `cubeb_device_info`.
+    @param context
+    @param collection collection to destroy
+    @retval CUBEB_OK
+    @retval CUBEB_ERROR_INVALID_PARAMETER if collection is an invalid pointer */
+CUBEB_EXPORT int cubeb_device_collection_destroy(cubeb * context,
+                                                 cubeb_device_collection * collection);
+
+/** Registers a callback which is called when the system detects
+    a new device or a device is removed.
+    @param context
+    @param devtype device type to include. Different callbacks and user pointers
+           can be registered for each devtype. The hybrid devtype
+           `CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT` is also valid
+           and will register the provided callback and user pointer in both sides.
+    @param callback a function called whenever the system device list changes.
+           Passing NULL allow to unregister a function. You have to unregister
+           first before you register a new callback.
+    @param user_ptr pointer to user specified data which will be present in
+           subsequent callbacks.
+    @retval CUBEB_ERROR_NOT_SUPPORTED */
+CUBEB_EXPORT int cubeb_register_device_collection_changed(cubeb * context,
+                                                          cubeb_device_type devtype,
+                                                          cubeb_device_collection_changed_callback callback,
+                                                          void * user_ptr);
+
+/** Set a callback to be called with a message.
+    @param log_level CUBEB_LOG_VERBOSE, CUBEB_LOG_NORMAL.
+    @param log_callback A function called with a message when there is
+                        something to log. Pass NULL to unregister.
+    @retval CUBEB_OK in case of success.
+    @retval CUBEB_ERROR_INVALID_PARAMETER if either context or log_callback are
+                                          invalid pointers, or if level is not
+                                          in cubeb_log_level. */
+CUBEB_EXPORT int cubeb_set_log_callback(cubeb_log_level log_level,
+                                        cubeb_log_callback log_callback);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382 */
diff --git a/dep/cubeb/include/cubeb/cubeb_export.h b/dep/cubeb/include/cubeb/cubeb_export.h
new file mode 100644
index 000000000..fa4cf0ca0
--- /dev/null
+++ b/dep/cubeb/include/cubeb/cubeb_export.h
@@ -0,0 +1,12 @@
+#ifndef CUBEB_EXPORT_H
+#define CUBEB_EXPORT_H
+
+#define CUBEB_EXPORT
+#define CUBEB_NO_EXPORT
+
+#ifdef WIN32
+#pragma comment(lib, "winmm.lib")
+#pragma comment(lib, "avrt.lib")
+#endif
+
+#endif
diff --git a/dep/cubeb/src/android/audiotrack_definitions.h b/dep/cubeb/src/android/audiotrack_definitions.h
new file mode 100644
index 000000000..cd501533d
--- /dev/null
+++ b/dep/cubeb/src/android/audiotrack_definitions.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+/*
+ * The following definitions are copied from the android sources. Only the
+ * relevant enum member and values needed are copied.
+ */
+
+/*
+ * From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/utils/Errors.h
+ */
+typedef int32_t status_t;
+
+/*
+ * From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/media/AudioTrack.h
+ */
+struct Buffer {
+  uint32_t    flags;
+  int         channelCount;
+  int         format;
+  size_t      frameCount;
+  size_t      size;
+  union {
+    void*       raw;
+    short*      i16;
+    int8_t*     i8;
+  };
+};
+
+enum event_type {
+  EVENT_MORE_DATA = 0,
+  EVENT_UNDERRUN = 1,
+  EVENT_LOOP_END = 2,
+  EVENT_MARKER = 3,
+  EVENT_NEW_POS = 4,
+  EVENT_BUFFER_END = 5
+};
+
+/**
+ * From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/media/AudioSystem.h
+ * and 
+ * https://android.googlesource.com/platform/system/core/+/android-4.2.2_r1/include/system/audio.h
+ */
+
+#define AUDIO_STREAM_TYPE_MUSIC 3
+
+enum {
+  AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS  = 0x1,
+  AUDIO_CHANNEL_OUT_FRONT_RIGHT_ICS = 0x2,
+  AUDIO_CHANNEL_OUT_MONO_ICS     = AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS,
+  AUDIO_CHANNEL_OUT_STEREO_ICS   = (AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS | AUDIO_CHANNEL_OUT_FRONT_RIGHT_ICS)
+} AudioTrack_ChannelMapping_ICS;
+
+enum {
+  AUDIO_CHANNEL_OUT_FRONT_LEFT_Legacy = 0x4,
+  AUDIO_CHANNEL_OUT_FRONT_RIGHT_Legacy = 0x8,
+  AUDIO_CHANNEL_OUT_MONO_Legacy = AUDIO_CHANNEL_OUT_FRONT_LEFT_Legacy,
+  AUDIO_CHANNEL_OUT_STEREO_Legacy = (AUDIO_CHANNEL_OUT_FRONT_LEFT_Legacy | AUDIO_CHANNEL_OUT_FRONT_RIGHT_Legacy)
+} AudioTrack_ChannelMapping_Legacy;
+
+typedef enum {
+  AUDIO_FORMAT_PCM = 0x00000000,
+  AUDIO_FORMAT_PCM_SUB_16_BIT = 0x1,
+  AUDIO_FORMAT_PCM_16_BIT = (AUDIO_FORMAT_PCM | AUDIO_FORMAT_PCM_SUB_16_BIT),
+} AudioTrack_SampleType;
+
diff --git a/dep/cubeb/src/android/cubeb-output-latency.h b/dep/cubeb/src/android/cubeb-output-latency.h
new file mode 100644
index 000000000..2128cd176
--- /dev/null
+++ b/dep/cubeb/src/android/cubeb-output-latency.h
@@ -0,0 +1,76 @@
+#ifndef _CUBEB_OUTPUT_LATENCY_H_
+#define _CUBEB_OUTPUT_LATENCY_H_
+
+#include <stdbool.h>
+#include "cubeb_media_library.h"
+#include "../cubeb-jni.h"
+
+struct output_latency_function {
+  media_lib * from_lib;
+  cubeb_jni * from_jni;
+  int version;
+};
+
+typedef struct output_latency_function output_latency_function;
+
+const int ANDROID_JELLY_BEAN_MR1_4_2 = 17;
+
+output_latency_function *
+cubeb_output_latency_load_method(int version)
+{
+  output_latency_function * ol = NULL;
+  ol = calloc(1, sizeof(output_latency_function));
+
+  ol->version = version;
+
+  if (ol->version > ANDROID_JELLY_BEAN_MR1_4_2){
+    ol->from_jni = cubeb_jni_init();
+    return ol;
+  }
+
+  ol->from_lib = cubeb_load_media_library();
+  return ol;
+}
+
+bool
+cubeb_output_latency_method_is_loaded(output_latency_function * ol)
+{
+  assert(ol);
+  if (ol->version > ANDROID_JELLY_BEAN_MR1_4_2){
+    return !!ol->from_jni;
+  }
+
+  return !!ol->from_lib;
+}
+
+void
+cubeb_output_latency_unload_method(output_latency_function * ol)
+{
+  if (!ol) {
+    return;
+  }
+
+  if (ol->version > ANDROID_JELLY_BEAN_MR1_4_2 && ol->from_jni) {
+    cubeb_jni_destroy(ol->from_jni);
+  }
+
+  if (ol->version <= ANDROID_JELLY_BEAN_MR1_4_2 && ol->from_lib) {
+    cubeb_close_media_library(ol->from_lib);
+  }
+
+  free(ol);
+}
+
+uint32_t
+cubeb_get_output_latency(output_latency_function * ol)
+{
+  assert(cubeb_output_latency_method_is_loaded(ol));
+
+  if (ol->version > ANDROID_JELLY_BEAN_MR1_4_2){
+    return cubeb_get_output_latency_from_jni(ol->from_jni);
+  }
+
+  return cubeb_get_output_latency_from_media_library(ol->from_lib);
+}
+
+#endif // _CUBEB_OUTPUT_LATENCY_H_
diff --git a/dep/cubeb/src/android/cubeb_media_library.h b/dep/cubeb/src/android/cubeb_media_library.h
new file mode 100644
index 000000000..ab21b779d
--- /dev/null
+++ b/dep/cubeb/src/android/cubeb_media_library.h
@@ -0,0 +1,62 @@
+#ifndef _CUBEB_MEDIA_LIBRARY_H_
+#define _CUBEB_MEDIA_LIBRARY_H_
+
+struct media_lib {
+  void * libmedia;
+  int32_t (* get_output_latency)(uint32_t * latency, int stream_type);
+};
+
+typedef struct media_lib media_lib;
+
+media_lib *
+cubeb_load_media_library()
+{
+  media_lib ml = {0};
+  ml.libmedia = dlopen("libmedia.so", RTLD_LAZY);
+  if (!ml.libmedia) {
+    return NULL;
+  }
+
+  // Get the latency, in ms, from AudioFlinger. First, try the most recent signature.
+  // status_t AudioSystem::getOutputLatency(uint32_t* latency, audio_stream_type_t streamType)
+  ml.get_output_latency =
+    dlsym(ml.libmedia, "_ZN7android11AudioSystem16getOutputLatencyEPj19audio_stream_type_t");
+  if (!ml.get_output_latency) {
+    // In case of failure, try the signature from legacy version.
+    // status_t AudioSystem::getOutputLatency(uint32_t* latency, int streamType)
+    ml.get_output_latency =
+      dlsym(ml.libmedia, "_ZN7android11AudioSystem16getOutputLatencyEPji");
+    if (!ml.get_output_latency) {
+      return NULL;
+    }
+  }
+
+  media_lib * rv = NULL;
+  rv = calloc(1, sizeof(media_lib));
+  assert(rv);
+  *rv = ml;
+  return rv;
+}
+
+void
+cubeb_close_media_library(media_lib * ml)
+{
+  dlclose(ml->libmedia);
+  ml->libmedia = NULL;
+  ml->get_output_latency = NULL;
+  free(ml);
+}
+
+uint32_t
+cubeb_get_output_latency_from_media_library(media_lib * ml)
+{
+  uint32_t latency = 0;
+  const int audio_stream_type_music = 3;
+  int32_t r = ml->get_output_latency(&latency, audio_stream_type_music);
+  if (r) {
+    return 0;
+  }
+  return latency;
+}
+
+#endif // _CUBEB_MEDIA_LIBRARY_H_
diff --git a/dep/cubeb/src/android/sles_definitions.h b/dep/cubeb/src/android/sles_definitions.h
new file mode 100644
index 000000000..06d2e8d49
--- /dev/null
+++ b/dep/cubeb/src/android/sles_definitions.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This file is similar to the file "OpenSLES_AndroidConfiguration.h" found in
+ * the Android NDK, but removes the #ifdef __cplusplus defines, so we can keep
+ * using a C compiler in cubeb.
+ */
+
+#ifndef OPENSL_ES_ANDROIDCONFIGURATION_H_
+#define OPENSL_ES_ANDROIDCONFIGURATION_H_
+
+/*---------------------------------------------------------------------------*/
+/* Android AudioRecorder configuration                                       */
+/*---------------------------------------------------------------------------*/
+
+/** Audio recording preset */
+/** Audio recording preset key */
+#define SL_ANDROID_KEY_RECORDING_PRESET ((const SLchar*) "androidRecordingPreset")
+/** Audio recording preset values */
+/**   preset "none" cannot be set, it is used to indicate the current settings
+ *     do not match any of the presets. */
+#define SL_ANDROID_RECORDING_PRESET_NONE                ((SLuint32) 0x00000000)
+/**   generic recording configuration on the platform */
+#define SL_ANDROID_RECORDING_PRESET_GENERIC             ((SLuint32) 0x00000001)
+/**   uses the microphone audio source with the same orientation as the camera
+ *     if available, the main device microphone otherwise */
+#define SL_ANDROID_RECORDING_PRESET_CAMCORDER           ((SLuint32) 0x00000002)
+/**   uses the main microphone tuned for voice recognition */
+#define SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION   ((SLuint32) 0x00000003)
+/**   uses the main microphone tuned for audio communications */
+#define SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION ((SLuint32) 0x00000004)
+/**   uses the main microphone unprocessed */
+#define SL_ANDROID_RECORDING_PRESET_UNPROCESSED         ((SLuint32) 0x00000005)
+
+
+/*---------------------------------------------------------------------------*/
+/* Android AudioPlayer configuration                                         */
+/*---------------------------------------------------------------------------*/
+
+/** Audio playback stream type */
+/** Audio playback stream type key */
+#define SL_ANDROID_KEY_STREAM_TYPE ((const SLchar*) "androidPlaybackStreamType")
+
+/** Audio playback stream type  values */
+/*      same as android.media.AudioManager.STREAM_VOICE_CALL */
+#define SL_ANDROID_STREAM_VOICE        ((SLint32) 0x00000000)
+/*      same as android.media.AudioManager.STREAM_SYSTEM */
+#define SL_ANDROID_STREAM_SYSTEM       ((SLint32) 0x00000001)
+/*      same as android.media.AudioManager.STREAM_RING */
+#define SL_ANDROID_STREAM_RING         ((SLint32) 0x00000002)
+/*      same as android.media.AudioManager.STREAM_MUSIC */
+#define SL_ANDROID_STREAM_MEDIA        ((SLint32) 0x00000003)
+/*      same as android.media.AudioManager.STREAM_ALARM */
+#define SL_ANDROID_STREAM_ALARM        ((SLint32) 0x00000004)
+/*      same as android.media.AudioManager.STREAM_NOTIFICATION */
+#define SL_ANDROID_STREAM_NOTIFICATION ((SLint32) 0x00000005)
+
+
+/*---------------------------------------------------------------------------*/
+/* Android AudioPlayer and AudioRecorder configuration                       */
+/*---------------------------------------------------------------------------*/
+
+/** Audio Performance mode.
+ * Performance mode tells the framework how to configure the audio path
+ * for a player or recorder according to application performance and
+ * functional requirements.
+ * It affects the output or input latency based on acceptable tradeoffs on
+ * battery drain and use of pre or post processing effects.
+ * Performance mode should be set before realizing the object and should be
+ * read after realizing the object to check if the requested mode could be
+ * granted or not.
+ */
+/** Audio Performance mode key */
+#define SL_ANDROID_KEY_PERFORMANCE_MODE ((const SLchar*) "androidPerformanceMode")
+
+/** Audio performance values */
+/*      No specific performance requirement. Allows HW and SW pre/post processing. */
+#define SL_ANDROID_PERFORMANCE_NONE ((SLuint32) 0x00000000)
+/*      Priority given to latency. No HW or software pre/post processing.
+ *      This is the default if no performance mode is specified. */
+#define SL_ANDROID_PERFORMANCE_LATENCY ((SLuint32) 0x00000001)
+/*      Priority given to latency while still allowing HW pre and post processing. */
+#define SL_ANDROID_PERFORMANCE_LATENCY_EFFECTS ((SLuint32) 0x00000002)
+/*      Priority given to power saving if latency is not a concern.
+ *      Allows HW and SW pre/post processing. */
+#define SL_ANDROID_PERFORMANCE_POWER_SAVING ((SLuint32) 0x00000003)
+
+#endif /* OPENSL_ES_ANDROIDCONFIGURATION_H_ */
diff --git a/dep/cubeb/src/cubeb-internal.h b/dep/cubeb/src/cubeb-internal.h
new file mode 100644
index 000000000..312a9ea3a
--- /dev/null
+++ b/dep/cubeb/src/cubeb-internal.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright © 2013 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+#if !defined(CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5)
+#define CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5
+
+#include "cubeb/cubeb.h"
+#include "cubeb_log.h"
+#include "cubeb_assert.h"
+#include <stdio.h>
+#include <string.h>
+
+#ifdef __clang__
+#ifndef CLANG_ANALYZER_NORETURN
+#if __has_feature(attribute_analyzer_noreturn)
+#define CLANG_ANALYZER_NORETURN __attribute__((analyzer_noreturn))
+#else
+#define CLANG_ANALYZER_NORETURN
+#endif // ifndef CLANG_ANALYZER_NORETURN
+#endif // __has_feature(attribute_analyzer_noreturn)
+#else // __clang__
+#define CLANG_ANALYZER_NORETURN
+#endif
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+
+struct cubeb_ops {
+  int (* init)(cubeb ** context, char const * context_name);
+  char const * (* get_backend_id)(cubeb * context);
+  int (* get_max_channel_count)(cubeb * context, uint32_t * max_channels);
+  int (* get_min_latency)(cubeb * context,
+                          cubeb_stream_params params,
+                          uint32_t * latency_ms);
+  int (* get_preferred_sample_rate)(cubeb * context, uint32_t * rate);
+  int (* enumerate_devices)(cubeb * context, cubeb_device_type type,
+                            cubeb_device_collection * collection);
+  int (* device_collection_destroy)(cubeb * context,
+                                    cubeb_device_collection * collection);
+  void (* destroy)(cubeb * context);
+  int (* stream_init)(cubeb * context,
+                      cubeb_stream ** stream,
+                      char const * stream_name,
+                      cubeb_devid input_device,
+                      cubeb_stream_params * input_stream_params,
+                      cubeb_devid output_device,
+                      cubeb_stream_params * output_stream_params,
+                      unsigned int latency,
+                      cubeb_data_callback data_callback,
+                      cubeb_state_callback state_callback,
+                      void * user_ptr);
+  void (* stream_destroy)(cubeb_stream * stream);
+  int (* stream_start)(cubeb_stream * stream);
+  int (* stream_stop)(cubeb_stream * stream);
+  int (* stream_reset_default_device)(cubeb_stream * stream);
+  int (* stream_get_position)(cubeb_stream * stream, uint64_t * position);
+  int (* stream_get_latency)(cubeb_stream * stream, uint32_t * latency);
+  int (* stream_set_volume)(cubeb_stream * stream, float volumes);
+  int (* stream_get_current_device)(cubeb_stream * stream,
+                                    cubeb_device ** const device);
+  int (* stream_device_destroy)(cubeb_stream * stream,
+                                cubeb_device * device);
+  int (* stream_register_device_changed_callback)(cubeb_stream * stream,
+                                                  cubeb_device_changed_callback device_changed_callback);
+  int (* register_device_collection_changed)(cubeb * context,
+                                             cubeb_device_type devtype,
+                                             cubeb_device_collection_changed_callback callback,
+                                             void * user_ptr);
+};
+
+#endif /* CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5 */
diff --git a/dep/cubeb/src/cubeb-jni-instances.h b/dep/cubeb/src/cubeb-jni-instances.h
new file mode 100644
index 000000000..4fed04659
--- /dev/null
+++ b/dep/cubeb/src/cubeb-jni-instances.h
@@ -0,0 +1,30 @@
+#ifndef _CUBEB_JNI_INSTANCES_H_
+#define _CUBEB_JNI_INSTANCES_H_
+
+/*
+ * The methods in this file offer a way to pass in the required
+ * JNI instances in the cubeb library. By default they return NULL.
+ * In this case part of the cubeb API that depends on JNI
+ * will return CUBEB_ERROR_NOT_SUPPORTED. Currently only one
+ * method depends on that:
+ *
+ * cubeb_stream_get_position()
+ *
+ * Users that want to use that cubeb API method must "override"
+ * the methods bellow to return a valid instance of JavaVM
+ * and application's Context object.
+ * */
+
+JNIEnv *
+cubeb_get_jni_env_for_thread()
+{
+  return nullptr;
+}
+
+jobject
+cubeb_jni_get_context_instance()
+{
+  return nullptr;
+}
+
+#endif //_CUBEB_JNI_INSTANCES_H_
diff --git a/dep/cubeb/src/cubeb-jni.cpp b/dep/cubeb/src/cubeb-jni.cpp
new file mode 100644
index 000000000..a5066967a
--- /dev/null
+++ b/dep/cubeb/src/cubeb-jni.cpp
@@ -0,0 +1,68 @@
+#include "jni.h"
+#include <assert.h>
+#include "cubeb-jni-instances.h"
+
+#define AUDIO_STREAM_TYPE_MUSIC 3
+
+struct cubeb_jni {
+  jobject s_audio_manager_obj = nullptr;
+  jclass s_audio_manager_class = nullptr;
+  jmethodID s_get_output_latency_id = nullptr;
+};
+
+extern "C"
+cubeb_jni *
+cubeb_jni_init()
+{
+  jobject ctx_obj = cubeb_jni_get_context_instance();
+  JNIEnv * jni_env = cubeb_get_jni_env_for_thread();
+  if (!jni_env || !ctx_obj) {
+    return nullptr;
+  }
+
+  cubeb_jni * cubeb_jni_ptr = new cubeb_jni;
+  assert(cubeb_jni_ptr);
+
+  // Find the audio manager object and make it global to call it from another method
+  jclass context_class = jni_env->FindClass("android/content/Context");
+  jfieldID audio_service_field = jni_env->GetStaticFieldID(context_class, "AUDIO_SERVICE", "Ljava/lang/String;");
+  jstring jstr = (jstring)jni_env->GetStaticObjectField(context_class, audio_service_field);
+  jmethodID get_system_service_id = jni_env->GetMethodID(context_class, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
+  jobject audio_manager_obj = jni_env->CallObjectMethod(ctx_obj, get_system_service_id, jstr);
+  cubeb_jni_ptr->s_audio_manager_obj = reinterpret_cast<jobject>(jni_env->NewGlobalRef(audio_manager_obj));
+
+  // Make the audio manager class a global reference in order to preserve method id
+  jclass audio_manager_class = jni_env->FindClass("android/media/AudioManager");
+  cubeb_jni_ptr->s_audio_manager_class = reinterpret_cast<jclass>(jni_env->NewGlobalRef(audio_manager_class));
+  cubeb_jni_ptr->s_get_output_latency_id = jni_env->GetMethodID (audio_manager_class, "getOutputLatency", "(I)I");
+
+  jni_env->DeleteLocalRef(ctx_obj);
+  jni_env->DeleteLocalRef(context_class);
+  jni_env->DeleteLocalRef(jstr);
+  jni_env->DeleteLocalRef(audio_manager_obj);
+  jni_env->DeleteLocalRef(audio_manager_class);
+
+  return cubeb_jni_ptr;
+}
+
+extern "C"
+int cubeb_get_output_latency_from_jni(cubeb_jni * cubeb_jni_ptr)
+{
+  assert(cubeb_jni_ptr);
+  JNIEnv * jni_env = cubeb_get_jni_env_for_thread();
+  return jni_env->CallIntMethod(cubeb_jni_ptr->s_audio_manager_obj, cubeb_jni_ptr->s_get_output_latency_id, AUDIO_STREAM_TYPE_MUSIC); //param: AudioManager.STREAM_MUSIC
+}
+
+extern "C"
+void cubeb_jni_destroy(cubeb_jni * cubeb_jni_ptr)
+{
+  assert(cubeb_jni_ptr);
+
+  JNIEnv * jni_env = cubeb_get_jni_env_for_thread();
+  assert(jni_env);
+
+  jni_env->DeleteGlobalRef(cubeb_jni_ptr->s_audio_manager_obj);
+  jni_env->DeleteGlobalRef(cubeb_jni_ptr->s_audio_manager_class);
+
+  delete cubeb_jni_ptr;
+}
diff --git a/dep/cubeb/src/cubeb-jni.h b/dep/cubeb/src/cubeb-jni.h
new file mode 100644
index 000000000..8c7ddb6ac
--- /dev/null
+++ b/dep/cubeb/src/cubeb-jni.h
@@ -0,0 +1,10 @@
+#ifndef _CUBEB_JNI_H_
+#define _CUBEB_JNI_H_
+
+typedef struct cubeb_jni cubeb_jni;
+
+cubeb_jni * cubeb_jni_init();
+int cubeb_get_output_latency_from_jni(cubeb_jni * cubeb_jni_ptr);
+void cubeb_jni_destroy(cubeb_jni * cubeb_jni_ptr);
+
+#endif // _CUBEB_JNI_H_
diff --git a/dep/cubeb/src/cubeb-sles.h b/dep/cubeb/src/cubeb-sles.h
new file mode 100644
index 000000000..ac22150e1
--- /dev/null
+++ b/dep/cubeb/src/cubeb-sles.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright © 2016 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+
+#ifndef _CUBEB_SLES_H_
+#define _CUBEB_SLES_H_
+#include <SLES/OpenSLES.h>
+
+static SLresult
+cubeb_get_sles_engine(SLObjectItf * pEngine,
+                      SLuint32 numOptions,
+                      const SLEngineOption * pEngineOptions,
+                      SLuint32 numInterfaces,
+                      const SLInterfaceID * pInterfaceIds,
+                      const SLboolean * pInterfaceRequired)
+{
+  return slCreateEngine(pEngine,
+                        numOptions,
+                        pEngineOptions,
+                        numInterfaces,
+                        pInterfaceIds,
+                        pInterfaceRequired);
+}
+
+static void
+cubeb_destroy_sles_engine(SLObjectItf * self)
+{
+  if (*self != NULL) {
+      (**self)->Destroy(*self);
+      *self = NULL;
+  }
+}
+
+static SLresult
+cubeb_realize_sles_engine(SLObjectItf self)
+{
+  return (*self)->Realize(self, SL_BOOLEAN_FALSE);
+}
+
+#endif
diff --git a/dep/cubeb/src/cubeb-speex-resampler.h b/dep/cubeb/src/cubeb-speex-resampler.h
new file mode 100644
index 000000000..9ecf747cb
--- /dev/null
+++ b/dep/cubeb/src/cubeb-speex-resampler.h
@@ -0,0 +1 @@
+#include <speex/speex_resampler.h>
diff --git a/dep/cubeb/src/cubeb.c b/dep/cubeb/src/cubeb.c
new file mode 100644
index 000000000..a43d012a4
--- /dev/null
+++ b/dep/cubeb/src/cubeb.c
@@ -0,0 +1,665 @@
+/*
+ * Copyright © 2013 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+#undef NDEBUG
+#include <assert.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include "cubeb/cubeb.h"
+#include "cubeb-internal.h"
+
+#define NELEMS(x) ((int) (sizeof(x) / sizeof(x[0])))
+
+struct cubeb {
+  struct cubeb_ops * ops;
+};
+
+struct cubeb_stream {
+  /*
+   * Note: All implementations of cubeb_stream must keep the following
+   * layout.
+   */
+  struct cubeb * context;
+  void * user_ptr;
+};
+
+#if defined(USE_PULSE)
+int pulse_init(cubeb ** context, char const * context_name);
+#endif
+#if defined(USE_PULSE_RUST)
+int pulse_rust_init(cubeb ** contet, char const * context_name);
+#endif
+#if defined(USE_JACK)
+int jack_init (cubeb ** context, char const * context_name);
+#endif
+#if defined(USE_ALSA)
+int alsa_init(cubeb ** context, char const * context_name);
+#endif
+#if defined(USE_AUDIOUNIT)
+int audiounit_init(cubeb ** context, char const * context_name);
+#endif
+#if defined(USE_AUDIOUNIT_RUST)
+int audiounit_rust_init(cubeb ** contet, char const * context_name);
+#endif
+#if defined(USE_WINMM)
+int winmm_init(cubeb ** context, char const * context_name);
+#endif
+#if defined(USE_WASAPI)
+int wasapi_init(cubeb ** context, char const * context_name);
+#endif
+#if defined(USE_SNDIO)
+int sndio_init(cubeb ** context, char const * context_name);
+#endif
+#if defined(USE_SUN)
+int sun_init(cubeb ** context, char const * context_name);
+#endif
+#if defined(USE_OPENSL)
+int opensl_init(cubeb ** context, char const * context_name);
+#endif
+#if defined(USE_AUDIOTRACK)
+int audiotrack_init(cubeb ** context, char const * context_name);
+#endif
+#if defined(USE_KAI)
+int kai_init(cubeb ** context, char const * context_name);
+#endif
+
+static int
+validate_stream_params(cubeb_stream_params * input_stream_params,
+                       cubeb_stream_params * output_stream_params)
+{
+  XASSERT(input_stream_params || output_stream_params);
+  if (output_stream_params) {
+    if (output_stream_params->rate < 1000 || output_stream_params->rate > 192000 ||
+        output_stream_params->channels < 1 || output_stream_params->channels > UINT8_MAX) {
+      return CUBEB_ERROR_INVALID_FORMAT;
+    }
+  }
+  if (input_stream_params) {
+    if (input_stream_params->rate < 1000 || input_stream_params->rate > 192000 ||
+        input_stream_params->channels < 1 || input_stream_params->channels > 8) {
+      return CUBEB_ERROR_INVALID_FORMAT;
+    }
+  }
+  // Rate and sample format must be the same for input and output, if using a
+  // duplex stream
+  if (input_stream_params && output_stream_params) {
+    if (input_stream_params->rate != output_stream_params->rate  ||
+        input_stream_params->format != output_stream_params->format) {
+      return CUBEB_ERROR_INVALID_FORMAT;
+    }
+  }
+
+  cubeb_stream_params * params = input_stream_params ?
+                                 input_stream_params : output_stream_params;
+
+  switch (params->format) {
+  case CUBEB_SAMPLE_S16LE:
+  case CUBEB_SAMPLE_S16BE:
+  case CUBEB_SAMPLE_FLOAT32LE:
+  case CUBEB_SAMPLE_FLOAT32BE:
+    return CUBEB_OK;
+  }
+
+  return CUBEB_ERROR_INVALID_FORMAT;
+}
+
+static int
+validate_latency(int latency)
+{
+  if (latency < 1 || latency > 96000) {
+    return CUBEB_ERROR_INVALID_PARAMETER;
+  }
+  return CUBEB_OK;
+}
+
+int
+cubeb_init(cubeb ** context, char const * context_name, char const * backend_name)
+{
+  int (* init_oneshot)(cubeb **, char const *) = NULL;
+
+  if (backend_name != NULL) {
+    if (!strcmp(backend_name, "pulse")) {
+#if defined(USE_PULSE)
+      init_oneshot = pulse_init;
+#endif
+    } else if (!strcmp(backend_name, "pulse-rust")) {
+#if defined(USE_PULSE_RUST)
+      init_oneshot = pulse_rust_init;
+#endif
+    } else if (!strcmp(backend_name, "jack")) {
+#if defined(USE_JACK)
+      init_oneshot = jack_init;
+#endif
+    } else if (!strcmp(backend_name, "alsa")) {
+#if defined(USE_ALSA)
+      init_oneshot = alsa_init;
+#endif
+    } else if (!strcmp(backend_name, "audiounit")) {
+#if defined(USE_AUDIOUNIT)
+      init_oneshot = audiounit_init;
+#endif
+    } else if (!strcmp(backend_name, "audiounit-rust")) {
+#if defined(USE_AUDIOUNIT_RUST)
+      init_oneshot = audiounit_rust_init;
+#endif
+    } else if (!strcmp(backend_name, "wasapi")) {
+#if defined(USE_WASAPI)
+      init_oneshot = wasapi_init;
+#endif
+    } else if (!strcmp(backend_name, "winmm")) {
+#if defined(USE_WINMM)
+      init_oneshot = winmm_init;
+#endif
+    } else if (!strcmp(backend_name, "sndio")) {
+#if defined(USE_SNDIO)
+      init_oneshot = sndio_init;
+#endif
+    } else if (!strcmp(backend_name, "sun")) {
+#if defined(USE_SUN)
+      init_oneshot = sun_init;
+#endif
+    } else if (!strcmp(backend_name, "opensl")) {
+#if defined(USE_OPENSL)
+      init_oneshot = opensl_init;
+#endif
+    } else if (!strcmp(backend_name, "audiotrack")) {
+#if defined(USE_AUDIOTRACK)
+      init_oneshot = audiotrack_init;
+#endif
+    } else if (!strcmp(backend_name, "kai")) {
+#if defined(USE_KAI)
+      init_oneshot = kai_init;
+#endif
+    } else {
+      /* Already set */
+    }
+  }
+
+  int (* default_init[])(cubeb **, char const *) = {
+    /*
+     * init_oneshot must be at the top to allow user
+     * to override all other choices
+     */
+    init_oneshot,
+#if defined(USE_PULSE_RUST)
+    pulse_rust_init,
+#endif
+#if defined(USE_PULSE)
+    pulse_init,
+#endif
+#if defined(USE_JACK)
+    jack_init,
+#endif
+#if defined(USE_SNDIO)
+    sndio_init,
+#endif
+#if defined(USE_ALSA)
+    alsa_init,
+#endif
+#if defined(USE_AUDIOUNIT)
+    audiounit_init,
+#endif
+#if defined(USE_AUDIOUNIT_RUST)
+    audiounit_rust_init,
+#endif
+#if defined(USE_WASAPI)
+    wasapi_init,
+#endif
+#if defined(USE_WINMM)
+    winmm_init,
+#endif
+#if defined(USE_SUN)
+    sun_init,
+#endif
+#if defined(USE_OPENSL)
+    opensl_init,
+#endif
+#if defined(USE_AUDIOTRACK)
+    audiotrack_init,
+#endif
+#if defined(USE_KAI)
+    kai_init,
+#endif
+  };
+  int i;
+
+  if (!context) {
+    return CUBEB_ERROR_INVALID_PARAMETER;
+  }
+
+#define OK(fn) assert((* context)->ops->fn)
+  for (i = 0; i < NELEMS(default_init); ++i) {
+    if (default_init[i] && default_init[i](context, context_name) == CUBEB_OK) {
+      /* Assert that the minimal API is implemented. */
+      OK(get_backend_id);
+      OK(destroy);
+      OK(stream_init);
+      OK(stream_destroy);
+      OK(stream_start);
+      OK(stream_stop);
+      OK(stream_get_position);
+      return CUBEB_OK;
+    }
+  }
+  return CUBEB_ERROR;
+}
+
+char const *
+cubeb_get_backend_id(cubeb * context)
+{
+  if (!context) {
+    return NULL;
+  }
+
+  return context->ops->get_backend_id(context);
+}
+
+int
+cubeb_get_max_channel_count(cubeb * context, uint32_t * max_channels)
+{
+  if (!context || !max_channels) {
+    return CUBEB_ERROR_INVALID_PARAMETER;
+  }
+
+  if (!context->ops->get_max_channel_count) {
+    return CUBEB_ERROR_NOT_SUPPORTED;
+  }
+
+  return context->ops->get_max_channel_count(context, max_channels);
+}
+
+int
+cubeb_get_min_latency(cubeb * context, cubeb_stream_params * params, uint32_t * latency_ms)
+{
+  if (!context || !params || !latency_ms) {
+    return CUBEB_ERROR_INVALID_PARAMETER;
+  }
+
+  if (!context->ops->get_min_latency) {
+    return CUBEB_ERROR_NOT_SUPPORTED;
+  }
+
+  return context->ops->get_min_latency(context, *params, latency_ms);
+}
+
+int
+cubeb_get_preferred_sample_rate(cubeb * context, uint32_t * rate)
+{
+  if (!context || !rate) {
+    return CUBEB_ERROR_INVALID_PARAMETER;
+  }
+
+  if (!context->ops->get_preferred_sample_rate) {
+    return CUBEB_ERROR_NOT_SUPPORTED;
+  }
+
+  return context->ops->get_preferred_sample_rate(context, rate);
+}
+
+void
+cubeb_destroy(cubeb * context)
+{
+  if (!context) {
+    return;
+  }
+
+  context->ops->destroy(context);
+}
+
+int
+cubeb_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
+                  cubeb_devid input_device,
+                  cubeb_stream_params * input_stream_params,
+                  cubeb_devid output_device,
+                  cubeb_stream_params * output_stream_params,
+                  unsigned int latency,
+                  cubeb_data_callback data_callback,
+                  cubeb_state_callback state_callback,
+                  void * user_ptr)
+{
+  int r;
+
+  if (!context || !stream || !data_callback || !state_callback) {
+    return CUBEB_ERROR_INVALID_PARAMETER;
+  }
+
+  if ((r = validate_stream_params(input_stream_params, output_stream_params)) != CUBEB_OK ||
+      (r = validate_latency(latency)) != CUBEB_OK) {
+    return r;
+  }
+
+  r = context->ops->stream_init(context, stream, stream_name,
+                                input_device,
+                                input_stream_params,
+                                output_device,
+                                output_stream_params,
+                                latency,
+                                data_callback,
+                                state_callback,
+                                user_ptr);
+
+  if (r == CUBEB_ERROR_INVALID_FORMAT) {
+    LOG("Invalid format, %p %p %d %d",
+        output_stream_params, input_stream_params,
+        output_stream_params && output_stream_params->format,
+        input_stream_params && input_stream_params->format);
+  }
+
+  return r;
+}
+
+void
+cubeb_stream_destroy(cubeb_stream * stream)
+{
+  if (!stream) {
+    return;
+  }
+
+  stream->context->ops->stream_destroy(stream);
+}
+
+int
+cubeb_stream_start(cubeb_stream * stream)
+{
+  if (!stream) {
+    return CUBEB_ERROR_INVALID_PARAMETER;
+  }
+
+  return stream->context->ops->stream_start(stream);
+}
+
+int
+cubeb_stream_stop(cubeb_stream * stream)
+{
+  if (!stream) {
+    return CUBEB_ERROR_INVALID_PARAMETER;
+  }
+
+  return stream->context->ops->stream_stop(stream);
+}
+
+int
+cubeb_stream_reset_default_device(cubeb_stream * stream)
+{
+  if (!stream) {
+    return CUBEB_ERROR_INVALID_PARAMETER;
+  }
+
+  if (!stream->context->ops->stream_reset_default_device) {
+    return CUBEB_ERROR_NOT_SUPPORTED;
+  }
+
+  return stream->context->ops->stream_reset_default_device(stream);
+}
+
+int
+cubeb_stream_get_position(cubeb_stream * stream, uint64_t * position)
+{
+  if (!stream || !position) {
+    return CUBEB_ERROR_INVALID_PARAMETER;
+  }
+
+  return stream->context->ops->stream_get_position(stream, position);
+}
+
+int
+cubeb_stream_get_latency(cubeb_stream * stream, uint32_t * latency)
+{
+  if (!stream || !latency) {
+    return CUBEB_ERROR_INVALID_PARAMETER;
+  }
+
+  if (!stream->context->ops->stream_get_latency) {
+    return CUBEB_ERROR_NOT_SUPPORTED;
+  }
+
+  return stream->context->ops->stream_get_latency(stream, latency);
+}
+
+int
+cubeb_stream_set_volume(cubeb_stream * stream, float volume)
+{
+  if (!stream || volume > 1.0 || volume < 0.0) {
+    return CUBEB_ERROR_INVALID_PARAMETER;
+  }
+
+  if (!stream->context->ops->stream_set_volume) {
+    return CUBEB_ERROR_NOT_SUPPORTED;
+  }
+
+  return stream->context->ops->stream_set_volume(stream, volume);
+}
+
+int cubeb_stream_get_current_device(cubeb_stream * stream,
+                                    cubeb_device ** const device)
+{
+  if (!stream || !device) {
+    return CUBEB_ERROR_INVALID_PARAMETER;
+  }
+
+  if (!stream->context->ops->stream_get_current_device) {
+    return CUBEB_ERROR_NOT_SUPPORTED;
+  }
+
+  return stream->context->ops->stream_get_current_device(stream, device);
+}
+
+int cubeb_stream_device_destroy(cubeb_stream * stream,
+                                cubeb_device * device)
+{
+  if (!stream || !device) {
+    return CUBEB_ERROR_INVALID_PARAMETER;
+  }
+
+  if (!stream->context->ops->stream_device_destroy) {
+    return CUBEB_ERROR_NOT_SUPPORTED;
+  }
+
+  return stream->context->ops->stream_device_destroy(stream, device);
+}
+
+int cubeb_stream_register_device_changed_callback(cubeb_stream * stream,
+                                                  cubeb_device_changed_callback device_changed_callback)
+{
+  if (!stream) {
+    return CUBEB_ERROR_INVALID_PARAMETER;
+  }
+
+  if (!stream->context->ops->stream_register_device_changed_callback) {
+    return CUBEB_ERROR_NOT_SUPPORTED;
+  }
+
+  return stream->context->ops->stream_register_device_changed_callback(stream, device_changed_callback);
+}
+
+void * cubeb_stream_user_ptr(cubeb_stream * stream)
+{
+  if (!stream) {
+    return NULL;
+  }
+
+  return stream->user_ptr;
+}
+
+static
+void log_device(cubeb_device_info * device_info)
+{
+  char devfmts[128] = "";
+  const char * devtype, * devstate, * devdeffmt;
+
+  switch (device_info->type) {
+    case CUBEB_DEVICE_TYPE_INPUT:
+      devtype = "input";
+      break;
+    case CUBEB_DEVICE_TYPE_OUTPUT:
+      devtype = "output";
+      break;
+    case CUBEB_DEVICE_TYPE_UNKNOWN:
+    default:
+      devtype = "unknown?";
+      break;
+  };
+
+  switch (device_info->state) {
+    case CUBEB_DEVICE_STATE_DISABLED:
+      devstate = "disabled";
+      break;
+    case CUBEB_DEVICE_STATE_UNPLUGGED:
+      devstate = "unplugged";
+      break;
+    case CUBEB_DEVICE_STATE_ENABLED:
+      devstate = "enabled";
+      break;
+    default:
+      devstate = "unknown?";
+      break;
+  };
+
+  switch (device_info->default_format) {
+    case CUBEB_DEVICE_FMT_S16LE:
+      devdeffmt = "S16LE";
+      break;
+    case CUBEB_DEVICE_FMT_S16BE:
+      devdeffmt = "S16BE";
+      break;
+    case CUBEB_DEVICE_FMT_F32LE:
+      devdeffmt = "F32LE";
+      break;
+    case CUBEB_DEVICE_FMT_F32BE:
+      devdeffmt = "F32BE";
+      break;
+    default:
+      devdeffmt = "unknown?";
+      break;
+  };
+
+  if (device_info->format & CUBEB_DEVICE_FMT_S16LE) {
+    strcat(devfmts, " S16LE");
+  }
+  if (device_info->format & CUBEB_DEVICE_FMT_S16BE) {
+    strcat(devfmts, " S16BE");
+  }
+  if (device_info->format & CUBEB_DEVICE_FMT_F32LE) {
+    strcat(devfmts, " F32LE");
+  }
+  if (device_info->format & CUBEB_DEVICE_FMT_F32BE) {
+    strcat(devfmts, " F32BE");
+  }
+
+  LOG("DeviceID: \"%s\"%s\n"
+      "\tName:\t\"%s\"\n"
+      "\tGroup:\t\"%s\"\n"
+      "\tVendor:\t\"%s\"\n"
+      "\tType:\t%s\n"
+      "\tState:\t%s\n"
+      "\tMaximum channels:\t%u\n"
+      "\tFormat:\t%s (0x%x) (default: %s)\n"
+      "\tRate:\t[%u, %u] (default: %u)\n"
+      "\tLatency: lo %u frames, hi %u frames",
+      device_info->device_id, device_info->preferred ? " (PREFERRED)" : "",
+      device_info->friendly_name,
+      device_info->group_id,
+      device_info->vendor_name,
+      devtype,
+      devstate,
+      device_info->max_channels,
+      (devfmts[0] == '\0') ? devfmts : devfmts + 1, (unsigned int)device_info->format, devdeffmt,
+      device_info->min_rate, device_info->max_rate, device_info->default_rate,
+      device_info->latency_lo, device_info->latency_hi);
+}
+
+int cubeb_enumerate_devices(cubeb * context,
+                            cubeb_device_type devtype,
+                            cubeb_device_collection * collection)
+{
+  int rv;
+  if ((devtype & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) == 0)
+    return CUBEB_ERROR_INVALID_PARAMETER;
+  if (collection == NULL)
+    return CUBEB_ERROR_INVALID_PARAMETER;
+  if (!context->ops->enumerate_devices)
+    return CUBEB_ERROR_NOT_SUPPORTED;
+
+  rv = context->ops->enumerate_devices(context, devtype, collection);
+
+  if (g_cubeb_log_callback) {
+    for (size_t i = 0; i < collection->count; i++) {
+      log_device(&collection->device[i]);
+    }
+  }
+
+  return rv;
+}
+
+int cubeb_device_collection_destroy(cubeb * context,
+                                    cubeb_device_collection * collection)
+{
+  int r;
+
+  if (context == NULL || collection == NULL)
+    return CUBEB_ERROR_INVALID_PARAMETER;
+
+  if (!context->ops->device_collection_destroy)
+    return CUBEB_ERROR_NOT_SUPPORTED;
+
+  if (!collection->device)
+    return CUBEB_OK;
+
+  r = context->ops->device_collection_destroy(context, collection);
+  if (r == CUBEB_OK) {
+    collection->device = NULL;
+    collection->count = 0;
+  }
+
+  return r;
+}
+
+int cubeb_register_device_collection_changed(cubeb * context,
+                                             cubeb_device_type devtype,
+                                             cubeb_device_collection_changed_callback callback,
+                                             void * user_ptr)
+{
+  if (context == NULL || (devtype & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) == 0)
+    return CUBEB_ERROR_INVALID_PARAMETER;
+
+  if (!context->ops->register_device_collection_changed) {
+    return CUBEB_ERROR_NOT_SUPPORTED;
+  }
+
+  return context->ops->register_device_collection_changed(context, devtype, callback, user_ptr);
+}
+
+int cubeb_set_log_callback(cubeb_log_level log_level,
+                           cubeb_log_callback log_callback)
+{
+  if (log_level < CUBEB_LOG_DISABLED || log_level > CUBEB_LOG_VERBOSE) {
+    return CUBEB_ERROR_INVALID_FORMAT;
+  }
+
+  if (!log_callback && log_level != CUBEB_LOG_DISABLED) {
+    return CUBEB_ERROR_INVALID_PARAMETER;
+  }
+
+  if (g_cubeb_log_callback && log_callback) {
+    return CUBEB_ERROR_NOT_SUPPORTED;
+  }
+
+  g_cubeb_log_callback = log_callback;
+  g_cubeb_log_level = log_level;
+
+  // Logging a message here allows to initialize the asynchronous logger from a
+  // thread that is not the audio rendering thread, and especially to not
+  // initialize it the first time we find a verbose log, which is often in the
+  // audio rendering callback, that runs from the audio rendering thread, and
+  // that is high priority, and that we don't want to block.
+  if (log_level >= CUBEB_LOG_VERBOSE) {
+    ALOGV("Starting cubeb log");
+  }
+
+  return CUBEB_OK;
+}
+
diff --git a/dep/cubeb/src/cubeb_alsa.c b/dep/cubeb/src/cubeb_alsa.c
new file mode 100644
index 000000000..a564fbfc6
--- /dev/null
+++ b/dep/cubeb/src/cubeb_alsa.c
@@ -0,0 +1,1452 @@
+/*
+ * Copyright © 2011 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+#undef NDEBUG
+#define _DEFAULT_SOURCE
+#define _BSD_SOURCE
+#define _XOPEN_SOURCE 500
+#include <pthread.h>
+#include <sys/time.h>
+#include <assert.h>
+#include <limits.h>
+#include <poll.h>
+#include <unistd.h>
+#include <dlfcn.h>
+#include <alsa/asoundlib.h>
+#include "cubeb/cubeb.h"
+#include "cubeb-internal.h"
+
+#ifdef DISABLE_LIBASOUND_DLOPEN
+#define WRAP(x) x
+#else
+#define WRAP(x) cubeb_##x
+#define LIBASOUND_API_VISIT(X)                   \
+  X(snd_config)                                  \
+  X(snd_config_add)                              \
+  X(snd_config_copy)                             \
+  X(snd_config_delete)                           \
+  X(snd_config_get_id)                           \
+  X(snd_config_get_string)                       \
+  X(snd_config_imake_integer)                    \
+  X(snd_config_search)                           \
+  X(snd_config_search_definition)                \
+  X(snd_lib_error_set_handler)                   \
+  X(snd_pcm_avail_update)                        \
+  X(snd_pcm_close)                               \
+  X(snd_pcm_delay)                               \
+  X(snd_pcm_drain)                               \
+  X(snd_pcm_frames_to_bytes)                     \
+  X(snd_pcm_get_params)                          \
+  X(snd_pcm_hw_params_any)                       \
+  X(snd_pcm_hw_params_get_channels_max)          \
+  X(snd_pcm_hw_params_get_rate)                  \
+  X(snd_pcm_hw_params_set_rate_near)             \
+  X(snd_pcm_hw_params_sizeof)                    \
+  X(snd_pcm_nonblock)                            \
+  X(snd_pcm_open)                                \
+  X(snd_pcm_open_lconf)                          \
+  X(snd_pcm_pause)                               \
+  X(snd_pcm_poll_descriptors)                    \
+  X(snd_pcm_poll_descriptors_count)              \
+  X(snd_pcm_poll_descriptors_revents)            \
+  X(snd_pcm_readi)                               \
+  X(snd_pcm_recover)                             \
+  X(snd_pcm_set_params)                          \
+  X(snd_pcm_start)                               \
+  X(snd_pcm_state)                               \
+  X(snd_pcm_writei)                              \
+
+#define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x;
+LIBASOUND_API_VISIT(MAKE_TYPEDEF);
+#undef MAKE_TYPEDEF
+/* snd_pcm_hw_params_alloca is actually a macro */
+#define snd_pcm_hw_params_sizeof cubeb_snd_pcm_hw_params_sizeof
+#endif
+
+#define CUBEB_STREAM_MAX 16
+#define CUBEB_WATCHDOG_MS 10000
+
+#define CUBEB_ALSA_PCM_NAME "default"
+
+#define ALSA_PA_PLUGIN "ALSA <-> PulseAudio PCM I/O Plugin"
+
+/* ALSA is not thread-safe.  snd_pcm_t instances are individually protected
+   by the owning cubeb_stream's mutex.  snd_pcm_t creation and destruction
+   is not thread-safe until ALSA 1.0.24 (see alsa-lib.git commit 91c9c8f1),
+   so those calls must be wrapped in the following mutex. */
+static pthread_mutex_t cubeb_alsa_mutex = PTHREAD_MUTEX_INITIALIZER;
+static int cubeb_alsa_error_handler_set = 0;
+
+static struct cubeb_ops const alsa_ops;
+
+struct cubeb {
+  struct cubeb_ops const * ops;
+  void * libasound;
+
+  pthread_t thread;
+
+  /* Mutex for streams array, must not be held while blocked in poll(2). */
+  pthread_mutex_t mutex;
+
+  /* Sparse array of streams managed by this context. */
+  cubeb_stream * streams[CUBEB_STREAM_MAX];
+
+  /* fds and nfds are only updated by alsa_run when rebuild is set. */
+  struct pollfd * fds;
+  nfds_t nfds;
+  int rebuild;
+
+  int shutdown;
+
+  /* Control pipe for forcing poll to wake and rebuild fds or recalculate the timeout. */
+  int control_fd_read;
+  int control_fd_write;
+
+  /* Track number of active streams.  This is limited to CUBEB_STREAM_MAX
+     due to resource contraints. */
+  unsigned int active_streams;
+
+  /* Local configuration with handle_underrun workaround set for PulseAudio
+     ALSA plugin.  Will be NULL if the PA ALSA plugin is not in use or the
+     workaround is not required. */
+  snd_config_t * local_config;
+  int is_pa;
+};
+
+enum stream_state {
+  INACTIVE,
+  RUNNING,
+  DRAINING,
+  PROCESSING,
+  ERROR
+};
+
+struct cubeb_stream {
+  /* Note: Must match cubeb_stream layout in cubeb.c. */
+  cubeb * context;
+  void * user_ptr;
+  /**/
+  pthread_mutex_t mutex;
+  snd_pcm_t * pcm;
+  cubeb_data_callback data_callback;
+  cubeb_state_callback state_callback;
+  snd_pcm_uframes_t stream_position;
+  snd_pcm_uframes_t last_position;
+  snd_pcm_uframes_t buffer_size;
+  cubeb_stream_params params;
+
+  /* Every member after this comment is protected by the owning context's
+     mutex rather than the stream's mutex, or is only used on the context's
+     run thread. */
+  pthread_cond_t cond; /* Signaled when the stream's state is changed. */
+
+  enum stream_state state;
+
+  struct pollfd * saved_fds; /* A copy of the pollfds passed in at init time. */
+  struct pollfd * fds; /* Pointer to this waitable's pollfds within struct cubeb's fds. */
+  nfds_t nfds;
+
+  struct timeval drain_timeout;
+
+  /* XXX: Horrible hack -- if an active stream has been idle for
+     CUBEB_WATCHDOG_MS it will be disabled and the error callback will be
+     called.  This works around a bug seen with older versions of ALSA and
+     PulseAudio where streams would stop requesting new data despite still
+     being logically active and playing. */
+  struct timeval last_activity;
+  float volume;
+
+  char * buffer;
+  snd_pcm_uframes_t bufframes;
+  snd_pcm_stream_t stream_type;
+
+  struct cubeb_stream * other_stream;
+};
+
+static int
+any_revents(struct pollfd * fds, nfds_t nfds)
+{
+  nfds_t i;
+
+  for (i = 0; i < nfds; ++i) {
+    if (fds[i].revents) {
+      return 1;
+    }
+  }
+
+  return 0;
+}
+
+static int
+cmp_timeval(struct timeval * a, struct timeval * b)
+{
+  if (a->tv_sec == b->tv_sec) {
+    if (a->tv_usec == b->tv_usec) {
+      return 0;
+    }
+    return a->tv_usec > b->tv_usec ? 1 : -1;
+  }
+  return a->tv_sec > b->tv_sec ? 1 : -1;
+}
+
+static int
+timeval_to_relative_ms(struct timeval * tv)
+{
+  struct timeval now;
+  struct timeval dt;
+  long long t;
+  int r;
+
+  gettimeofday(&now, NULL);
+  r = cmp_timeval(tv, &now);
+  if (r >= 0) {
+    timersub(tv, &now, &dt);
+  } else {
+    timersub(&now, tv, &dt);
+  }
+  t = dt.tv_sec;
+  t *= 1000;
+  t += (dt.tv_usec + 500) / 1000;
+
+  if (t > INT_MAX) {
+    t = INT_MAX;
+  } else if (t < INT_MIN) {
+    t = INT_MIN;
+  }
+
+  return r >= 0 ? t : -t;
+}
+
+static int
+ms_until(struct timeval * tv)
+{
+  return timeval_to_relative_ms(tv);
+}
+
+static int
+ms_since(struct timeval * tv)
+{
+  return -timeval_to_relative_ms(tv);
+}
+
+static void
+rebuild(cubeb * ctx)
+{
+  nfds_t nfds;
+  int i;
+  nfds_t j;
+  cubeb_stream * stm;
+
+  assert(ctx->rebuild);
+
+  /* Always count context's control pipe fd. */
+  nfds = 1;
+  for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
+    stm = ctx->streams[i];
+    if (stm) {
+      stm->fds = NULL;
+      if (stm->state == RUNNING) {
+        nfds += stm->nfds;
+      }
+    }
+  }
+
+  free(ctx->fds);
+  ctx->fds = calloc(nfds, sizeof(struct pollfd));
+  assert(ctx->fds);
+  ctx->nfds = nfds;
+
+  /* Include context's control pipe fd. */
+  ctx->fds[0].fd = ctx->control_fd_read;
+  ctx->fds[0].events = POLLIN | POLLERR;
+
+  for (i = 0, j = 1; i < CUBEB_STREAM_MAX; ++i) {
+    stm = ctx->streams[i];
+    if (stm && stm->state == RUNNING) {
+      memcpy(&ctx->fds[j], stm->saved_fds, stm->nfds * sizeof(struct pollfd));
+      stm->fds = &ctx->fds[j];
+      j += stm->nfds;
+    }
+  }
+
+  ctx->rebuild = 0;
+}
+
+static void
+poll_wake(cubeb * ctx)
+{
+  if (write(ctx->control_fd_write, "x", 1) < 0) {
+    /* ignore write error */
+  }
+}
+
+static void
+set_timeout(struct timeval * timeout, unsigned int ms)
+{
+  gettimeofday(timeout, NULL);
+  timeout->tv_sec += ms / 1000;
+  timeout->tv_usec += (ms % 1000) * 1000;
+}
+
+static void
+stream_buffer_decrement(cubeb_stream * stm, long count)
+{
+  char * bufremains = stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, count);
+  memmove(stm->buffer, bufremains, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes - count));
+  stm->bufframes -= count;
+}
+
+static void
+alsa_set_stream_state(cubeb_stream * stm, enum stream_state state)
+{
+  cubeb * ctx;
+  int r;
+
+  ctx = stm->context;
+  stm->state = state;
+  r = pthread_cond_broadcast(&stm->cond);
+  assert(r == 0);
+  ctx->rebuild = 1;
+  poll_wake(ctx);
+}
+
+static enum stream_state
+alsa_process_stream(cubeb_stream * stm)
+{
+  unsigned short revents;
+  snd_pcm_sframes_t avail;
+  int draining;
+
+  draining = 0;
+
+  pthread_mutex_lock(&stm->mutex);
+
+  /* Call _poll_descriptors_revents() even if we don't use it
+     to let underlying plugins clear null events.  Otherwise poll()
+     may wake up again and again, producing unnecessary CPU usage. */
+  WRAP(snd_pcm_poll_descriptors_revents)(stm->pcm, stm->fds, stm->nfds, &revents);
+
+  avail = WRAP(snd_pcm_avail_update)(stm->pcm);
+
+  /* Got null event? Bail and wait for another wakeup. */
+  if (avail == 0) {
+    pthread_mutex_unlock(&stm->mutex);
+    return RUNNING;
+  }
+
+  /* This could happen if we were suspended with SIGSTOP/Ctrl+Z for a long time. */
+  if ((unsigned int) avail > stm->buffer_size) {
+    avail = stm->buffer_size;
+  }
+
+  /* Capture: Read available frames */
+  if (stm->stream_type == SND_PCM_STREAM_CAPTURE && avail > 0) {
+    snd_pcm_sframes_t got;
+
+    if (avail + stm->bufframes > stm->buffer_size) {
+      /* Buffer overflow. Skip and overwrite with new data. */
+      stm->bufframes = 0;
+      // TODO: should it be marked as DRAINING?
+    }
+
+    got = WRAP(snd_pcm_readi)(stm->pcm, stm->buffer+stm->bufframes, avail);
+
+    if (got < 0) {
+      avail = got; // the error handler below will recover us
+    } else {
+      stm->bufframes += got;
+      stm->stream_position += got;
+
+      gettimeofday(&stm->last_activity, NULL);
+    }
+  }
+
+  /* Capture: Pass read frames to callback function */
+  if (stm->stream_type == SND_PCM_STREAM_CAPTURE && stm->bufframes > 0 &&
+      (!stm->other_stream || stm->other_stream->bufframes < stm->other_stream->buffer_size)) {
+    snd_pcm_sframes_t wrote = stm->bufframes;
+    struct cubeb_stream * mainstm = stm->other_stream ? stm->other_stream : stm;
+    void * other_buffer = stm->other_stream ? stm->other_stream->buffer + stm->other_stream->bufframes : NULL;
+
+    /* Correct write size to the other stream available space */
+    if (stm->other_stream && wrote > (snd_pcm_sframes_t) (stm->other_stream->buffer_size - stm->other_stream->bufframes)) {
+      wrote = stm->other_stream->buffer_size - stm->other_stream->bufframes;
+    }
+
+    pthread_mutex_unlock(&stm->mutex);
+    wrote = stm->data_callback(mainstm, stm->user_ptr, stm->buffer, other_buffer, wrote);
+    pthread_mutex_lock(&stm->mutex);
+
+    if (wrote < 0) {
+      avail = wrote; // the error handler below will recover us
+    } else {
+      stream_buffer_decrement(stm, wrote);
+
+      if (stm->other_stream) {
+        stm->other_stream->bufframes += wrote;
+      }
+    }
+  }
+
+  /* Playback: Don't have enough data? Let's ask for more. */
+  if (stm->stream_type == SND_PCM_STREAM_PLAYBACK && avail > (snd_pcm_sframes_t) stm->bufframes &&
+      (!stm->other_stream || stm->other_stream->bufframes > 0)) {
+    long got = avail - stm->bufframes;
+    void * other_buffer = stm->other_stream ? stm->other_stream->buffer : NULL;
+    char * buftail = stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes);
+
+    /* Correct read size to the other stream available frames */
+    if (stm->other_stream && got > (snd_pcm_sframes_t) stm->other_stream->bufframes) {
+      got = stm->other_stream->bufframes;
+    }
+
+    pthread_mutex_unlock(&stm->mutex);
+    got = stm->data_callback(stm, stm->user_ptr, other_buffer, buftail, got);
+    pthread_mutex_lock(&stm->mutex);
+
+    if (got < 0) {
+      avail = got; // the error handler below will recover us
+    } else {
+      stm->bufframes += got;
+
+      if (stm->other_stream) {
+        stream_buffer_decrement(stm->other_stream, got);
+      }
+    }
+  }
+
+  /* Playback: Still don't have enough data? Add some silence. */
+  if (stm->stream_type == SND_PCM_STREAM_PLAYBACK && avail > (snd_pcm_sframes_t) stm->bufframes) {
+    long drain_frames = avail - stm->bufframes;
+    double drain_time = (double) drain_frames / stm->params.rate;
+
+    char * buftail = stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes);
+    memset(buftail, 0, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, drain_frames));
+    stm->bufframes = avail;
+
+    /* Mark as draining, unless we're waiting for capture */
+    if (!stm->other_stream || stm->other_stream->bufframes > 0) {
+      set_timeout(&stm->drain_timeout, drain_time * 1000);
+
+      draining = 1;
+    }
+  }
+
+  /* Playback: Have enough data and no errors. Let's write it out. */
+  if (stm->stream_type == SND_PCM_STREAM_PLAYBACK && avail > 0) {
+    snd_pcm_sframes_t wrote;
+
+    if (stm->params.format == CUBEB_SAMPLE_FLOAT32NE) {
+      float * b = (float *) stm->buffer;
+      for (uint32_t i = 0; i < avail * stm->params.channels; i++) {
+        b[i] *= stm->volume;
+      }
+    } else {
+      short * b = (short *) stm->buffer;
+      for (uint32_t i = 0; i < avail * stm->params.channels; i++) {
+        b[i] *= stm->volume;
+      }
+    }
+
+    wrote = WRAP(snd_pcm_writei)(stm->pcm, stm->buffer, avail);
+    if (wrote < 0) {
+      avail = wrote; // the error handler below will recover us
+    } else {
+      stream_buffer_decrement(stm, wrote);
+
+      stm->stream_position += wrote;
+      gettimeofday(&stm->last_activity, NULL);
+    }
+  }
+
+  /* Got some error? Let's try to recover the stream. */
+  if (avail < 0) {
+    avail = WRAP(snd_pcm_recover)(stm->pcm, avail, 0);
+
+    /* Capture pcm must be started after initial setup/recover */
+    if (avail >= 0 &&
+        stm->stream_type == SND_PCM_STREAM_CAPTURE &&
+        WRAP(snd_pcm_state)(stm->pcm) == SND_PCM_STATE_PREPARED) {
+      avail = WRAP(snd_pcm_start)(stm->pcm);
+    }
+  }
+
+  /* Failed to recover, this stream must be broken. */
+  if (avail < 0) {
+    pthread_mutex_unlock(&stm->mutex);
+    stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
+    return ERROR;
+  }
+
+  pthread_mutex_unlock(&stm->mutex);
+  return draining ? DRAINING : RUNNING;
+}
+
+static int
+alsa_run(cubeb * ctx)
+{
+  int r;
+  int timeout;
+  int i;
+  char dummy;
+  cubeb_stream * stm;
+  enum stream_state state;
+
+  pthread_mutex_lock(&ctx->mutex);
+
+  if (ctx->rebuild) {
+    rebuild(ctx);
+  }
+
+  /* Wake up at least once per second for the watchdog. */
+  timeout = 1000;
+  for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
+    stm = ctx->streams[i];
+    if (stm && stm->state == DRAINING) {
+      r = ms_until(&stm->drain_timeout);
+      if (r >= 0 && timeout > r) {
+        timeout = r;
+      }
+    }
+  }
+
+  pthread_mutex_unlock(&ctx->mutex);
+  r = poll(ctx->fds, ctx->nfds, timeout);
+  pthread_mutex_lock(&ctx->mutex);
+
+  if (r > 0) {
+    if (ctx->fds[0].revents & POLLIN) {
+      if (read(ctx->control_fd_read, &dummy, 1) < 0) {
+        /* ignore read error */
+      }
+
+      if (ctx->shutdown) {
+        pthread_mutex_unlock(&ctx->mutex);
+        return -1;
+      }
+    }
+
+    for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
+      stm = ctx->streams[i];
+      /* We can't use snd_pcm_poll_descriptors_revents here because of
+         https://github.com/kinetiknz/cubeb/issues/135. */
+      if (stm && stm->state == RUNNING && stm->fds && any_revents(stm->fds, stm->nfds)) {
+        alsa_set_stream_state(stm, PROCESSING);
+        pthread_mutex_unlock(&ctx->mutex);
+        state = alsa_process_stream(stm);
+        pthread_mutex_lock(&ctx->mutex);
+        alsa_set_stream_state(stm, state);
+      }
+    }
+  } else if (r == 0) {
+    for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
+      stm = ctx->streams[i];
+      if (stm) {
+        if (stm->state == DRAINING && ms_since(&stm->drain_timeout) >= 0) {
+          alsa_set_stream_state(stm, INACTIVE);
+          stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
+        } else if (stm->state == RUNNING && ms_since(&stm->last_activity) > CUBEB_WATCHDOG_MS) {
+          alsa_set_stream_state(stm, ERROR);
+          stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
+        }
+      }
+    }
+  }
+
+  pthread_mutex_unlock(&ctx->mutex);
+
+  return 0;
+}
+
+static void *
+alsa_run_thread(void * context)
+{
+  cubeb * ctx = context;
+  int r;
+
+  do {
+    r = alsa_run(ctx);
+  } while (r >= 0);
+
+  return NULL;
+}
+
+static snd_config_t *
+get_slave_pcm_node(snd_config_t * lconf, snd_config_t * root_pcm)
+{
+  int r;
+  snd_config_t * slave_pcm;
+  snd_config_t * slave_def;
+  snd_config_t * pcm;
+  char const * string;
+  char node_name[64];
+
+  slave_def = NULL;
+
+  r = WRAP(snd_config_search)(root_pcm, "slave", &slave_pcm);
+  if (r < 0) {
+    return NULL;
+  }
+
+  r = WRAP(snd_config_get_string)(slave_pcm, &string);
+  if (r >= 0) {
+    r = WRAP(snd_config_search_definition)(lconf, "pcm_slave", string, &slave_def);
+    if (r < 0) {
+      return NULL;
+    }
+  }
+
+  do {
+    r = WRAP(snd_config_search)(slave_def ? slave_def : slave_pcm, "pcm", &pcm);
+    if (r < 0) {
+      break;
+    }
+
+    r = WRAP(snd_config_get_string)(slave_def ? slave_def : slave_pcm, &string);
+    if (r < 0) {
+      break;
+    }
+
+    r = snprintf(node_name, sizeof(node_name), "pcm.%s", string);
+    if (r < 0 || r > (int) sizeof(node_name)) {
+      break;
+    }
+    r = WRAP(snd_config_search)(lconf, node_name, &pcm);
+    if (r < 0) {
+      break;
+    }
+
+    return pcm;
+  } while (0);
+
+  if (slave_def) {
+    WRAP(snd_config_delete)(slave_def);
+  }
+
+  return NULL;
+}
+
+/* Work around PulseAudio ALSA plugin bug where the PA server forces a
+   higher than requested latency, but the plugin does not update its (and
+   ALSA's) internal state to reflect that, leading to an immediate underrun
+   situation.  Inspired by WINE's make_handle_underrun_config.
+   Reference: http://mailman.alsa-project.org/pipermail/alsa-devel/2012-July/05 */
+static snd_config_t *
+init_local_config_with_workaround(char const * pcm_name)
+{
+  int r;
+  snd_config_t * lconf;
+  snd_config_t * pcm_node;
+  snd_config_t * node;
+  char const * string;
+  char node_name[64];
+
+  lconf = NULL;
+
+  if (*WRAP(snd_config) == NULL) {
+    return NULL;
+  }
+
+  r = WRAP(snd_config_copy)(&lconf, *WRAP(snd_config));
+  if (r < 0) {
+    return NULL;
+  }
+
+  do {
+    r = WRAP(snd_config_search_definition)(lconf, "pcm", pcm_name, &pcm_node);
+    if (r < 0) {
+      break;
+    }
+
+    r = WRAP(snd_config_get_id)(pcm_node, &string);
+    if (r < 0) {
+      break;
+    }
+
+    r = snprintf(node_name, sizeof(node_name), "pcm.%s", string);
+    if (r < 0 || r > (int) sizeof(node_name)) {
+      break;
+    }
+    r = WRAP(snd_config_search)(lconf, node_name, &pcm_node);
+    if (r < 0) {
+      break;
+    }
+
+    /* If this PCM has a slave, walk the slave configurations until we reach the bottom. */
+    while ((node = get_slave_pcm_node(lconf, pcm_node)) != NULL) {
+      pcm_node = node;
+    }
+
+    /* Fetch the PCM node's type, and bail out if it's not the PulseAudio plugin. */
+    r = WRAP(snd_config_search)(pcm_node, "type", &node);
+    if (r < 0) {
+      break;
+    }
+
+    r = WRAP(snd_config_get_string)(node, &string);
+    if (r < 0) {
+      break;
+    }
+
+    if (strcmp(string, "pulse") != 0) {
+      break;
+    }
+
+    /* Don't clobber an explicit existing handle_underrun value, set it only
+       if it doesn't already exist. */
+    r = WRAP(snd_config_search)(pcm_node, "handle_underrun", &node);
+    if (r != -ENOENT) {
+      break;
+    }
+
+    /* Disable pcm_pulse's asynchronous underrun handling. */
+    r = WRAP(snd_config_imake_integer)(&node, "handle_underrun", 0);
+    if (r < 0) {
+      break;
+    }
+
+    r = WRAP(snd_config_add)(pcm_node, node);
+    if (r < 0) {
+      break;
+    }
+
+    return lconf;
+  } while (0);
+
+  WRAP(snd_config_delete)(lconf);
+
+  return NULL;
+}
+
+static int
+alsa_locked_pcm_open(snd_pcm_t ** pcm, char const * pcm_name, snd_pcm_stream_t stream, snd_config_t * local_config)
+{
+  int r;
+
+  pthread_mutex_lock(&cubeb_alsa_mutex);
+  if (local_config) {
+    r = WRAP(snd_pcm_open_lconf)(pcm, pcm_name, stream, SND_PCM_NONBLOCK, local_config);
+  } else {
+    r = WRAP(snd_pcm_open)(pcm, pcm_name, stream, SND_PCM_NONBLOCK);
+  }
+  pthread_mutex_unlock(&cubeb_alsa_mutex);
+
+  return r;
+}
+
+static int
+alsa_locked_pcm_close(snd_pcm_t * pcm)
+{
+  int r;
+
+  pthread_mutex_lock(&cubeb_alsa_mutex);
+  r = WRAP(snd_pcm_close)(pcm);
+  pthread_mutex_unlock(&cubeb_alsa_mutex);
+
+  return r;
+}
+
+static int
+alsa_register_stream(cubeb * ctx, cubeb_stream * stm)
+{
+  int i;
+
+  pthread_mutex_lock(&ctx->mutex);
+  for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
+    if (!ctx->streams[i]) {
+      ctx->streams[i] = stm;
+      break;
+    }
+  }
+  pthread_mutex_unlock(&ctx->mutex);
+
+  return i == CUBEB_STREAM_MAX;
+}
+
+static void
+alsa_unregister_stream(cubeb_stream * stm)
+{
+  cubeb * ctx;
+  int i;
+
+  ctx = stm->context;
+
+  pthread_mutex_lock(&ctx->mutex);
+  for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
+    if (ctx->streams[i] == stm) {
+      ctx->streams[i] = NULL;
+      break;
+    }
+  }
+  pthread_mutex_unlock(&ctx->mutex);
+}
+
+static void
+silent_error_handler(char const * file, int line, char const * function,
+                     int err, char const * fmt, ...)
+{
+  (void)file;
+  (void)line;
+  (void)function;
+  (void)err;
+  (void)fmt;
+}
+
+/*static*/ int
+alsa_init(cubeb ** context, char const * context_name)
+{
+  (void)context_name;
+  void * libasound = NULL;
+  cubeb * ctx;
+  int r;
+  int i;
+  int fd[2];
+  pthread_attr_t attr;
+  snd_pcm_t * dummy;
+
+  assert(context);
+  *context = NULL;
+
+#ifndef DISABLE_LIBASOUND_DLOPEN
+  libasound = dlopen("libasound.so.2", RTLD_LAZY);
+  if (!libasound) {
+    libasound = dlopen("libasound.so", RTLD_LAZY);
+    if (!libasound) {
+      return CUBEB_ERROR;
+    }
+  }
+
+#define LOAD(x) {                               \
+    cubeb_##x = dlsym(libasound, #x);            \
+    if (!cubeb_##x) {                           \
+      dlclose(libasound);                        \
+      return CUBEB_ERROR;                       \
+    }                                           \
+  }
+
+  LIBASOUND_API_VISIT(LOAD);
+#undef LOAD
+#endif
+
+  pthread_mutex_lock(&cubeb_alsa_mutex);
+  if (!cubeb_alsa_error_handler_set) {
+    WRAP(snd_lib_error_set_handler)(silent_error_handler);
+    cubeb_alsa_error_handler_set = 1;
+  }
+  pthread_mutex_unlock(&cubeb_alsa_mutex);
+
+  ctx = calloc(1, sizeof(*ctx));
+  assert(ctx);
+
+  ctx->ops = &alsa_ops;
+  ctx->libasound = libasound;
+
+  r = pthread_mutex_init(&ctx->mutex, NULL);
+  assert(r == 0);
+
+  r = pipe(fd);
+  assert(r == 0);
+
+  for (i = 0; i < 2; ++i) {
+    fcntl(fd[i], F_SETFD, fcntl(fd[i], F_GETFD) | FD_CLOEXEC);
+    fcntl(fd[i], F_SETFL, fcntl(fd[i], F_GETFL) | O_NONBLOCK);
+  }
+
+  ctx->control_fd_read = fd[0];
+  ctx->control_fd_write = fd[1];
+
+  /* Force an early rebuild when alsa_run is first called to ensure fds and
+     nfds have been initialized. */
+  ctx->rebuild = 1;
+
+  r = pthread_attr_init(&attr);
+  assert(r == 0);
+
+  r = pthread_attr_setstacksize(&attr, 256 * 1024);
+  assert(r == 0);
+
+  r = pthread_create(&ctx->thread, &attr, alsa_run_thread, ctx);
+  assert(r == 0);
+
+  r = pthread_attr_destroy(&attr);
+  assert(r == 0);
+
+  /* Open a dummy PCM to force the configuration space to be evaluated so that
+     init_local_config_with_workaround can find and modify the default node. */
+  r = alsa_locked_pcm_open(&dummy, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK, NULL);
+  if (r >= 0) {
+    alsa_locked_pcm_close(dummy);
+  }
+  ctx->is_pa = 0;
+  pthread_mutex_lock(&cubeb_alsa_mutex);
+  ctx->local_config = init_local_config_with_workaround(CUBEB_ALSA_PCM_NAME);
+  pthread_mutex_unlock(&cubeb_alsa_mutex);
+  if (ctx->local_config) {
+    ctx->is_pa = 1;
+    r = alsa_locked_pcm_open(&dummy, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK, ctx->local_config);
+    /* If we got a local_config, we found a PA PCM.  If opening a PCM with that
+       config fails with EINVAL, the PA PCM is too old for this workaround. */
+    if (r == -EINVAL) {
+      pthread_mutex_lock(&cubeb_alsa_mutex);
+      WRAP(snd_config_delete)(ctx->local_config);
+      pthread_mutex_unlock(&cubeb_alsa_mutex);
+      ctx->local_config = NULL;
+    } else if (r >= 0) {
+      alsa_locked_pcm_close(dummy);
+    }
+  }
+
+  *context = ctx;
+
+  return CUBEB_OK;
+}
+
+static char const *
+alsa_get_backend_id(cubeb * ctx)
+{
+  (void)ctx;
+  return "alsa";
+}
+
+static void
+alsa_destroy(cubeb * ctx)
+{
+  int r;
+
+  assert(ctx);
+
+  pthread_mutex_lock(&ctx->mutex);
+  ctx->shutdown = 1;
+  poll_wake(ctx);
+  pthread_mutex_unlock(&ctx->mutex);
+
+  r = pthread_join(ctx->thread, NULL);
+  assert(r == 0);
+
+  close(ctx->control_fd_read);
+  close(ctx->control_fd_write);
+  pthread_mutex_destroy(&ctx->mutex);
+  free(ctx->fds);
+
+  if (ctx->local_config) {
+    pthread_mutex_lock(&cubeb_alsa_mutex);
+    WRAP(snd_config_delete)(ctx->local_config);
+    pthread_mutex_unlock(&cubeb_alsa_mutex);
+  }
+
+  if (ctx->libasound) {
+    dlclose(ctx->libasound);
+  }
+
+  free(ctx);
+}
+
+static void alsa_stream_destroy(cubeb_stream * stm);
+
+static int
+alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
+                        snd_pcm_stream_t stream_type,
+                        cubeb_devid deviceid,
+                        cubeb_stream_params * stream_params,
+                        unsigned int latency_frames,
+                        cubeb_data_callback data_callback,
+                        cubeb_state_callback state_callback,
+                        void * user_ptr)
+{
+  (void)stream_name;
+  cubeb_stream * stm;
+  int r;
+  snd_pcm_format_t format;
+  snd_pcm_uframes_t period_size;
+  int latency_us = 0;
+  char const * pcm_name = deviceid ? (char const *) deviceid : CUBEB_ALSA_PCM_NAME;
+
+  assert(ctx && stream);
+
+  *stream = NULL;
+
+  if (stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
+    return CUBEB_ERROR_NOT_SUPPORTED;
+  }
+
+  switch (stream_params->format) {
+  case CUBEB_SAMPLE_S16LE:
+    format = SND_PCM_FORMAT_S16_LE;
+    break;
+  case CUBEB_SAMPLE_S16BE:
+    format = SND_PCM_FORMAT_S16_BE;
+    break;
+  case CUBEB_SAMPLE_FLOAT32LE:
+    format = SND_PCM_FORMAT_FLOAT_LE;
+    break;
+  case CUBEB_SAMPLE_FLOAT32BE:
+    format = SND_PCM_FORMAT_FLOAT_BE;
+    break;
+  default:
+    return CUBEB_ERROR_INVALID_FORMAT;
+  }
+
+  pthread_mutex_lock(&ctx->mutex);
+  if (ctx->active_streams >= CUBEB_STREAM_MAX) {
+    pthread_mutex_unlock(&ctx->mutex);
+    return CUBEB_ERROR;
+  }
+  ctx->active_streams += 1;
+  pthread_mutex_unlock(&ctx->mutex);
+
+  stm = calloc(1, sizeof(*stm));
+  assert(stm);
+
+  stm->context = ctx;
+  stm->data_callback = data_callback;
+  stm->state_callback = state_callback;
+  stm->user_ptr = user_ptr;
+  stm->params = *stream_params;
+  stm->state = INACTIVE;
+  stm->volume = 1.0;
+  stm->buffer = NULL;
+  stm->bufframes = 0;
+  stm->stream_type = stream_type;
+  stm->other_stream = NULL;
+
+  r = pthread_mutex_init(&stm->mutex, NULL);
+  assert(r == 0);
+
+  r = pthread_cond_init(&stm->cond, NULL);
+  assert(r == 0);
+
+  r = alsa_locked_pcm_open(&stm->pcm, pcm_name, stm->stream_type, ctx->local_config);
+  if (r < 0) {
+    alsa_stream_destroy(stm);
+    return CUBEB_ERROR;
+  }
+
+  r = WRAP(snd_pcm_nonblock)(stm->pcm, 1);
+  assert(r == 0);
+
+  latency_us = latency_frames * 1e6 / stm->params.rate;
+
+  /* Ugly hack: the PA ALSA plugin allows buffer configurations that can't
+     possibly work.  See https://bugzilla.mozilla.org/show_bug.cgi?id=761274.
+     Only resort to this hack if the handle_underrun workaround failed. */
+  if (!ctx->local_config && ctx->is_pa) {
+    const int min_latency = 5e5;
+    latency_us = latency_us < min_latency ? min_latency: latency_us;
+  }
+
+  r = WRAP(snd_pcm_set_params)(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED,
+                         stm->params.channels, stm->params.rate, 1,
+                         latency_us);
+  if (r < 0) {
+    alsa_stream_destroy(stm);
+    return CUBEB_ERROR_INVALID_FORMAT;
+  }
+
+  r = WRAP(snd_pcm_get_params)(stm->pcm, &stm->buffer_size, &period_size);
+  assert(r == 0);
+
+  /* Double internal buffer size to have enough space when waiting for the other side of duplex connection */
+  stm->buffer_size *= 2;
+  stm->buffer = calloc(1, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->buffer_size));
+  assert(stm->buffer);
+
+  stm->nfds = WRAP(snd_pcm_poll_descriptors_count)(stm->pcm);
+  assert(stm->nfds > 0);
+
+  stm->saved_fds = calloc(stm->nfds, sizeof(struct pollfd));
+  assert(stm->saved_fds);
+  r = WRAP(snd_pcm_poll_descriptors)(stm->pcm, stm->saved_fds, stm->nfds);
+  assert((nfds_t) r == stm->nfds);
+
+  if (alsa_register_stream(ctx, stm) != 0) {
+    alsa_stream_destroy(stm);
+    return CUBEB_ERROR;
+  }
+
+  *stream = stm;
+
+  return CUBEB_OK;
+}
+
+static int
+alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
+                 cubeb_devid input_device,
+                 cubeb_stream_params * input_stream_params,
+                 cubeb_devid output_device,
+                 cubeb_stream_params * output_stream_params,
+                 unsigned int latency_frames,
+                 cubeb_data_callback data_callback, cubeb_state_callback state_callback,
+                 void * user_ptr)
+{
+  int result = CUBEB_OK;
+  cubeb_stream * instm = NULL, * outstm = NULL;
+
+  if (result == CUBEB_OK && input_stream_params) {
+    result = alsa_stream_init_single(ctx, &instm, stream_name, SND_PCM_STREAM_CAPTURE,
+                                     input_device, input_stream_params, latency_frames,
+                                     data_callback, state_callback, user_ptr);
+  }
+
+  if (result == CUBEB_OK && output_stream_params) {
+    result = alsa_stream_init_single(ctx, &outstm, stream_name, SND_PCM_STREAM_PLAYBACK,
+                                     output_device, output_stream_params, latency_frames,
+                                     data_callback, state_callback, user_ptr);
+  }
+
+  if (result == CUBEB_OK && input_stream_params && output_stream_params) {
+    instm->other_stream = outstm;
+    outstm->other_stream = instm;
+  }
+
+  if (result != CUBEB_OK && instm) {
+    alsa_stream_destroy(instm);
+  }
+
+  *stream = outstm ? outstm : instm;
+
+  return result;
+}
+
+static void
+alsa_stream_destroy(cubeb_stream * stm)
+{
+  int r;
+  cubeb * ctx;
+
+  assert(stm && (stm->state == INACTIVE ||
+                 stm->state == ERROR ||
+                 stm->state == DRAINING));
+
+  ctx = stm->context;
+
+  if (stm->other_stream) {
+    stm->other_stream->other_stream = NULL; // to stop infinite recursion
+    alsa_stream_destroy(stm->other_stream);
+  }
+
+  pthread_mutex_lock(&stm->mutex);
+  if (stm->pcm) {
+    if (stm->state == DRAINING) {
+      WRAP(snd_pcm_drain)(stm->pcm);
+    }
+    alsa_locked_pcm_close(stm->pcm);
+    stm->pcm = NULL;
+  }
+  free(stm->saved_fds);
+  pthread_mutex_unlock(&stm->mutex);
+  pthread_mutex_destroy(&stm->mutex);
+
+  r = pthread_cond_destroy(&stm->cond);
+  assert(r == 0);
+
+  alsa_unregister_stream(stm);
+
+  pthread_mutex_lock(&ctx->mutex);
+  assert(ctx->active_streams >= 1);
+  ctx->active_streams -= 1;
+  pthread_mutex_unlock(&ctx->mutex);
+
+  free(stm->buffer);
+
+  free(stm);
+}
+
+static int
+alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
+{
+  int r;
+  cubeb_stream * stm;
+  snd_pcm_hw_params_t* hw_params;
+  cubeb_stream_params params;
+  params.rate = 44100;
+  params.format = CUBEB_SAMPLE_FLOAT32NE;
+  params.channels = 2;
+
+  snd_pcm_hw_params_alloca(&hw_params);
+
+  assert(ctx);
+
+  r = alsa_stream_init(ctx, &stm, "", NULL, NULL, NULL, &params, 100, NULL, NULL, NULL);
+  if (r != CUBEB_OK) {
+    return CUBEB_ERROR;
+  }
+
+  assert(stm);
+
+  r = WRAP(snd_pcm_hw_params_any)(stm->pcm, hw_params);
+  if (r < 0) {
+    return CUBEB_ERROR;
+  }
+
+  r = WRAP(snd_pcm_hw_params_get_channels_max)(hw_params, max_channels);
+  if (r < 0) {
+    return CUBEB_ERROR;
+  }
+
+  alsa_stream_destroy(stm);
+
+  return CUBEB_OK;
+}
+
+static int
+alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) {
+  (void)ctx;
+  int r, dir;
+  snd_pcm_t * pcm;
+  snd_pcm_hw_params_t * hw_params;
+
+  snd_pcm_hw_params_alloca(&hw_params);
+
+  /* get a pcm, disabling resampling, so we get a rate the
+   * hardware/dmix/pulse/etc. supports. */
+  r = WRAP(snd_pcm_open)(&pcm, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK, SND_PCM_NO_AUTO_RESAMPLE);
+  if (r < 0) {
+    return CUBEB_ERROR;
+  }
+
+  r = WRAP(snd_pcm_hw_params_any)(pcm, hw_params);
+  if (r < 0) {
+    WRAP(snd_pcm_close)(pcm);
+    return CUBEB_ERROR;
+  }
+
+  r = WRAP(snd_pcm_hw_params_get_rate)(hw_params, rate, &dir);
+  if (r >= 0) {
+    /* There is a default rate: use it. */
+    WRAP(snd_pcm_close)(pcm);
+    return CUBEB_OK;
+  }
+
+  /* Use a common rate, alsa may adjust it based on hw/etc. capabilities. */
+  *rate = 44100;
+
+  r = WRAP(snd_pcm_hw_params_set_rate_near)(pcm, hw_params, rate, NULL);
+  if (r < 0) {
+    WRAP(snd_pcm_close)(pcm);
+    return CUBEB_ERROR;
+  }
+
+  WRAP(snd_pcm_close)(pcm);
+
+  return CUBEB_OK;
+}
+
+static int
+alsa_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
+{
+  (void)ctx;
+  /* 40ms is found to be an acceptable minimum, even on a super low-end
+   * machine. */
+  *latency_frames = 40 * params.rate / 1000;
+
+  return CUBEB_OK;
+}
+
+static int
+alsa_stream_start(cubeb_stream * stm)
+{
+  cubeb * ctx;
+
+  assert(stm);
+  ctx = stm->context;
+
+  if (stm->stream_type == SND_PCM_STREAM_PLAYBACK && stm->other_stream) {
+    int r = alsa_stream_start(stm->other_stream);
+    if (r != CUBEB_OK)
+      return r;
+  }
+
+  pthread_mutex_lock(&stm->mutex);
+  /* Capture pcm must be started after initial setup/recover */
+  if (stm->stream_type == SND_PCM_STREAM_CAPTURE &&
+      WRAP(snd_pcm_state)(stm->pcm) == SND_PCM_STATE_PREPARED) {
+    WRAP(snd_pcm_start)(stm->pcm);
+  }
+  WRAP(snd_pcm_pause)(stm->pcm, 0);
+  gettimeofday(&stm->last_activity, NULL);
+  pthread_mutex_unlock(&stm->mutex);
+
+  pthread_mutex_lock(&ctx->mutex);
+  if (stm->state != INACTIVE) {
+    pthread_mutex_unlock(&ctx->mutex);
+    return CUBEB_ERROR;
+  }
+  alsa_set_stream_state(stm, RUNNING);
+  pthread_mutex_unlock(&ctx->mutex);
+
+  return CUBEB_OK;
+}
+
+static int
+alsa_stream_stop(cubeb_stream * stm)
+{
+  cubeb * ctx;
+  int r;
+
+  assert(stm);
+  ctx = stm->context;
+
+  if (stm->stream_type == SND_PCM_STREAM_PLAYBACK && stm->other_stream) {
+    int r = alsa_stream_stop(stm->other_stream);
+    if (r != CUBEB_OK)
+      return r;
+  }
+
+  pthread_mutex_lock(&ctx->mutex);
+  while (stm->state == PROCESSING) {
+    r = pthread_cond_wait(&stm->cond, &ctx->mutex);
+    assert(r == 0);
+  }
+
+  alsa_set_stream_state(stm, INACTIVE);
+  pthread_mutex_unlock(&ctx->mutex);
+
+  pthread_mutex_lock(&stm->mutex);
+  WRAP(snd_pcm_pause)(stm->pcm, 1);
+  pthread_mutex_unlock(&stm->mutex);
+
+  return CUBEB_OK;
+}
+
+static int
+alsa_stream_get_position(cubeb_stream * stm, uint64_t * position)
+{
+  snd_pcm_sframes_t delay;
+
+  assert(stm && position);
+
+  pthread_mutex_lock(&stm->mutex);
+
+  delay = -1;
+  if (WRAP(snd_pcm_state)(stm->pcm) != SND_PCM_STATE_RUNNING ||
+      WRAP(snd_pcm_delay)(stm->pcm, &delay) != 0) {
+    *position = stm->last_position;
+    pthread_mutex_unlock(&stm->mutex);
+    return CUBEB_OK;
+  }
+
+  assert(delay >= 0);
+
+  *position = 0;
+  if (stm->stream_position >= (snd_pcm_uframes_t) delay) {
+    *position = stm->stream_position - delay;
+  }
+
+  stm->last_position = *position;
+
+  pthread_mutex_unlock(&stm->mutex);
+  return CUBEB_OK;
+}
+
+static int
+alsa_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
+{
+  snd_pcm_sframes_t delay;
+  /* This function returns the delay in frames until a frame written using
+     snd_pcm_writei is sent to the DAC. The DAC delay should be < 1ms anyways. */
+  if (WRAP(snd_pcm_delay)(stm->pcm, &delay)) {
+    return CUBEB_ERROR;
+  }
+
+  *latency = delay;
+
+  return CUBEB_OK;
+}
+
+static int
+alsa_stream_set_volume(cubeb_stream * stm, float volume)
+{
+  /* setting the volume using an API call does not seem very stable/supported */
+  pthread_mutex_lock(&stm->mutex);
+  stm->volume = volume;
+  pthread_mutex_unlock(&stm->mutex);
+
+  return CUBEB_OK;
+}
+
+static int
+alsa_enumerate_devices(cubeb * context, cubeb_device_type type,
+                       cubeb_device_collection * collection)
+{
+  cubeb_device_info* device = NULL;
+
+  if (!context)
+    return CUBEB_ERROR;
+
+  uint32_t rate, max_channels;
+  int r;
+
+  r = alsa_get_preferred_sample_rate(context, &rate);
+  if (r != CUBEB_OK) {
+    return CUBEB_ERROR;
+  }
+
+  r = alsa_get_max_channel_count(context, &max_channels);
+  if (r != CUBEB_OK) {
+    return CUBEB_ERROR;
+  }
+
+  char const * a_name = "default";
+  device = (cubeb_device_info *) calloc(1, sizeof(cubeb_device_info));
+  assert(device);
+  if (!device)
+    return CUBEB_ERROR;
+
+  device->device_id = a_name;
+  device->devid = (cubeb_devid) device->device_id;
+  device->friendly_name = a_name;
+  device->group_id = a_name;
+  device->vendor_name = a_name;
+  device->type = type;
+  device->state = CUBEB_DEVICE_STATE_ENABLED;
+  device->preferred = CUBEB_DEVICE_PREF_ALL;
+  device->format = CUBEB_DEVICE_FMT_S16NE;
+  device->default_format = CUBEB_DEVICE_FMT_S16NE;
+  device->max_channels = max_channels;
+  device->min_rate = rate;
+  device->max_rate = rate;
+  device->default_rate = rate;
+  device->latency_lo = 0;
+  device->latency_hi = 0;
+
+  collection->device = device;
+  collection->count = 1;
+
+  return CUBEB_OK;
+}
+
+static int
+alsa_device_collection_destroy(cubeb * context,
+                               cubeb_device_collection * collection)
+{
+  assert(collection->count == 1);
+  (void) context;
+  free(collection->device);
+  return CUBEB_OK;
+}
+
+static struct cubeb_ops const alsa_ops = {
+  .init = alsa_init,
+  .get_backend_id = alsa_get_backend_id,
+  .get_max_channel_count = alsa_get_max_channel_count,
+  .get_min_latency = alsa_get_min_latency,
+  .get_preferred_sample_rate = alsa_get_preferred_sample_rate,
+  .enumerate_devices = alsa_enumerate_devices,
+  .device_collection_destroy = alsa_device_collection_destroy,
+  .destroy = alsa_destroy,
+  .stream_init = alsa_stream_init,
+  .stream_destroy = alsa_stream_destroy,
+  .stream_start = alsa_stream_start,
+  .stream_stop = alsa_stream_stop,
+  .stream_reset_default_device = NULL,
+  .stream_get_position = alsa_stream_get_position,
+  .stream_get_latency = alsa_stream_get_latency,
+  .stream_set_volume = alsa_stream_set_volume,
+  .stream_get_current_device = NULL,
+  .stream_device_destroy = NULL,
+  .stream_register_device_changed_callback = NULL,
+  .register_device_collection_changed = NULL
+};
diff --git a/dep/cubeb/src/cubeb_array_queue.h b/dep/cubeb/src/cubeb_array_queue.h
new file mode 100644
index 000000000..a8ea4cd17
--- /dev/null
+++ b/dep/cubeb/src/cubeb_array_queue.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright © 2016 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+
+#ifndef CUBEB_ARRAY_QUEUE_H
+#define CUBEB_ARRAY_QUEUE_H
+
+#include <assert.h>
+#include <pthread.h>
+#include <unistd.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef struct
+{
+  void ** buf;
+  size_t num;
+  size_t writePos;
+  size_t readPos;
+  pthread_mutex_t mutex;
+} array_queue;
+
+array_queue * array_queue_create(size_t num)
+{
+  assert(num != 0);
+  array_queue * new_queue = (array_queue*)calloc(1, sizeof(array_queue));
+  new_queue->buf = (void **)calloc(1, sizeof(void *) * num);
+  new_queue->readPos = 0;
+  new_queue->writePos = 0;
+  new_queue->num = num;
+
+  pthread_mutex_init(&new_queue->mutex, NULL);
+
+  return new_queue;
+}
+
+void array_queue_destroy(array_queue * aq)
+{
+  assert(aq);
+
+  free(aq->buf);
+  pthread_mutex_destroy(&aq->mutex);
+  free(aq);
+}
+
+int array_queue_push(array_queue * aq, void * item)
+{
+  assert(item);
+
+  pthread_mutex_lock(&aq->mutex);
+  int ret = -1;
+  if(aq->buf[aq->writePos % aq->num] == NULL)
+  {
+    aq->buf[aq->writePos % aq->num] = item;
+    aq->writePos = (aq->writePos + 1) % aq->num;
+    ret = 0;
+  }
+  // else queue is full
+  pthread_mutex_unlock(&aq->mutex);
+  return ret;
+}
+
+void* array_queue_pop(array_queue * aq)
+{
+  pthread_mutex_lock(&aq->mutex);
+  void * value = aq->buf[aq->readPos % aq->num];
+  if(value)
+  {
+    aq->buf[aq->readPos % aq->num] = NULL;
+    aq->readPos = (aq->readPos + 1) % aq->num;
+  }
+  pthread_mutex_unlock(&aq->mutex);
+  return value;
+}
+
+size_t array_queue_get_size(array_queue * aq)
+{
+  pthread_mutex_lock(&aq->mutex);
+  ssize_t r = aq->writePos - aq->readPos;
+  if (r < 0) {
+    r = aq->num + r;
+    assert(r >= 0);
+  }
+  pthread_mutex_unlock(&aq->mutex);
+  return (size_t)r;
+}
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif //CUBE_ARRAY_QUEUE_H
diff --git a/dep/cubeb/src/cubeb_assert.h b/dep/cubeb/src/cubeb_assert.h
new file mode 100644
index 000000000..9257a2c86
--- /dev/null
+++ b/dep/cubeb/src/cubeb_assert.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright © 2017 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+
+#ifndef CUBEB_ASSERT
+#define CUBEB_ASSERT
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/**
+ * This allow using an external release assert method. This file should only
+ * export a function or macro called XASSERT that aborts the program.
+ */
+
+#define XASSERT(expr) do {                                                     \
+    if (!(expr)) {                                                             \
+      fprintf(stderr, "%s:%d - fatal error: %s\n", __FILE__, __LINE__, #expr); \
+      abort();                                                                 \
+    }                                                                          \
+  } while (0)
+
+#endif
diff --git a/dep/cubeb/src/cubeb_audiotrack.c b/dep/cubeb/src/cubeb_audiotrack.c
new file mode 100644
index 000000000..22f1fe0bc
--- /dev/null
+++ b/dep/cubeb/src/cubeb_audiotrack.c
@@ -0,0 +1,441 @@
+/*
+ * Copyright © 2013 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+
+#if !defined(NDEBUG)
+#define NDEBUG
+#endif
+#include <assert.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <time.h>
+#include <dlfcn.h>
+#include <android/log.h>
+
+#include "cubeb/cubeb.h"
+#include "cubeb-internal.h"
+#include "android/audiotrack_definitions.h"
+
+#ifndef ALOG
+#if defined(DEBUG) || defined(FORCE_ALOG)
+#define ALOG(args...)  __android_log_print(ANDROID_LOG_INFO, "Gecko - Cubeb" , ## args)
+#else
+#define ALOG(args...)
+#endif
+#endif
+
+/**
+ * A lot of bytes for safety. It should be possible to bring this down a bit. */
+#define SIZE_AUDIOTRACK_INSTANCE 256
+
+/**
+ * call dlsym to get the symbol |mangled_name|, handle the error and store the
+ * pointer in |pointer|. Because depending on Android version, we want different
+ * symbols, not finding a symbol is not an error. */
+#define DLSYM_DLERROR(mangled_name, pointer, lib)                       \
+  do {                                                                  \
+    pointer = dlsym(lib, mangled_name);                                 \
+    if (!pointer) {                                                     \
+      ALOG("error while loading %stm: %stm\n", mangled_name, dlerror()); \
+    } else {                                                            \
+      ALOG("%stm: OK", mangled_name);                                   \
+    }                                                                   \
+  } while(0);
+
+static struct cubeb_ops const audiotrack_ops;
+void audiotrack_destroy(cubeb * context);
+void audiotrack_stream_destroy(cubeb_stream * stream);
+
+struct AudioTrack {
+  /* only available on ICS and later. The second int paramter is in fact of type audio_stream_type_t. */
+  /* static */ status_t (*get_min_frame_count)(int* frame_count, int stream_type, uint32_t rate);
+  /* if we have a recent ctor, but can't find the above symbol, we
+   * can get the minimum frame count with this signature, and we are
+   * running gingerbread. */
+  /* static */ status_t (*get_min_frame_count_gingerbread)(int* frame_count, int stream_type, uint32_t rate);
+  void* (*ctor)(void* instance, int, unsigned int, int, int, int, unsigned int, void (*)(int, void*, void*), void*, int, int);
+  void* (*dtor)(void* instance);
+  void (*start)(void* instance);
+  void (*pause)(void* instance);
+  uint32_t (*latency)(void* instance);
+  status_t (*check)(void* instance);
+  status_t (*get_position)(void* instance, uint32_t* position);
+  /* static */ int (*get_output_samplingrate)(int* samplerate, int stream);
+  status_t (*set_marker_position)(void* instance, unsigned int);
+  status_t (*set_volume)(void* instance, float left, float right);
+};
+
+struct cubeb {
+  struct cubeb_ops const * ops;
+  void * library;
+  struct AudioTrack klass;
+};
+
+struct cubeb_stream {
+  /* Note: Must match cubeb_stream layout in cubeb.c. */
+  cubeb * context;
+  void * user_ptr;
+  /**/
+  cubeb_stream_params params;
+  cubeb_data_callback data_callback;
+  cubeb_state_callback state_callback;
+  void * instance;
+  /* Number of frames that have been passed to the AudioTrack callback */
+  long unsigned written;
+  int draining;
+};
+
+static void
+audiotrack_refill(int event, void* user, void* info)
+{
+  cubeb_stream * stream = user;
+  switch (event) {
+  case EVENT_MORE_DATA: {
+    long got = 0;
+    struct Buffer * b = (struct Buffer*)info;
+
+    if (stream->draining) {
+      return;
+    }
+
+    got = stream->data_callback(stream, stream->user_ptr, NULL, b->raw, b->frameCount);
+
+    stream->written += got;
+
+    if (got != (long)b->frameCount) {
+      stream->draining = 1;
+      /* set a marker so we are notified when the are done draining, that is,
+       * when every frame has been played by android. */
+      stream->context->klass.set_marker_position(stream->instance, stream->written);
+    }
+
+    break;
+  }
+  case EVENT_UNDERRUN:
+    ALOG("underrun in cubeb backend.");
+    break;
+  case EVENT_LOOP_END:
+    assert(0 && "We don't support the loop feature of audiotrack.");
+    break;
+  case EVENT_MARKER:
+    assert(stream->draining);
+    stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_DRAINED);
+    break;
+  case EVENT_NEW_POS:
+    assert(0 && "We don't support the setPositionUpdatePeriod feature of audiotrack.");
+    break;
+  case EVENT_BUFFER_END:
+    assert(0 && "Should not happen.");
+    break;
+  }
+}
+
+/* We are running on gingerbread if we found the gingerbread signature for
+ * getMinFrameCount */
+static int
+audiotrack_version_is_gingerbread(cubeb * ctx)
+{
+  return ctx->klass.get_min_frame_count_gingerbread != NULL;
+}
+
+int
+audiotrack_get_min_frame_count(cubeb * ctx, cubeb_stream_params * params, int * min_frame_count)
+{
+  status_t status;
+  /* Recent Android have a getMinFrameCount method. */
+  if (!audiotrack_version_is_gingerbread(ctx)) {
+    status = ctx->klass.get_min_frame_count(min_frame_count, AUDIO_STREAM_TYPE_MUSIC, params->rate);
+  } else {
+    status = ctx->klass.get_min_frame_count_gingerbread(min_frame_count, AUDIO_STREAM_TYPE_MUSIC, params->rate);
+  }
+  if (status != 0) {
+    ALOG("error getting the min frame count");
+    return CUBEB_ERROR;
+  }
+  return CUBEB_OK;
+}
+
+int
+audiotrack_init(cubeb ** context, char const * context_name)
+{
+  cubeb * ctx;
+  struct AudioTrack* c;
+
+  assert(context);
+  *context = NULL;
+
+  ctx = calloc(1, sizeof(*ctx));
+  assert(ctx);
+
+  /* If we use an absolute path here ("/system/lib/libmedia.so"), and on Android
+   * 2.2, the dlopen succeeds, all the dlsym succeed, but a segfault happens on
+   * the first call to a dlsym'ed function. Somehow this does not happen when
+   * using only the name of the library. */
+  ctx->library = dlopen("libmedia.so", RTLD_LAZY);
+  if (!ctx->library) {
+    ALOG("dlopen error: %s.", dlerror());
+    free(ctx);
+    return CUBEB_ERROR;
+  }
+
+  /* Recent Android first, then Gingerbread. */
+  DLSYM_DLERROR("_ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_ii", ctx->klass.ctor, ctx->library);
+  DLSYM_DLERROR("_ZN7android10AudioTrackD1Ev", ctx->klass.dtor, ctx->library);
+
+  DLSYM_DLERROR("_ZNK7android10AudioTrack7latencyEv", ctx->klass.latency, ctx->library);
+  DLSYM_DLERROR("_ZNK7android10AudioTrack9initCheckEv", ctx->klass.check, ctx->library);
+
+  DLSYM_DLERROR("_ZN7android11AudioSystem21getOutputSamplingRateEPii", ctx->klass.get_output_samplingrate, ctx->library);
+
+  /* |getMinFrameCount| is available on gingerbread and ICS with different signatures. */
+  DLSYM_DLERROR("_ZN7android10AudioTrack16getMinFrameCountEPi19audio_stream_type_tj", ctx->klass.get_min_frame_count, ctx->library);
+  if (!ctx->klass.get_min_frame_count) {
+    DLSYM_DLERROR("_ZN7android10AudioTrack16getMinFrameCountEPiij", ctx->klass.get_min_frame_count_gingerbread, ctx->library);
+  }
+
+  DLSYM_DLERROR("_ZN7android10AudioTrack5startEv", ctx->klass.start, ctx->library);
+  DLSYM_DLERROR("_ZN7android10AudioTrack5pauseEv", ctx->klass.pause, ctx->library);
+  DLSYM_DLERROR("_ZN7android10AudioTrack11getPositionEPj", ctx->klass.get_position, ctx->library);
+  DLSYM_DLERROR("_ZN7android10AudioTrack17setMarkerPositionEj", ctx->klass.set_marker_position, ctx->library);
+  DLSYM_DLERROR("_ZN7android10AudioTrack9setVolumeEff", ctx->klass.set_volume, ctx->library);
+
+  /* check that we have a combination of symbol that makes sense */
+  c = &ctx->klass;
+  if(!(c->ctor &&
+       c->dtor && c->latency && c->check &&
+       /* at least one way to get the minimum frame count to request. */
+       (c->get_min_frame_count ||
+        c->get_min_frame_count_gingerbread) &&
+       c->start && c->pause && c->get_position && c->set_marker_position)) {
+    ALOG("Could not find all the symbols we need.");
+    audiotrack_destroy(ctx);
+    return CUBEB_ERROR;
+  }
+
+  ctx->ops = &audiotrack_ops;
+
+  *context = ctx;
+
+  return CUBEB_OK;
+}
+
+char const *
+audiotrack_get_backend_id(cubeb * context)
+{
+  return "audiotrack";
+}
+
+static int
+audiotrack_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
+{
+  assert(ctx && max_channels);
+
+  /* The android mixer handles up to two channels, see
+     http://androidxref.com/4.2.2_r1/xref/frameworks/av/services/audioflinger/AudioFlinger.h#67 */
+  *max_channels = 2;
+
+  return CUBEB_OK;
+}
+
+static int
+audiotrack_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms)
+{
+  /* We always use the lowest latency possible when using this backend (see
+   * audiotrack_stream_init), so this value is not going to be used. */
+  int r;
+
+  r = audiotrack_get_min_frame_count(ctx, &params, (int *)latency_ms);
+  if (r != CUBEB_OK) {
+    return CUBEB_ERROR;
+  }
+
+  return CUBEB_OK;
+}
+
+static int
+audiotrack_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
+{
+  status_t r;
+
+  r = ctx->klass.get_output_samplingrate((int32_t *)rate, 3 /* MUSIC */);
+
+  return r == 0 ? CUBEB_OK : CUBEB_ERROR;
+}
+
+void
+audiotrack_destroy(cubeb * context)
+{
+  assert(context);
+
+  dlclose(context->library);
+
+  free(context);
+}
+
+int
+audiotrack_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
+                       cubeb_devid input_device,
+                       cubeb_stream_params * input_stream_params,
+                       cubeb_devid output_device,
+                       cubeb_stream_params * output_stream_params,
+                       unsigned int latency,
+                       cubeb_data_callback data_callback,
+                       cubeb_state_callback state_callback,
+                       void * user_ptr)
+{
+  cubeb_stream * stm;
+  int32_t channels;
+  uint32_t min_frame_count;
+
+  assert(ctx && stream);
+
+  assert(!input_stream_params && "not supported");
+  if (input_device || output_device) {
+    /* Device selection not yet implemented. */
+    return CUBEB_ERROR_DEVICE_UNAVAILABLE;
+  }
+
+  if (output_stream_params->format == CUBEB_SAMPLE_FLOAT32LE ||
+      output_stream_params->format == CUBEB_SAMPLE_FLOAT32BE) {
+    return CUBEB_ERROR_INVALID_FORMAT;
+  }
+
+  if (audiotrack_get_min_frame_count(ctx, output_stream_params, (int *)&min_frame_count)) {
+    return CUBEB_ERROR;
+  }
+
+  stm = calloc(1, sizeof(*stm));
+  assert(stm);
+
+  stm->context = ctx;
+  stm->data_callback = data_callback;
+  stm->state_callback = state_callback;
+  stm->user_ptr = user_ptr;
+  stm->params = *output_stream_params;
+
+  stm->instance = calloc(SIZE_AUDIOTRACK_INSTANCE, 1);
+  (*(uint32_t*)((intptr_t)stm->instance + SIZE_AUDIOTRACK_INSTANCE - 4)) = 0xbaadbaad;
+  assert(stm->instance && "cubeb: EOM");
+
+  /* gingerbread uses old channel layout enum */
+  if (audiotrack_version_is_gingerbread(ctx)) {
+    channels = stm->params.channels == 2 ? AUDIO_CHANNEL_OUT_STEREO_Legacy : AUDIO_CHANNEL_OUT_MONO_Legacy;
+  } else {
+    channels = stm->params.channels == 2 ? AUDIO_CHANNEL_OUT_STEREO_ICS : AUDIO_CHANNEL_OUT_MONO_ICS;
+  }
+
+  ctx->klass.ctor(stm->instance, AUDIO_STREAM_TYPE_MUSIC, stm->params.rate,
+                  AUDIO_FORMAT_PCM_16_BIT, channels, min_frame_count, 0,
+                  audiotrack_refill, stm, 0, 0);
+
+  assert((*(uint32_t*)((intptr_t)stm->instance + SIZE_AUDIOTRACK_INSTANCE - 4)) == 0xbaadbaad);
+
+  if (ctx->klass.check(stm->instance)) {
+    ALOG("stream not initialized properly.");
+    audiotrack_stream_destroy(stm);
+    return CUBEB_ERROR;
+  }
+
+  *stream = stm;
+
+  return CUBEB_OK;
+}
+
+void
+audiotrack_stream_destroy(cubeb_stream * stream)
+{
+  assert(stream->context);
+
+  stream->context->klass.dtor(stream->instance);
+
+  free(stream->instance);
+  stream->instance = NULL;
+  free(stream);
+}
+
+int
+audiotrack_stream_start(cubeb_stream * stream)
+{
+  assert(stream->instance);
+
+  stream->context->klass.start(stream->instance);
+  stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_STARTED);
+
+  return CUBEB_OK;
+}
+
+int
+audiotrack_stream_stop(cubeb_stream * stream)
+{
+  assert(stream->instance);
+
+  stream->context->klass.pause(stream->instance);
+  stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_STOPPED);
+
+  return CUBEB_OK;
+}
+
+int
+audiotrack_stream_get_position(cubeb_stream * stream, uint64_t * position)
+{
+  uint32_t p;
+
+  assert(stream->instance && position);
+  stream->context->klass.get_position(stream->instance, &p);
+  *position = p;
+
+  return CUBEB_OK;
+}
+
+int
+audiotrack_stream_get_latency(cubeb_stream * stream, uint32_t * latency)
+{
+  assert(stream->instance && latency);
+
+  /* Android returns the latency in ms, we want it in frames. */
+  *latency = stream->context->klass.latency(stream->instance);
+  /* with rate <= 96000, we won't overflow until 44.739 seconds of latency */
+  *latency = (*latency * stream->params.rate) / 1000;
+
+  return 0;
+}
+
+int
+audiotrack_stream_set_volume(cubeb_stream * stream, float volume)
+{
+  status_t status;
+
+  status = stream->context->klass.set_volume(stream->instance, volume, volume);
+
+  if (status) {
+    return CUBEB_ERROR;
+  }
+
+  return CUBEB_OK;
+}
+
+static struct cubeb_ops const audiotrack_ops = {
+  .init = audiotrack_init,
+  .get_backend_id = audiotrack_get_backend_id,
+  .get_max_channel_count = audiotrack_get_max_channel_count,
+  .get_min_latency = audiotrack_get_min_latency,
+  .get_preferred_sample_rate = audiotrack_get_preferred_sample_rate,
+  .enumerate_devices = NULL,
+  .device_collection_destroy = NULL,
+  .destroy = audiotrack_destroy,
+  .stream_init = audiotrack_stream_init,
+  .stream_destroy = audiotrack_stream_destroy,
+  .stream_start = audiotrack_stream_start,
+  .stream_stop = audiotrack_stream_stop,
+  .stream_reset_default_device = NULL,
+  .stream_get_position = audiotrack_stream_get_position,
+  .stream_get_latency = audiotrack_stream_get_latency,
+  .stream_set_volume = audiotrack_stream_set_volume,
+  .stream_get_current_device = NULL,
+  .stream_device_destroy = NULL,
+  .stream_register_device_changed_callback = NULL,
+  .register_device_collection_changed = NULL
+};
diff --git a/dep/cubeb/src/cubeb_audiounit.cpp b/dep/cubeb/src/cubeb_audiounit.cpp
new file mode 100644
index 000000000..4e89a7d5a
--- /dev/null
+++ b/dep/cubeb/src/cubeb_audiounit.cpp
@@ -0,0 +1,3620 @@
+/*
+ * Copyright © 2011 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+#undef NDEBUG
+
+#include <TargetConditionals.h>
+#include <assert.h>
+#include <mach/mach_time.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <AudioUnit/AudioUnit.h>
+#if !TARGET_OS_IPHONE
+#include <AvailabilityMacros.h>
+#include <CoreAudio/AudioHardware.h>
+#include <CoreAudio/HostTime.h>
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+#include <CoreAudio/CoreAudioTypes.h>
+#include <AudioToolbox/AudioToolbox.h>
+#include "cubeb/cubeb.h"
+#include "cubeb-internal.h"
+#include "cubeb_mixer.h"
+#if !TARGET_OS_IPHONE
+#include "cubeb_osx_run_loop.h"
+#endif
+#include "cubeb_resampler.h"
+#include "cubeb_ring_array.h"
+#include <algorithm>
+#include <atomic>
+#include <vector>
+#include <set>
+#include <sys/time.h>
+#include <string>
+
+using namespace std;
+
+#if MAC_OS_X_VERSION_MIN_REQUIRED < 101000
+typedef UInt32 AudioFormatFlags;
+#endif
+
+#define AU_OUT_BUS    0
+#define AU_IN_BUS     1
+
+const char * DISPATCH_QUEUE_LABEL = "org.mozilla.cubeb";
+const char * PRIVATE_AGGREGATE_DEVICE_NAME = "CubebAggregateDevice";
+
+#ifdef ALOGV
+#undef ALOGV
+#endif
+#define ALOGV(msg, ...) dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{LOGV(msg, ##__VA_ARGS__);})
+
+#ifdef ALOG
+#undef ALOG
+#endif
+#define ALOG(msg, ...) dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{LOG(msg, ##__VA_ARGS__);})
+
+/* Testing empirically, some headsets report a minimal latency that is very
+ * low, but this does not work in practice. Lie and say the minimum is 256
+ * frames. */
+const uint32_t SAFE_MIN_LATENCY_FRAMES = 128;
+const uint32_t SAFE_MAX_LATENCY_FRAMES = 512;
+
+const AudioObjectPropertyAddress DEFAULT_INPUT_DEVICE_PROPERTY_ADDRESS = {
+  kAudioHardwarePropertyDefaultInputDevice,
+  kAudioObjectPropertyScopeGlobal,
+  kAudioObjectPropertyElementMaster
+};
+
+const AudioObjectPropertyAddress DEFAULT_OUTPUT_DEVICE_PROPERTY_ADDRESS = {
+  kAudioHardwarePropertyDefaultOutputDevice,
+  kAudioObjectPropertyScopeGlobal,
+  kAudioObjectPropertyElementMaster
+};
+
+const AudioObjectPropertyAddress DEVICE_IS_ALIVE_PROPERTY_ADDRESS = {
+  kAudioDevicePropertyDeviceIsAlive,
+  kAudioObjectPropertyScopeGlobal,
+  kAudioObjectPropertyElementMaster
+};
+
+const AudioObjectPropertyAddress DEVICES_PROPERTY_ADDRESS = {
+  kAudioHardwarePropertyDevices,
+  kAudioObjectPropertyScopeGlobal,
+  kAudioObjectPropertyElementMaster
+};
+
+const AudioObjectPropertyAddress INPUT_DATA_SOURCE_PROPERTY_ADDRESS = {
+  kAudioDevicePropertyDataSource,
+  kAudioDevicePropertyScopeInput,
+  kAudioObjectPropertyElementMaster
+};
+
+const AudioObjectPropertyAddress OUTPUT_DATA_SOURCE_PROPERTY_ADDRESS = {
+  kAudioDevicePropertyDataSource,
+  kAudioDevicePropertyScopeOutput,
+  kAudioObjectPropertyElementMaster
+};
+
+typedef uint32_t device_flags_value;
+
+enum device_flags {
+  DEV_UNKNOWN           = 0x00, /* Unknown */
+  DEV_INPUT             = 0x01, /* Record device like mic */
+  DEV_OUTPUT            = 0x02, /* Playback device like speakers */
+  DEV_SYSTEM_DEFAULT    = 0x04, /* System default device */
+  DEV_SELECTED_DEFAULT  = 0x08, /* User selected to use the system default device */
+};
+
+void audiounit_stream_stop_internal(cubeb_stream * stm);
+static int audiounit_stream_start_internal(cubeb_stream * stm);
+static void audiounit_close_stream(cubeb_stream *stm);
+static int audiounit_setup_stream(cubeb_stream *stm);
+static vector<AudioObjectID>
+audiounit_get_devices_of_type(cubeb_device_type devtype);
+static UInt32 audiounit_get_device_presentation_latency(AudioObjectID devid, AudioObjectPropertyScope scope);
+
+#if !TARGET_OS_IPHONE
+static AudioObjectID audiounit_get_default_device_id(cubeb_device_type type);
+static int audiounit_uninstall_device_changed_callback(cubeb_stream * stm);
+static int audiounit_uninstall_system_changed_callback(cubeb_stream * stm);
+static void audiounit_reinit_stream_async(cubeb_stream * stm, device_flags_value flags);
+#endif
+
+extern cubeb_ops const audiounit_ops;
+
+struct cubeb {
+  cubeb_ops const * ops = &audiounit_ops;
+  owned_critical_section mutex;
+  int active_streams = 0;
+  uint32_t global_latency_frames = 0;
+  cubeb_device_collection_changed_callback input_collection_changed_callback = nullptr;
+  void * input_collection_changed_user_ptr = nullptr;
+  cubeb_device_collection_changed_callback output_collection_changed_callback = nullptr;
+  void * output_collection_changed_user_ptr = nullptr;
+  // Store list of devices to detect changes
+  vector<AudioObjectID> input_device_array;
+  vector<AudioObjectID> output_device_array;
+  // The queue should be released when it’s no longer needed.
+  dispatch_queue_t serial_queue = dispatch_queue_create(DISPATCH_QUEUE_LABEL, DISPATCH_QUEUE_SERIAL);
+  // Current used channel layout
+  atomic<cubeb_channel_layout> layout{ CUBEB_LAYOUT_UNDEFINED };
+  uint32_t channels = 0;
+};
+
+static unique_ptr<AudioChannelLayout, decltype(&free)>
+make_sized_audio_channel_layout(size_t sz)
+{
+    assert(sz >= sizeof(AudioChannelLayout));
+    AudioChannelLayout * acl = reinterpret_cast<AudioChannelLayout *>(calloc(1, sz));
+    assert(acl); // Assert the allocation works.
+    return unique_ptr<AudioChannelLayout, decltype(&free)>(acl, free);
+}
+
+enum class io_side {
+  INPUT,
+  OUTPUT,
+};
+
+static char const *
+to_string(io_side side)
+{
+  switch (side) {
+  case io_side::INPUT:
+    return "input";
+  case io_side::OUTPUT:
+    return "output";
+  }
+}
+
+struct device_info {
+  AudioDeviceID id = kAudioObjectUnknown;
+  device_flags_value flags = DEV_UNKNOWN;
+};
+
+struct property_listener {
+  AudioDeviceID device_id;
+  const AudioObjectPropertyAddress * property_address;
+  AudioObjectPropertyListenerProc callback;
+  cubeb_stream * stream;
+
+  property_listener(AudioDeviceID id,
+                    const AudioObjectPropertyAddress * address,
+                    AudioObjectPropertyListenerProc proc,
+                    cubeb_stream * stm)
+    : device_id(id)
+    , property_address(address)
+    , callback(proc)
+    , stream(stm)
+  {}
+};
+
+struct cubeb_stream {
+  explicit cubeb_stream(cubeb * context);
+
+  /* Note: Must match cubeb_stream layout in cubeb.c. */
+  cubeb * context;
+  void * user_ptr = nullptr;
+  /**/
+
+  cubeb_data_callback data_callback = nullptr;
+  cubeb_state_callback state_callback = nullptr;
+  cubeb_device_changed_callback device_changed_callback = nullptr;
+  owned_critical_section device_changed_callback_lock;
+  /* Stream creation parameters */
+  cubeb_stream_params input_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE };
+  cubeb_stream_params output_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE };
+  device_info input_device;
+  device_info output_device;
+  /* Format descriptions */
+  AudioStreamBasicDescription input_desc;
+  AudioStreamBasicDescription output_desc;
+  /* I/O AudioUnits */
+  AudioUnit input_unit = nullptr;
+  AudioUnit output_unit = nullptr;
+  /* I/O device sample rate */
+  Float64 input_hw_rate = 0;
+  Float64 output_hw_rate = 0;
+  /* Expected I/O thread interleave,
+   * calculated from I/O hw rate. */
+  int expected_output_callbacks_in_a_row = 0;
+  owned_critical_section mutex;
+  // Hold the input samples in every input callback iteration.
+  // Only accessed on input/output callback thread and during initial configure.
+  unique_ptr<auto_array_wrapper> input_linear_buffer;
+  /* Frame counters */
+  atomic<uint64_t> frames_played{ 0 };
+  uint64_t frames_queued = 0;
+  // How many frames got read from the input since the stream started (includes
+  // padded silence)
+  atomic<int64_t> frames_read{ 0 };
+  // How many frames got written to the output device since the stream started
+  atomic<int64_t> frames_written{ 0 };
+  atomic<bool> shutdown{ true };
+  atomic<bool> draining{ false };
+  atomic<bool> reinit_pending { false };
+  atomic<bool> destroy_pending{ false };
+  /* Latency requested by the user. */
+  uint32_t latency_frames = 0;
+  atomic<uint32_t> current_latency_frames{ 0 };
+  atomic<uint32_t> total_output_latency_frames { 0 };
+  unique_ptr<cubeb_resampler, decltype(&cubeb_resampler_destroy)> resampler;
+  /* This is true if a device change callback is currently running.  */
+  atomic<bool> switching_device{ false };
+  atomic<bool> buffer_size_change_state{ false };
+  AudioDeviceID aggregate_device_id = kAudioObjectUnknown;  // the aggregate device id
+  AudioObjectID plugin_id = kAudioObjectUnknown;            // used to create aggregate device
+  /* Mixer interface */
+  unique_ptr<cubeb_mixer, decltype(&cubeb_mixer_destroy)> mixer;
+  /* Buffer where remixing/resampling will occur when upmixing is required */
+  /* Only accessed from callback thread */
+  unique_ptr<uint8_t[]> temp_buffer;
+  size_t temp_buffer_size = 0; // size in bytes.
+  /* Listeners indicating what system events are monitored. */
+  unique_ptr<property_listener> default_input_listener;
+  unique_ptr<property_listener> default_output_listener;
+  unique_ptr<property_listener> input_alive_listener;
+  unique_ptr<property_listener> input_source_listener;
+  unique_ptr<property_listener> output_source_listener;
+};
+
+bool has_input(cubeb_stream * stm)
+{
+  return stm->input_stream_params.rate != 0;
+}
+
+bool has_output(cubeb_stream * stm)
+{
+  return stm->output_stream_params.rate != 0;
+}
+
+cubeb_channel
+channel_label_to_cubeb_channel(UInt32 label)
+{
+  switch (label) {
+    case kAudioChannelLabel_Left:
+      return CHANNEL_FRONT_LEFT;
+    case kAudioChannelLabel_Right:
+      return CHANNEL_FRONT_RIGHT;
+    case kAudioChannelLabel_Center:
+      return CHANNEL_FRONT_CENTER;
+    case kAudioChannelLabel_LFEScreen:
+      return CHANNEL_LOW_FREQUENCY;
+    case kAudioChannelLabel_LeftSurround:
+      return CHANNEL_BACK_LEFT;
+    case kAudioChannelLabel_RightSurround:
+      return CHANNEL_BACK_RIGHT;
+    case kAudioChannelLabel_LeftCenter:
+      return CHANNEL_FRONT_LEFT_OF_CENTER;
+    case kAudioChannelLabel_RightCenter:
+      return CHANNEL_FRONT_RIGHT_OF_CENTER;
+    case kAudioChannelLabel_CenterSurround:
+      return CHANNEL_BACK_CENTER;
+    case kAudioChannelLabel_LeftSurroundDirect:
+      return CHANNEL_SIDE_LEFT;
+    case kAudioChannelLabel_RightSurroundDirect:
+      return CHANNEL_SIDE_RIGHT;
+    case kAudioChannelLabel_TopCenterSurround:
+      return CHANNEL_TOP_CENTER;
+    case kAudioChannelLabel_VerticalHeightLeft:
+      return CHANNEL_TOP_FRONT_LEFT;
+    case kAudioChannelLabel_VerticalHeightCenter:
+      return CHANNEL_TOP_FRONT_CENTER;
+    case kAudioChannelLabel_VerticalHeightRight:
+      return CHANNEL_TOP_FRONT_RIGHT;
+    case kAudioChannelLabel_TopBackLeft:
+      return CHANNEL_TOP_BACK_LEFT;
+    case kAudioChannelLabel_TopBackCenter:
+      return CHANNEL_TOP_BACK_CENTER;
+    case kAudioChannelLabel_TopBackRight:
+      return CHANNEL_TOP_BACK_RIGHT;
+    default:
+      return CHANNEL_UNKNOWN;
+  }
+}
+
+AudioChannelLabel
+cubeb_channel_to_channel_label(cubeb_channel channel)
+{
+  switch (channel) {
+    case CHANNEL_FRONT_LEFT:
+      return kAudioChannelLabel_Left;
+    case CHANNEL_FRONT_RIGHT:
+      return kAudioChannelLabel_Right;
+    case CHANNEL_FRONT_CENTER:
+      return kAudioChannelLabel_Center;
+    case CHANNEL_LOW_FREQUENCY:
+      return kAudioChannelLabel_LFEScreen;
+    case CHANNEL_BACK_LEFT:
+      return kAudioChannelLabel_LeftSurround;
+    case CHANNEL_BACK_RIGHT:
+      return kAudioChannelLabel_RightSurround;
+    case CHANNEL_FRONT_LEFT_OF_CENTER:
+      return kAudioChannelLabel_LeftCenter;
+    case CHANNEL_FRONT_RIGHT_OF_CENTER:
+      return kAudioChannelLabel_RightCenter;
+    case CHANNEL_BACK_CENTER:
+      return kAudioChannelLabel_CenterSurround;
+    case CHANNEL_SIDE_LEFT:
+      return kAudioChannelLabel_LeftSurroundDirect;
+    case CHANNEL_SIDE_RIGHT:
+      return kAudioChannelLabel_RightSurroundDirect;
+    case CHANNEL_TOP_CENTER:
+      return kAudioChannelLabel_TopCenterSurround;
+    case CHANNEL_TOP_FRONT_LEFT:
+      return kAudioChannelLabel_VerticalHeightLeft;
+    case CHANNEL_TOP_FRONT_CENTER:
+      return kAudioChannelLabel_VerticalHeightCenter;
+    case CHANNEL_TOP_FRONT_RIGHT:
+      return kAudioChannelLabel_VerticalHeightRight;
+    case CHANNEL_TOP_BACK_LEFT:
+      return kAudioChannelLabel_TopBackLeft;
+    case CHANNEL_TOP_BACK_CENTER:
+      return kAudioChannelLabel_TopBackCenter;
+    case CHANNEL_TOP_BACK_RIGHT:
+      return kAudioChannelLabel_TopBackRight;
+    default:
+      return kAudioChannelLabel_Unknown;
+  }
+}
+
+#if TARGET_OS_IPHONE
+typedef UInt32 AudioDeviceID;
+typedef UInt32 AudioObjectID;
+
+#define AudioGetCurrentHostTime mach_absolute_time
+
+#endif
+
+uint64_t
+ConvertHostTimeToNanos(uint64_t host_time)
+{
+  static struct mach_timebase_info timebase_info;
+  static bool initialized = false;
+  if (!initialized) {
+    mach_timebase_info(&timebase_info);
+    initialized = true;
+  }
+
+  long double answer = host_time;
+  if (timebase_info.numer != timebase_info.denom) {
+    answer *= timebase_info.numer;
+    answer /= timebase_info.denom;
+  }
+  return (uint64_t)answer;
+}
+
+static void
+audiounit_increment_active_streams(cubeb * ctx)
+{
+  ctx->mutex.assert_current_thread_owns();
+  ctx->active_streams += 1;
+}
+
+static void
+audiounit_decrement_active_streams(cubeb * ctx)
+{
+  ctx->mutex.assert_current_thread_owns();
+  ctx->active_streams -= 1;
+}
+
+static int
+audiounit_active_streams(cubeb * ctx)
+{
+  ctx->mutex.assert_current_thread_owns();
+  return ctx->active_streams;
+}
+
+static void
+audiounit_set_global_latency(cubeb * ctx, uint32_t latency_frames)
+{
+  ctx->mutex.assert_current_thread_owns();
+  assert(audiounit_active_streams(ctx) == 1);
+  ctx->global_latency_frames = latency_frames;
+}
+
+static void
+audiounit_make_silent(AudioBuffer * ioData)
+{
+  assert(ioData);
+  assert(ioData->mData);
+  memset(ioData->mData, 0, ioData->mDataByteSize);
+}
+
+static OSStatus
+audiounit_render_input(cubeb_stream * stm,
+                       AudioUnitRenderActionFlags * flags,
+                       AudioTimeStamp const * tstamp,
+                       UInt32 bus,
+                       UInt32 input_frames)
+{
+  /* Create the AudioBufferList to store input. */
+  AudioBufferList input_buffer_list;
+  input_buffer_list.mBuffers[0].mDataByteSize =
+      stm->input_desc.mBytesPerFrame * input_frames;
+  input_buffer_list.mBuffers[0].mData = nullptr;
+  input_buffer_list.mBuffers[0].mNumberChannels = stm->input_desc.mChannelsPerFrame;
+  input_buffer_list.mNumberBuffers = 1;
+
+  /* Render input samples */
+  OSStatus r = AudioUnitRender(stm->input_unit,
+                               flags,
+                               tstamp,
+                               bus,
+                               input_frames,
+                               &input_buffer_list);
+
+  if (r != noErr) {
+    LOG("AudioUnitRender rv=%d", r);
+    if (r != kAudioUnitErr_CannotDoInCurrentContext) {
+      return r;
+    }
+    if (stm->output_unit) {
+      // kAudioUnitErr_CannotDoInCurrentContext is returned when using a BT
+      // headset and the profile is changed from A2DP to HFP/HSP. The previous
+      // output device is no longer valid and must be reset.
+      audiounit_reinit_stream_async(stm, DEV_INPUT | DEV_OUTPUT);
+    }
+    // For now state that no error occurred and feed silence, stream will be
+    // resumed once reinit has completed.
+    ALOGV("(%p) input: reinit pending feeding silence instead", stm);
+    stm->input_linear_buffer->push_silence(input_frames * stm->input_desc.mChannelsPerFrame);
+  } else {
+    /* Copy input data in linear buffer. */
+    stm->input_linear_buffer->push(input_buffer_list.mBuffers[0].mData,
+                                   input_frames * stm->input_desc.mChannelsPerFrame);
+  }
+
+  /* Advance input frame counter. */
+  assert(input_frames > 0);
+  stm->frames_read += input_frames;
+
+  ALOGV("(%p) input: buffers %u, size %u, channels %u, rendered frames %d, total frames %lu.",
+        stm,
+        (unsigned int) input_buffer_list.mNumberBuffers,
+        (unsigned int) input_buffer_list.mBuffers[0].mDataByteSize,
+        (unsigned int) input_buffer_list.mBuffers[0].mNumberChannels,
+        (unsigned int) input_frames,
+        stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame);
+
+  return noErr;
+}
+
+static OSStatus
+audiounit_input_callback(void * user_ptr,
+                         AudioUnitRenderActionFlags * flags,
+                         AudioTimeStamp const * tstamp,
+                         UInt32 bus,
+                         UInt32 input_frames,
+                         AudioBufferList * /* bufs */)
+{
+  cubeb_stream * stm = static_cast<cubeb_stream *>(user_ptr);
+
+  assert(stm->input_unit != NULL);
+  assert(AU_IN_BUS == bus);
+
+  if (stm->shutdown) {
+    ALOG("(%p) input shutdown", stm);
+    return noErr;
+  }
+
+  if (stm->draining) {
+    OSStatus r = AudioOutputUnitStop(stm->input_unit);
+    assert(r == 0);
+    // Only fire state callback in input-only stream. For duplex stream,
+    // the state callback will be fired in output callback.
+    if (stm->output_unit == NULL) {
+      stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
+    }
+    return noErr;
+  }
+
+  OSStatus r = audiounit_render_input(stm, flags, tstamp, bus, input_frames);
+  if (r != noErr) {
+    return r;
+  }
+
+  // Full Duplex. We'll call data_callback in the AudioUnit output callback.
+  if (stm->output_unit != NULL) {
+    return noErr;
+  }
+
+  /* Input only. Call the user callback through resampler.
+     Resampler will deliver input buffer in the correct rate. */
+  assert(input_frames <= stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame);
+  long total_input_frames = stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame;
+  long outframes = cubeb_resampler_fill(stm->resampler.get(),
+                                        stm->input_linear_buffer->data(),
+                                        &total_input_frames,
+                                        NULL,
+                                        0);
+  stm->draining = outframes < total_input_frames;
+
+  // Reset input buffer
+  stm->input_linear_buffer->clear();
+
+  return noErr;
+}
+
+static void
+audiounit_mix_output_buffer(cubeb_stream * stm,
+                            size_t output_frames,
+                            void * input_buffer,
+                            size_t input_buffer_size,
+                            void * output_buffer,
+                            size_t output_buffer_size)
+{
+  assert(input_buffer_size >=
+         cubeb_sample_size(stm->output_stream_params.format) *
+           stm->output_stream_params.channels * output_frames);
+  assert(output_buffer_size >= stm->output_desc.mBytesPerFrame * output_frames);
+
+  int r = cubeb_mixer_mix(stm->mixer.get(),
+                          output_frames,
+                          input_buffer,
+                          input_buffer_size,
+                          output_buffer,
+                          output_buffer_size);
+  if (r != 0) {
+    LOG("Remix error = %d", r);
+  }
+}
+
+// Return how many input frames (sampled at input_hw_rate) are needed to provide
+// output_frames (sampled at output_stream_params.rate)
+static int64_t
+minimum_resampling_input_frames(cubeb_stream * stm, uint32_t output_frames)
+{
+  if (stm->input_hw_rate == stm->output_stream_params.rate) {
+    // Fast path.
+    return output_frames;
+  }
+  return ceil(stm->input_hw_rate * output_frames /
+              stm->output_stream_params.rate);
+}
+
+static OSStatus
+audiounit_output_callback(void * user_ptr,
+                          AudioUnitRenderActionFlags * /* flags */,
+                          AudioTimeStamp const * tstamp,
+                          UInt32 bus,
+                          UInt32 output_frames,
+                          AudioBufferList * outBufferList)
+{
+  assert(AU_OUT_BUS == bus);
+  assert(outBufferList->mNumberBuffers == 1);
+
+  cubeb_stream * stm = static_cast<cubeb_stream *>(user_ptr);
+
+  uint64_t now = ConvertHostTimeToNanos(mach_absolute_time());
+  uint64_t audio_output_time = ConvertHostTimeToNanos(tstamp->mHostTime);
+  uint64_t output_latency_ns = audio_output_time - now;
+
+  const int ns2s = 1e9;
+  // The total output latency is the timestamp difference + the stream latency +
+  // the hardware latency.
+  stm->total_output_latency_frames = output_latency_ns * stm->output_hw_rate / ns2s + stm->current_latency_frames;
+
+  ALOGV("(%p) output: buffers %u, size %u, channels %u, frames %u, total input frames %lu.",
+        stm,
+        (unsigned int) outBufferList->mNumberBuffers,
+        (unsigned int) outBufferList->mBuffers[0].mDataByteSize,
+        (unsigned int) outBufferList->mBuffers[0].mNumberChannels,
+        (unsigned int) output_frames,
+        has_input(stm) ? stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame : 0);
+
+  long input_frames = 0;
+  void * output_buffer = NULL, * input_buffer = NULL;
+
+  if (stm->shutdown) {
+    ALOG("(%p) output shutdown.", stm);
+    audiounit_make_silent(&outBufferList->mBuffers[0]);
+    return noErr;
+  }
+
+  if (stm->draining) {
+    OSStatus r = AudioOutputUnitStop(stm->output_unit);
+    assert(r == 0);
+    stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
+    audiounit_make_silent(&outBufferList->mBuffers[0]);
+    return noErr;
+  }
+
+  /* Get output buffer. */
+  if (stm->mixer) {
+    // If remixing needs to occur, we can't directly work in our final
+    // destination buffer as data may be overwritten or too small to start with.
+    size_t size_needed = output_frames * stm->output_stream_params.channels *
+                         cubeb_sample_size(stm->output_stream_params.format);
+    if (stm->temp_buffer_size < size_needed) {
+      stm->temp_buffer.reset(new uint8_t[size_needed]);
+      stm->temp_buffer_size = size_needed;
+    }
+    output_buffer = stm->temp_buffer.get();
+  } else {
+    output_buffer = outBufferList->mBuffers[0].mData;
+  }
+
+  stm->frames_written += output_frames;
+
+  /* If Full duplex get also input buffer */
+  if (stm->input_unit != NULL) {
+    /* If the output callback came first and this is a duplex stream, we need to
+     * fill in some additional silence in the resampler.
+     * Otherwise, if we had more than expected callbacks in a row, or we're
+     * currently switching, we add some silence as well to compensate for the
+     * fact that we're lacking some input data. */
+    uint32_t input_frames_needed =
+      minimum_resampling_input_frames(stm, stm->frames_written);
+    long missing_frames = input_frames_needed - stm->frames_read;
+    if (missing_frames > 0) {
+      stm->input_linear_buffer->push_silence(missing_frames * stm->input_desc.mChannelsPerFrame);
+      stm->frames_read = input_frames_needed;
+
+      ALOG("(%p) %s pushed %ld frames of input silence.", stm, stm->frames_read == 0 ? "Input hasn't started," :
+           stm->switching_device ? "Device switching," : "Drop out,", missing_frames);
+    }
+    input_buffer = stm->input_linear_buffer->data();
+    // Number of input frames in the buffer. It will change to actually used frames
+    // inside fill
+    input_frames = stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame;
+  }
+
+  /* Call user callback through resampler. */
+  long outframes = cubeb_resampler_fill(stm->resampler.get(),
+                                        input_buffer,
+                                        input_buffer ? &input_frames : NULL,
+                                        output_buffer,
+                                        output_frames);
+
+  if (input_buffer) {
+    // Pop from the buffer the frames used by the the resampler.
+    stm->input_linear_buffer->pop(input_frames * stm->input_desc.mChannelsPerFrame);
+  }
+
+  if (outframes < 0 || outframes > output_frames) {
+    stm->shutdown = true;
+    OSStatus r = AudioOutputUnitStop(stm->output_unit);
+    assert(r == 0);
+    if (stm->input_unit) {
+      r = AudioOutputUnitStop(stm->input_unit);
+      assert(r == 0);
+    }
+    stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
+    audiounit_make_silent(&outBufferList->mBuffers[0]);
+    return noErr;
+  }
+
+  stm->draining = (UInt32) outframes < output_frames;
+  stm->frames_played = stm->frames_queued;
+  stm->frames_queued += outframes;
+
+  /* Post process output samples. */
+  if (stm->draining) {
+    /* Clear missing frames (silence) */
+    size_t channels = stm->output_stream_params.channels;
+    size_t missing_samples = (output_frames - outframes) * channels;
+    size_t size_sample = cubeb_sample_size(stm->output_stream_params.format);
+    /* number of bytes that have been filled with valid audio by the callback. */
+    size_t audio_byte_count = outframes * channels * size_sample;
+    PodZero((uint8_t*)output_buffer + audio_byte_count,
+            missing_samples * size_sample);
+  }
+
+  /* Mixing */
+  if (stm->mixer) {
+    audiounit_mix_output_buffer(stm,
+                                output_frames,
+                                output_buffer,
+                                stm->temp_buffer_size,
+                                outBufferList->mBuffers[0].mData,
+                                outBufferList->mBuffers[0].mDataByteSize);
+  }
+
+  return noErr;
+}
+
+extern "C" {
+int
+audiounit_init(cubeb ** context, char const * /* context_name */)
+{
+#if !TARGET_OS_IPHONE
+  cubeb_set_coreaudio_notification_runloop();
+#endif
+
+  *context = new cubeb;
+
+  return CUBEB_OK;
+}
+}
+
+static char const *
+audiounit_get_backend_id(cubeb * /* ctx */)
+{
+  return "audiounit";
+}
+
+#if !TARGET_OS_IPHONE
+
+static int audiounit_stream_get_volume(cubeb_stream * stm, float * volume);
+static int audiounit_stream_set_volume(cubeb_stream * stm, float volume);
+
+static int
+audiounit_set_device_info(cubeb_stream * stm, AudioDeviceID id, io_side side)
+{
+  assert(stm);
+
+  device_info * info = nullptr;
+  cubeb_device_type type = CUBEB_DEVICE_TYPE_UNKNOWN;
+
+  if (side == io_side::INPUT) {
+    info = &stm->input_device;
+    type = CUBEB_DEVICE_TYPE_INPUT;
+  } else if (side == io_side::OUTPUT) {
+    info = &stm->output_device;
+    type = CUBEB_DEVICE_TYPE_OUTPUT;
+  }
+  memset(info, 0, sizeof(device_info));
+  info->id = id;
+
+  if (side == io_side::INPUT) {
+    info->flags |= DEV_INPUT;
+  } else if (side == io_side::OUTPUT) {
+    info->flags |= DEV_OUTPUT;
+  }
+
+  AudioDeviceID default_device_id = audiounit_get_default_device_id(type);
+  if (default_device_id == kAudioObjectUnknown) {
+    return CUBEB_ERROR;
+  }
+  if (id == kAudioObjectUnknown) {
+    info->id = default_device_id;
+    info->flags |= DEV_SELECTED_DEFAULT;
+  }
+
+  if (info->id == default_device_id) {
+    info->flags |= DEV_SYSTEM_DEFAULT;
+  }
+
+  assert(info->id);
+  assert(info->flags & DEV_INPUT && !(info->flags & DEV_OUTPUT) ||
+           !(info->flags & DEV_INPUT) && info->flags & DEV_OUTPUT);
+
+  return CUBEB_OK;
+}
+
+
+static int
+audiounit_reinit_stream(cubeb_stream * stm, device_flags_value flags)
+{
+  auto_lock context_lock(stm->context->mutex);
+  assert((flags & DEV_INPUT && stm->input_unit) ||
+         (flags & DEV_OUTPUT && stm->output_unit));
+  if (!stm->shutdown) {
+    audiounit_stream_stop_internal(stm);
+  }
+
+  int r = audiounit_uninstall_device_changed_callback(stm);
+  if (r != CUBEB_OK) {
+    LOG("(%p) Could not uninstall all device change listeners.", stm);
+  }
+
+  {
+    auto_lock lock(stm->mutex);
+    float volume = 0.0;
+    int vol_rv = CUBEB_ERROR;
+    if (stm->output_unit) {
+      vol_rv = audiounit_stream_get_volume(stm, &volume);
+    }
+
+    audiounit_close_stream(stm);
+
+    /* Reinit occurs in one of the following case:
+     * - When the device is not alive any more
+     * - When the default system device change.
+     * - The bluetooth device changed from A2DP to/from HFP/HSP profile
+     * We first attempt to re-use the same device id, should that fail we will
+     * default to the (potentially new) default device. */
+    AudioDeviceID input_device = flags & DEV_INPUT ? stm->input_device.id : kAudioObjectUnknown;
+    if (flags & DEV_INPUT) {
+      r = audiounit_set_device_info(stm, input_device, io_side::INPUT);
+      if (r != CUBEB_OK) {
+        LOG("(%p) Set input device info failed. This can happen when last media device is unplugged", stm);
+        return CUBEB_ERROR;
+      }
+    }
+
+    /* Always use the default output on reinit. This is not correct in every
+     * case but it is sufficient for Firefox and prevent reinit from reporting
+     * failures. It will change soon when reinit mechanism will be updated. */
+    r = audiounit_set_device_info(stm, kAudioObjectUnknown, io_side::OUTPUT);
+    if (r != CUBEB_OK) {
+      LOG("(%p) Set output device info failed. This can happen when last media device is unplugged", stm);
+      return CUBEB_ERROR;
+    }
+
+    if (audiounit_setup_stream(stm) != CUBEB_OK) {
+      LOG("(%p) Stream reinit failed.", stm);
+      if (flags & DEV_INPUT && input_device != kAudioObjectUnknown) {
+        // Attempt to re-use the same device-id failed, so attempt again with
+        // default input device.
+        audiounit_close_stream(stm);
+        if (audiounit_set_device_info(stm, kAudioObjectUnknown, io_side::INPUT) != CUBEB_OK ||
+            audiounit_setup_stream(stm) != CUBEB_OK) {
+          LOG("(%p) Second stream reinit failed.", stm);
+          return CUBEB_ERROR;
+        }
+      }
+    }
+
+    if (vol_rv == CUBEB_OK) {
+      audiounit_stream_set_volume(stm, volume);
+    }
+
+    // If the stream was running, start it again.
+    if (!stm->shutdown) {
+      r = audiounit_stream_start_internal(stm);
+      if (r != CUBEB_OK) {
+        return CUBEB_ERROR;
+      }
+    }
+  }
+  return CUBEB_OK;
+}
+
+static void
+audiounit_reinit_stream_async(cubeb_stream * stm, device_flags_value flags)
+{
+  if (std::atomic_exchange(&stm->reinit_pending, true)) {
+    // A reinit task is already pending, nothing more to do.
+    ALOG("(%p) re-init stream task already pending, cancelling request", stm);
+    return;
+  }
+
+  // Use a new thread, through the queue, to avoid deadlock when calling
+  // Get/SetProperties method from inside notify callback
+  dispatch_async(stm->context->serial_queue, ^() {
+    if (stm->destroy_pending) {
+      ALOG("(%p) stream pending destroy, cancelling reinit task", stm);
+      return;
+    }
+
+    if (audiounit_reinit_stream(stm, flags) != CUBEB_OK) {
+      if (audiounit_uninstall_system_changed_callback(stm) != CUBEB_OK) {
+        LOG("(%p) Could not uninstall system changed callback", stm);
+      }
+      stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
+      LOG("(%p) Could not reopen the stream after switching.", stm);
+    }
+    stm->switching_device = false;
+    stm->reinit_pending = false;
+  });
+}
+
+static char const *
+event_addr_to_string(AudioObjectPropertySelector selector)
+{
+  switch(selector) {
+    case kAudioHardwarePropertyDefaultOutputDevice:
+      return "kAudioHardwarePropertyDefaultOutputDevice";
+    case kAudioHardwarePropertyDefaultInputDevice:
+      return "kAudioHardwarePropertyDefaultInputDevice";
+    case kAudioDevicePropertyDeviceIsAlive:
+      return "kAudioDevicePropertyDeviceIsAlive";
+    case kAudioDevicePropertyDataSource:
+      return "kAudioDevicePropertyDataSource";
+    default:
+      return "Unknown";
+  }
+}
+
+static OSStatus
+audiounit_property_listener_callback(AudioObjectID id, UInt32 address_count,
+                                     const AudioObjectPropertyAddress * addresses,
+                                     void * user)
+{
+  cubeb_stream * stm = (cubeb_stream*) user;
+  if (stm->switching_device) {
+    LOG("Switching is already taking place. Skip Event %s for id=%d", event_addr_to_string(addresses[0].mSelector), id);
+    return noErr;
+  }
+  stm->switching_device = true;
+
+  LOG("(%p) Audio device changed, %u events.", stm, (unsigned int) address_count);
+  for (UInt32 i = 0; i < address_count; i++) {
+    switch(addresses[i].mSelector) {
+      case kAudioHardwarePropertyDefaultOutputDevice: {
+          LOG("Event[%u] - mSelector == kAudioHardwarePropertyDefaultOutputDevice for id=%d", (unsigned int) i, id);
+        }
+        break;
+      case kAudioHardwarePropertyDefaultInputDevice: {
+          LOG("Event[%u] - mSelector == kAudioHardwarePropertyDefaultInputDevice for id=%d", (unsigned int) i, id);
+        }
+      break;
+      case kAudioDevicePropertyDeviceIsAlive: {
+          LOG("Event[%u] - mSelector == kAudioDevicePropertyDeviceIsAlive for id=%d", (unsigned int) i, id);
+          // If this is the default input device ignore the event,
+          // kAudioHardwarePropertyDefaultInputDevice will take care of the switch
+          if (stm->input_device.flags & DEV_SYSTEM_DEFAULT) {
+            LOG("It's the default input device, ignore the event");
+            stm->switching_device = false;
+            return noErr;
+          }
+        }
+        break;
+      case kAudioDevicePropertyDataSource: {
+          LOG("Event[%u] - mSelector == kAudioDevicePropertyDataSource for id=%d", (unsigned int) i, id);
+        }
+        break;
+      default:
+        LOG("Event[%u] - mSelector == Unexpected Event id %d, return", (unsigned int) i, addresses[i].mSelector);
+        stm->switching_device = false;
+        return noErr;
+    }
+  }
+
+  // Allow restart to choose the new default
+  device_flags_value switch_side = DEV_UNKNOWN;
+  if (has_input(stm)) {
+    switch_side |= DEV_INPUT;
+  }
+  if (has_output(stm)) {
+    switch_side |= DEV_OUTPUT;
+  }
+
+  for (UInt32 i = 0; i < address_count; i++) {
+    switch(addresses[i].mSelector) {
+    case kAudioHardwarePropertyDefaultOutputDevice:
+    case kAudioHardwarePropertyDefaultInputDevice:
+    case kAudioDevicePropertyDeviceIsAlive:
+      /* fall through */
+    case kAudioDevicePropertyDataSource: {
+        auto_lock dev_cb_lock(stm->device_changed_callback_lock);
+        if (stm->device_changed_callback) {
+          stm->device_changed_callback(stm->user_ptr);
+        }
+        break;
+      }
+    }
+  }
+
+  audiounit_reinit_stream_async(stm, switch_side);
+
+  return noErr;
+}
+
+OSStatus
+audiounit_add_listener(const property_listener * listener)
+{
+  assert(listener);
+  return AudioObjectAddPropertyListener(listener->device_id,
+                                        listener->property_address,
+                                        listener->callback,
+                                        listener->stream);
+}
+
+OSStatus
+audiounit_remove_listener(const property_listener * listener)
+{
+  assert(listener);
+  return AudioObjectRemovePropertyListener(listener->device_id,
+                                           listener->property_address,
+                                           listener->callback,
+                                           listener->stream);
+}
+
+static int
+audiounit_install_device_changed_callback(cubeb_stream * stm)
+{
+  OSStatus rv;
+  int r = CUBEB_OK;
+
+  if (stm->output_unit) {
+    /* This event will notify us when the data source on the same device changes,
+     * for example when the user plugs in a normal (non-usb) headset in the
+     * headphone jack. */
+    stm->output_source_listener.reset(new property_listener(
+      stm->output_device.id, &OUTPUT_DATA_SOURCE_PROPERTY_ADDRESS,
+      &audiounit_property_listener_callback, stm));
+    rv = audiounit_add_listener(stm->output_source_listener.get());
+    if (rv != noErr) {
+      stm->output_source_listener.reset();
+      LOG("AudioObjectAddPropertyListener/output/kAudioDevicePropertyDataSource rv=%d, device id=%d", rv, stm->output_device.id);
+      r = CUBEB_ERROR;
+    }
+  }
+
+  if (stm->input_unit) {
+    /* This event will notify us when the data source on the input device changes. */
+    stm->input_source_listener.reset(new property_listener(
+      stm->input_device.id, &INPUT_DATA_SOURCE_PROPERTY_ADDRESS,
+      &audiounit_property_listener_callback, stm));
+    rv = audiounit_add_listener(stm->input_source_listener.get());
+    if (rv != noErr) {
+      stm->input_source_listener.reset();
+      LOG("AudioObjectAddPropertyListener/input/kAudioDevicePropertyDataSource rv=%d, device id=%d", rv, stm->input_device.id);
+      r = CUBEB_ERROR;
+    }
+
+    /* Event to notify when the input is going away. */
+    stm->input_alive_listener.reset(new property_listener(
+      stm->input_device.id, &DEVICE_IS_ALIVE_PROPERTY_ADDRESS,
+      &audiounit_property_listener_callback, stm));
+    rv = audiounit_add_listener(stm->input_alive_listener.get());
+    if (rv != noErr) {
+      stm->input_alive_listener.reset();
+      LOG("AudioObjectAddPropertyListener/input/kAudioDevicePropertyDeviceIsAlive rv=%d, device id =%d", rv, stm->input_device.id);
+      r = CUBEB_ERROR;
+    }
+  }
+
+  return r;
+}
+
+static int
+audiounit_install_system_changed_callback(cubeb_stream * stm)
+{
+  OSStatus r;
+
+  if (stm->output_unit) {
+    /* This event will notify us when the default audio device changes,
+     * for example when the user plugs in a USB headset and the system chooses it
+     * automatically as the default, or when another device is chosen in the
+     * dropdown list. */
+    stm->default_output_listener.reset(new property_listener(
+      kAudioObjectSystemObject, &DEFAULT_OUTPUT_DEVICE_PROPERTY_ADDRESS,
+      &audiounit_property_listener_callback, stm));
+    r = audiounit_add_listener(stm->default_output_listener.get());
+    if (r != noErr) {
+      stm->default_output_listener.reset();
+      LOG("AudioObjectAddPropertyListener/output/kAudioHardwarePropertyDefaultOutputDevice rv=%d", r);
+      return CUBEB_ERROR;
+    }
+  }
+
+  if (stm->input_unit) {
+    /* This event will notify us when the default input device changes. */
+    stm->default_input_listener.reset(new property_listener(
+      kAudioObjectSystemObject, &DEFAULT_INPUT_DEVICE_PROPERTY_ADDRESS,
+      &audiounit_property_listener_callback, stm));
+    r = audiounit_add_listener(stm->default_input_listener.get());
+    if (r != noErr) {
+      stm->default_input_listener.reset();
+      LOG("AudioObjectAddPropertyListener/input/kAudioHardwarePropertyDefaultInputDevice rv=%d", r);
+      return CUBEB_ERROR;
+    }
+  }
+
+  return CUBEB_OK;
+}
+
+static int
+audiounit_uninstall_device_changed_callback(cubeb_stream * stm)
+{
+  OSStatus rv;
+  // Failing to uninstall listeners is not a fatal error.
+  int r = CUBEB_OK;
+
+  if (stm->output_source_listener) {
+    rv = audiounit_remove_listener(stm->output_source_listener.get());
+    if (rv != noErr) {
+      LOG("AudioObjectRemovePropertyListener/output/kAudioDevicePropertyDataSource rv=%d, device id=%d", rv, stm->output_device.id);
+      r = CUBEB_ERROR;
+    }
+    stm->output_source_listener.reset();
+  }
+
+  if (stm->input_source_listener) {
+    rv = audiounit_remove_listener(stm->input_source_listener.get());
+    if (rv != noErr) {
+      LOG("AudioObjectRemovePropertyListener/input/kAudioDevicePropertyDataSource rv=%d, device id=%d", rv, stm->input_device.id);
+      r = CUBEB_ERROR;
+    }
+    stm->input_source_listener.reset();
+  }
+
+  if (stm->input_alive_listener) {
+    rv = audiounit_remove_listener(stm->input_alive_listener.get());
+    if (rv != noErr) {
+      LOG("AudioObjectRemovePropertyListener/input/kAudioDevicePropertyDeviceIsAlive rv=%d, device id=%d", rv, stm->input_device.id);
+      r = CUBEB_ERROR;
+    }
+    stm->input_alive_listener.reset();
+  }
+
+  return r;
+}
+
+static int
+audiounit_uninstall_system_changed_callback(cubeb_stream * stm)
+{
+  OSStatus r;
+
+  if (stm->default_output_listener) {
+    r = audiounit_remove_listener(stm->default_output_listener.get());
+    if (r != noErr) {
+      return CUBEB_ERROR;
+    }
+    stm->default_output_listener.reset();
+  }
+
+  if (stm->default_input_listener) {
+    r = audiounit_remove_listener(stm->default_input_listener.get());
+    if (r != noErr) {
+      return CUBEB_ERROR;
+    }
+    stm->default_input_listener.reset();
+  }
+  return CUBEB_OK;
+}
+
+/* Get the acceptable buffer size (in frames) that this device can work with. */
+static int
+audiounit_get_acceptable_latency_range(AudioValueRange * latency_range)
+{
+  UInt32 size;
+  OSStatus r;
+  AudioDeviceID output_device_id;
+  AudioObjectPropertyAddress output_device_buffer_size_range = {
+    kAudioDevicePropertyBufferFrameSizeRange,
+    kAudioDevicePropertyScopeOutput,
+    kAudioObjectPropertyElementMaster
+  };
+
+  output_device_id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT);
+  if (output_device_id == kAudioObjectUnknown) {
+    LOG("Could not get default output device id.");
+    return CUBEB_ERROR;
+  }
+
+  /* Get the buffer size range this device supports */
+  size = sizeof(*latency_range);
+
+  r = AudioObjectGetPropertyData(output_device_id,
+                                 &output_device_buffer_size_range,
+                                 0,
+                                 NULL,
+                                 &size,
+                                 latency_range);
+  if (r != noErr) {
+    LOG("AudioObjectGetPropertyData/buffer size range rv=%d", r);
+    return CUBEB_ERROR;
+  }
+
+  return CUBEB_OK;
+}
+#endif /* !TARGET_OS_IPHONE */
+
+static AudioObjectID
+audiounit_get_default_device_id(cubeb_device_type type)
+{
+  const AudioObjectPropertyAddress * adr;
+  if (type == CUBEB_DEVICE_TYPE_OUTPUT) {
+    adr = &DEFAULT_OUTPUT_DEVICE_PROPERTY_ADDRESS;
+  } else if (type == CUBEB_DEVICE_TYPE_INPUT) {
+    adr = &DEFAULT_INPUT_DEVICE_PROPERTY_ADDRESS;
+  } else {
+    return kAudioObjectUnknown;
+  }
+
+  AudioDeviceID devid;
+  UInt32 size = sizeof(AudioDeviceID);
+  if (AudioObjectGetPropertyData(kAudioObjectSystemObject,
+                                 adr, 0, NULL, &size, &devid) != noErr) {
+    return kAudioObjectUnknown;
+  }
+
+  return devid;
+}
+
+int
+audiounit_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
+{
+#if TARGET_OS_IPHONE
+  //TODO: [[AVAudioSession sharedInstance] maximumOutputNumberOfChannels]
+  *max_channels = 2;
+#else
+  UInt32 size;
+  OSStatus r;
+  AudioDeviceID output_device_id;
+  AudioStreamBasicDescription stream_format;
+  AudioObjectPropertyAddress stream_format_address = {
+    kAudioDevicePropertyStreamFormat,
+    kAudioDevicePropertyScopeOutput,
+    kAudioObjectPropertyElementMaster
+  };
+
+  assert(ctx && max_channels);
+
+  output_device_id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT);
+  if (output_device_id == kAudioObjectUnknown) {
+    return CUBEB_ERROR;
+  }
+
+  size = sizeof(stream_format);
+
+  r = AudioObjectGetPropertyData(output_device_id,
+                                 &stream_format_address,
+                                 0,
+                                 NULL,
+                                 &size,
+                                 &stream_format);
+  if (r != noErr) {
+    LOG("AudioObjectPropertyAddress/StreamFormat rv=%d", r);
+    return CUBEB_ERROR;
+  }
+
+  *max_channels = stream_format.mChannelsPerFrame;
+#endif
+  return CUBEB_OK;
+}
+
+static int
+audiounit_get_min_latency(cubeb * /* ctx */,
+                          cubeb_stream_params /* params */,
+                          uint32_t * latency_frames)
+{
+#if TARGET_OS_IPHONE
+  //TODO: [[AVAudioSession sharedInstance] inputLatency]
+  return CUBEB_ERROR_NOT_SUPPORTED;
+#else
+  AudioValueRange latency_range;
+  if (audiounit_get_acceptable_latency_range(&latency_range) != CUBEB_OK) {
+    LOG("Could not get acceptable latency range.");
+    return CUBEB_ERROR;
+  }
+
+  *latency_frames = max<uint32_t>(latency_range.mMinimum,
+                                       SAFE_MIN_LATENCY_FRAMES);
+#endif
+
+  return CUBEB_OK;
+}
+
+static int
+audiounit_get_preferred_sample_rate(cubeb * /* ctx */, uint32_t * rate)
+{
+#if TARGET_OS_IPHONE
+  //TODO
+  return CUBEB_ERROR_NOT_SUPPORTED;
+#else
+  UInt32 size;
+  OSStatus r;
+  Float64 fsamplerate;
+  AudioDeviceID output_device_id;
+  AudioObjectPropertyAddress samplerate_address = {
+    kAudioDevicePropertyNominalSampleRate,
+    kAudioObjectPropertyScopeGlobal,
+    kAudioObjectPropertyElementMaster
+  };
+
+  output_device_id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT);
+  if (output_device_id == kAudioObjectUnknown) {
+    return CUBEB_ERROR;
+  }
+
+  size = sizeof(fsamplerate);
+  r = AudioObjectGetPropertyData(output_device_id,
+                                 &samplerate_address,
+                                 0,
+                                 NULL,
+                                 &size,
+                                 &fsamplerate);
+
+  if (r != noErr) {
+    return CUBEB_ERROR;
+  }
+
+  *rate = static_cast<uint32_t>(fsamplerate);
+#endif
+  return CUBEB_OK;
+}
+
+static cubeb_channel_layout
+audiounit_convert_channel_layout(AudioChannelLayout * layout)
+{
+  // When having one or two channel, force mono or stereo. Some devices (namely,
+  // Bose QC35, mark 1 and 2), expose a single channel mapped to the right for
+  // some reason.
+  if (layout->mNumberChannelDescriptions == 1) {
+    return CUBEB_LAYOUT_MONO;
+  } else if (layout->mNumberChannelDescriptions == 2) {
+    return CUBEB_LAYOUT_STEREO;
+  }
+
+  if (layout->mChannelLayoutTag != kAudioChannelLayoutTag_UseChannelDescriptions) {
+    // kAudioChannelLayoutTag_UseChannelBitmap
+    // kAudioChannelLayoutTag_Mono
+    // kAudioChannelLayoutTag_Stereo
+    // ....
+    LOG("Only handle UseChannelDescriptions for now.\n");
+    return CUBEB_LAYOUT_UNDEFINED;
+  }
+
+  cubeb_channel_layout cl = 0;
+  for (UInt32 i = 0; i < layout->mNumberChannelDescriptions; ++i) {
+    cubeb_channel cc = channel_label_to_cubeb_channel(
+      layout->mChannelDescriptions[i].mChannelLabel);
+    if (cc == CHANNEL_UNKNOWN) {
+      return CUBEB_LAYOUT_UNDEFINED;
+    }
+    cl |= cc;
+  }
+
+  return cl;
+}
+
+static cubeb_channel_layout
+audiounit_get_preferred_channel_layout(AudioUnit output_unit)
+{
+  OSStatus rv = noErr;
+  UInt32 size = 0;
+  rv = AudioUnitGetPropertyInfo(output_unit,
+                                kAudioDevicePropertyPreferredChannelLayout,
+                                kAudioUnitScope_Output,
+                                AU_OUT_BUS,
+                                &size,
+                                nullptr);
+  if (rv != noErr) {
+    LOG("AudioUnitGetPropertyInfo/kAudioDevicePropertyPreferredChannelLayout rv=%d", rv);
+    return CUBEB_LAYOUT_UNDEFINED;
+  }
+  assert(size > 0);
+
+  auto layout = make_sized_audio_channel_layout(size);
+  rv = AudioUnitGetProperty(output_unit,
+                            kAudioDevicePropertyPreferredChannelLayout,
+                            kAudioUnitScope_Output,
+                            AU_OUT_BUS,
+                            layout.get(),
+                            &size);
+  if (rv != noErr) {
+    LOG("AudioUnitGetProperty/kAudioDevicePropertyPreferredChannelLayout rv=%d", rv);
+    return CUBEB_LAYOUT_UNDEFINED;
+  }
+
+  return audiounit_convert_channel_layout(layout.get());
+}
+
+static cubeb_channel_layout
+audiounit_get_current_channel_layout(AudioUnit output_unit)
+{
+  OSStatus rv = noErr;
+  UInt32 size = 0;
+  rv = AudioUnitGetPropertyInfo(output_unit,
+                                kAudioUnitProperty_AudioChannelLayout,
+                                kAudioUnitScope_Output,
+                                AU_OUT_BUS,
+                                &size,
+                                nullptr);
+  if (rv != noErr) {
+    LOG("AudioUnitGetPropertyInfo/kAudioUnitProperty_AudioChannelLayout rv=%d", rv);
+    // This property isn't known before macOS 10.12, attempt another method.
+    return audiounit_get_preferred_channel_layout(output_unit);
+  }
+  assert(size > 0);
+
+  auto layout = make_sized_audio_channel_layout(size);
+  rv = AudioUnitGetProperty(output_unit,
+                            kAudioUnitProperty_AudioChannelLayout,
+                            kAudioUnitScope_Output,
+                            AU_OUT_BUS,
+                            layout.get(),
+                            &size);
+  if (rv != noErr) {
+    LOG("AudioUnitGetProperty/kAudioUnitProperty_AudioChannelLayout rv=%d", rv);
+    return CUBEB_LAYOUT_UNDEFINED;
+  }
+
+  return audiounit_convert_channel_layout(layout.get());
+}
+
+static int audiounit_create_unit(AudioUnit * unit, device_info * device);
+
+static OSStatus audiounit_remove_device_listener(cubeb * context, cubeb_device_type devtype);
+
+static void
+audiounit_destroy(cubeb * ctx)
+{
+  {
+    auto_lock lock(ctx->mutex);
+
+    // Disabling this assert for bug 1083664 -- we seem to leak a stream
+    // assert(ctx->active_streams == 0);
+    if (audiounit_active_streams(ctx) > 0) {
+      LOG("(%p) API misuse, %d streams active when context destroyed!", ctx, audiounit_active_streams(ctx));
+    }
+
+    /* Unregister the callback if necessary. */
+    if (ctx->input_collection_changed_callback) {
+      audiounit_remove_device_listener(ctx, CUBEB_DEVICE_TYPE_INPUT);
+    }
+    if (ctx->output_collection_changed_callback) {
+      audiounit_remove_device_listener(ctx, CUBEB_DEVICE_TYPE_OUTPUT);
+    }
+  }
+
+  dispatch_release(ctx->serial_queue);
+
+  delete ctx;
+}
+
+static void audiounit_stream_destroy(cubeb_stream * stm);
+
+static int
+audio_stream_desc_init(AudioStreamBasicDescription * ss,
+                       const cubeb_stream_params * stream_params)
+{
+  switch (stream_params->format) {
+  case CUBEB_SAMPLE_S16LE:
+    ss->mBitsPerChannel = 16;
+    ss->mFormatFlags = kAudioFormatFlagIsSignedInteger;
+    break;
+  case CUBEB_SAMPLE_S16BE:
+    ss->mBitsPerChannel = 16;
+    ss->mFormatFlags = kAudioFormatFlagIsSignedInteger |
+      kAudioFormatFlagIsBigEndian;
+    break;
+  case CUBEB_SAMPLE_FLOAT32LE:
+    ss->mBitsPerChannel = 32;
+    ss->mFormatFlags = kAudioFormatFlagIsFloat;
+    break;
+  case CUBEB_SAMPLE_FLOAT32BE:
+    ss->mBitsPerChannel = 32;
+    ss->mFormatFlags = kAudioFormatFlagIsFloat |
+      kAudioFormatFlagIsBigEndian;
+    break;
+  default:
+    return CUBEB_ERROR_INVALID_FORMAT;
+  }
+
+  ss->mFormatID = kAudioFormatLinearPCM;
+  ss->mFormatFlags |= kLinearPCMFormatFlagIsPacked;
+  ss->mSampleRate = stream_params->rate;
+  ss->mChannelsPerFrame = stream_params->channels;
+
+  ss->mBytesPerFrame = (ss->mBitsPerChannel / 8) * ss->mChannelsPerFrame;
+  ss->mFramesPerPacket = 1;
+  ss->mBytesPerPacket = ss->mBytesPerFrame * ss->mFramesPerPacket;
+
+  ss->mReserved = 0;
+
+  return CUBEB_OK;
+}
+
+void
+audiounit_init_mixer(cubeb_stream * stm)
+{
+  // We can't rely on macOS' AudioUnit to properly downmix (or upmix) the audio
+  // data, it silently drop the channels so we need to remix the
+  // audio data by ourselves to keep all the information.
+  stm->mixer.reset(cubeb_mixer_create(stm->output_stream_params.format,
+                                      stm->output_stream_params.channels,
+                                      stm->output_stream_params.layout,
+                                      stm->context->channels,
+                                      stm->context->layout));
+  assert(stm->mixer);
+}
+
+static int
+audiounit_set_channel_layout(AudioUnit unit,
+                             io_side side,
+                             cubeb_channel_layout layout)
+{
+  if (side != io_side::OUTPUT) {
+    return CUBEB_ERROR;
+  }
+
+  if (layout == CUBEB_LAYOUT_UNDEFINED) {
+    // We leave everything as-is...
+    return CUBEB_OK;
+  }
+
+
+  OSStatus r;
+  uint32_t nb_channels = cubeb_channel_layout_nb_channels(layout);
+
+  // We do not use CoreAudio standard layout for lack of documentation on what
+  // the actual channel orders are. So we set a custom layout.
+  size_t size = offsetof(AudioChannelLayout, mChannelDescriptions[nb_channels]);
+  auto au_layout = make_sized_audio_channel_layout(size);
+  au_layout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
+  au_layout->mNumberChannelDescriptions = nb_channels;
+
+  uint32_t channels = 0;
+  cubeb_channel_layout channelMap = layout;
+  for (uint32_t i = 0; channelMap != 0; ++i) {
+    XASSERT(channels < nb_channels);
+    uint32_t channel = (channelMap & 1) << i;
+    if (channel != 0) {
+      au_layout->mChannelDescriptions[channels].mChannelLabel =
+        cubeb_channel_to_channel_label(static_cast<cubeb_channel>(channel));
+      au_layout->mChannelDescriptions[channels].mChannelFlags = kAudioChannelFlags_AllOff;
+      channels++;
+    }
+    channelMap = channelMap >> 1;
+  }
+
+  r = AudioUnitSetProperty(unit,
+                           kAudioUnitProperty_AudioChannelLayout,
+                           kAudioUnitScope_Input,
+                           AU_OUT_BUS,
+                           au_layout.get(),
+                           size);
+  if (r != noErr) {
+    LOG("AudioUnitSetProperty/%s/kAudioUnitProperty_AudioChannelLayout rv=%d", to_string(side), r);
+    return CUBEB_ERROR;
+  }
+
+  return CUBEB_OK;
+}
+
+void
+audiounit_layout_init(cubeb_stream * stm, io_side side)
+{
+  // We currently don't support the input layout setting.
+  if (side == io_side::INPUT) {
+    return;
+  }
+
+  stm->context->layout = audiounit_get_current_channel_layout(stm->output_unit);
+
+  audiounit_set_channel_layout(stm->output_unit, io_side::OUTPUT, stm->context->layout);
+}
+
+static vector<AudioObjectID>
+audiounit_get_sub_devices(AudioDeviceID device_id)
+{
+  vector<AudioDeviceID> sub_devices;
+  AudioObjectPropertyAddress property_address = { kAudioAggregateDevicePropertyActiveSubDeviceList,
+                                                  kAudioObjectPropertyScopeGlobal,
+                                                  kAudioObjectPropertyElementMaster };
+  UInt32 size = 0;
+  OSStatus rv = AudioObjectGetPropertyDataSize(device_id,
+                                               &property_address,
+                                               0,
+                                               nullptr,
+                                               &size);
+
+  if (rv != noErr) {
+    sub_devices.push_back(device_id);
+    return sub_devices;
+  }
+
+  uint32_t count = static_cast<uint32_t>(size / sizeof(AudioObjectID));
+  sub_devices.resize(count);
+  rv = AudioObjectGetPropertyData(device_id,
+                                  &property_address,
+                                  0,
+                                  nullptr,
+                                  &size,
+                                  sub_devices.data());
+  if (rv != noErr) {
+    sub_devices.clear();
+    sub_devices.push_back(device_id);
+  } else {
+    LOG("Found %u sub-devices", count);
+  }
+  return sub_devices;
+}
+
+static int
+audiounit_create_blank_aggregate_device(AudioObjectID * plugin_id, AudioDeviceID * aggregate_device_id)
+{
+  AudioObjectPropertyAddress address_plugin_bundle_id = { kAudioHardwarePropertyPlugInForBundleID,
+                                                          kAudioObjectPropertyScopeGlobal,
+                                                          kAudioObjectPropertyElementMaster };
+  UInt32 size = 0;
+  OSStatus r = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
+                                              &address_plugin_bundle_id,
+                                              0, NULL,
+                                              &size);
+  if (r != noErr) {
+    LOG("AudioObjectGetPropertyDataSize/kAudioHardwarePropertyPlugInForBundleID, rv=%d", r);
+    return CUBEB_ERROR;
+  }
+
+  AudioValueTranslation translation_value;
+  CFStringRef in_bundle_ref = CFSTR("com.apple.audio.CoreAudio");
+  translation_value.mInputData = &in_bundle_ref;
+  translation_value.mInputDataSize = sizeof(in_bundle_ref);
+  translation_value.mOutputData = plugin_id;
+  translation_value.mOutputDataSize = sizeof(*plugin_id);
+
+  r = AudioObjectGetPropertyData(kAudioObjectSystemObject,
+                                 &address_plugin_bundle_id,
+                                 0,
+                                 nullptr,
+                                 &size,
+                                 &translation_value);
+  if (r != noErr) {
+    LOG("AudioObjectGetPropertyData/kAudioHardwarePropertyPlugInForBundleID, rv=%d", r);
+    return CUBEB_ERROR;
+  }
+
+  AudioObjectPropertyAddress create_aggregate_device_address = { kAudioPlugInCreateAggregateDevice,
+                                                                 kAudioObjectPropertyScopeGlobal,
+                                                                 kAudioObjectPropertyElementMaster };
+  r = AudioObjectGetPropertyDataSize(*plugin_id,
+                                     &create_aggregate_device_address,
+                                     0,
+                                     nullptr,
+                                     &size);
+  if (r != noErr) {
+    LOG("AudioObjectGetPropertyDataSize/kAudioPlugInCreateAggregateDevice, rv=%d", r);
+    return CUBEB_ERROR;
+  }
+
+  CFMutableDictionaryRef aggregate_device_dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+                                                                           &kCFTypeDictionaryKeyCallBacks,
+                                                                           &kCFTypeDictionaryValueCallBacks);
+  struct timeval timestamp;
+  gettimeofday(&timestamp, NULL);
+  long long int time_id = timestamp.tv_sec * 1000000LL + timestamp.tv_usec;
+  CFStringRef aggregate_device_name = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s_%llx"), PRIVATE_AGGREGATE_DEVICE_NAME, time_id);
+  CFDictionaryAddValue(aggregate_device_dict, CFSTR(kAudioAggregateDeviceNameKey), aggregate_device_name);
+  CFRelease(aggregate_device_name);
+
+  CFStringRef aggregate_device_UID = CFStringCreateWithFormat(NULL, NULL, CFSTR("org.mozilla.%s_%llx"), PRIVATE_AGGREGATE_DEVICE_NAME, time_id);
+  CFDictionaryAddValue(aggregate_device_dict, CFSTR(kAudioAggregateDeviceUIDKey), aggregate_device_UID);
+  CFRelease(aggregate_device_UID);
+
+  int private_value = 1;
+  CFNumberRef aggregate_device_private_key = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &private_value);
+  CFDictionaryAddValue(aggregate_device_dict, CFSTR(kAudioAggregateDeviceIsPrivateKey), aggregate_device_private_key);
+  CFRelease(aggregate_device_private_key);
+
+  int stacked_value = 0;
+  CFNumberRef aggregate_device_stacked_key = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &stacked_value);
+  CFDictionaryAddValue(aggregate_device_dict, CFSTR(kAudioAggregateDeviceIsStackedKey), aggregate_device_stacked_key);
+  CFRelease(aggregate_device_stacked_key);
+
+  r = AudioObjectGetPropertyData(*plugin_id,
+                                 &create_aggregate_device_address,
+                                 sizeof(aggregate_device_dict),
+                                 &aggregate_device_dict,
+                                 &size,
+                                 aggregate_device_id);
+  CFRelease(aggregate_device_dict);
+  if (r != noErr) {
+    LOG("AudioObjectGetPropertyData/kAudioPlugInCreateAggregateDevice, rv=%d", r);
+    return CUBEB_ERROR;
+  }
+  LOG("New aggregate device %u", *aggregate_device_id);
+
+  return CUBEB_OK;
+}
+
+// The returned CFStringRef object needs to be released (via CFRelease)
+// if it's not NULL, since the reference count of the returned CFStringRef
+// object is increased.
+static CFStringRef
+get_device_name(AudioDeviceID id)
+{
+  UInt32 size = sizeof(CFStringRef);
+  CFStringRef UIname = nullptr;
+  AudioObjectPropertyAddress address_uuid = { kAudioDevicePropertyDeviceUID,
+                                              kAudioObjectPropertyScopeGlobal,
+                                              kAudioObjectPropertyElementMaster };
+  OSStatus err = AudioObjectGetPropertyData(id, &address_uuid, 0, nullptr, &size, &UIname);
+  return (err == noErr) ? UIname : NULL;
+}
+
+static int
+audiounit_set_aggregate_sub_device_list(AudioDeviceID aggregate_device_id,
+                                        AudioDeviceID input_device_id,
+                                        AudioDeviceID output_device_id)
+{
+  LOG("Add devices input %u and output %u into aggregate device %u",
+      input_device_id, output_device_id, aggregate_device_id);
+  const vector<AudioDeviceID> output_sub_devices = audiounit_get_sub_devices(output_device_id);
+  const vector<AudioDeviceID> input_sub_devices = audiounit_get_sub_devices(input_device_id);
+
+  CFMutableArrayRef aggregate_sub_devices_array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+  /* The order of the items in the array is significant and is used to determine the order of the streams
+     of the AudioAggregateDevice. */
+  for (UInt32 i = 0; i < output_sub_devices.size(); i++) {
+    CFStringRef ref = get_device_name(output_sub_devices[i]);
+    if (ref == NULL) {
+      CFRelease(aggregate_sub_devices_array);
+      return CUBEB_ERROR;
+    }
+    CFArrayAppendValue(aggregate_sub_devices_array, ref);
+    CFRelease(ref);
+  }
+  for (UInt32 i = 0; i < input_sub_devices.size(); i++) {
+    CFStringRef ref = get_device_name(input_sub_devices[i]);
+    if (ref == NULL) {
+      CFRelease(aggregate_sub_devices_array);
+      return CUBEB_ERROR;
+    }
+    CFArrayAppendValue(aggregate_sub_devices_array, ref);
+    CFRelease(ref);
+  }
+
+  AudioObjectPropertyAddress aggregate_sub_device_list = { kAudioAggregateDevicePropertyFullSubDeviceList,
+                                                           kAudioObjectPropertyScopeGlobal,
+                                                           kAudioObjectPropertyElementMaster };
+  UInt32 size = sizeof(CFMutableArrayRef);
+  OSStatus rv = AudioObjectSetPropertyData(aggregate_device_id,
+                                           &aggregate_sub_device_list,
+                                           0,
+                                           nullptr,
+                                           size,
+                                           &aggregate_sub_devices_array);
+  CFRelease(aggregate_sub_devices_array);
+  if (rv != noErr) {
+    LOG("AudioObjectSetPropertyData/kAudioAggregateDevicePropertyFullSubDeviceList, rv=%d", rv);
+    return CUBEB_ERROR;
+  }
+
+  return CUBEB_OK;
+}
+
+static int
+audiounit_set_master_aggregate_device(const AudioDeviceID aggregate_device_id)
+{
+  assert(aggregate_device_id != kAudioObjectUnknown);
+  AudioObjectPropertyAddress master_aggregate_sub_device =  { kAudioAggregateDevicePropertyMasterSubDevice,
+                                                              kAudioObjectPropertyScopeGlobal,
+                                                              kAudioObjectPropertyElementMaster };
+
+  // Master become the 1st output sub device
+  AudioDeviceID output_device_id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT);
+  const vector<AudioDeviceID> output_sub_devices = audiounit_get_sub_devices(output_device_id);
+  CFStringRef master_sub_device = get_device_name(output_sub_devices[0]);
+
+  UInt32 size = sizeof(CFStringRef);
+  OSStatus rv = AudioObjectSetPropertyData(aggregate_device_id,
+                                           &master_aggregate_sub_device,
+                                           0,
+                                           NULL,
+                                           size,
+                                           &master_sub_device);
+  if (master_sub_device) {
+    CFRelease(master_sub_device);
+  }
+  if (rv != noErr) {
+    LOG("AudioObjectSetPropertyData/kAudioAggregateDevicePropertyMasterSubDevice, rv=%d", rv);
+    return CUBEB_ERROR;
+  }
+
+  return CUBEB_OK;
+}
+
+static int
+audiounit_activate_clock_drift_compensation(const AudioDeviceID aggregate_device_id)
+{
+  assert(aggregate_device_id != kAudioObjectUnknown);
+  AudioObjectPropertyAddress address_owned = { kAudioObjectPropertyOwnedObjects,
+                                               kAudioObjectPropertyScopeGlobal,
+                                               kAudioObjectPropertyElementMaster };
+
+  UInt32 qualifier_data_size = sizeof(AudioObjectID);
+  AudioClassID class_id = kAudioSubDeviceClassID;
+  void * qualifier_data = &class_id;
+  UInt32 size = 0;
+  OSStatus rv = AudioObjectGetPropertyDataSize(aggregate_device_id,
+                                               &address_owned,
+                                               qualifier_data_size,
+                                               qualifier_data,
+                                               &size);
+  if (rv != noErr) {
+    LOG("AudioObjectGetPropertyDataSize/kAudioObjectPropertyOwnedObjects, rv=%d", rv);
+    return CUBEB_ERROR;
+  }
+
+  UInt32 subdevices_num = 0;
+  subdevices_num = size / sizeof(AudioObjectID);
+  AudioObjectID sub_devices[subdevices_num];
+  size = sizeof(sub_devices);
+
+  rv = AudioObjectGetPropertyData(aggregate_device_id,
+                                  &address_owned,
+                                  qualifier_data_size,
+                                  qualifier_data,
+                                  &size,
+                                  sub_devices);
+  if (rv != noErr) {
+    LOG("AudioObjectGetPropertyData/kAudioObjectPropertyOwnedObjects, rv=%d", rv);
+    return CUBEB_ERROR;
+  }
+
+  AudioObjectPropertyAddress address_drift = { kAudioSubDevicePropertyDriftCompensation,
+                                               kAudioObjectPropertyScopeGlobal,
+                                               kAudioObjectPropertyElementMaster };
+
+  // Start from the second device since the first is the master clock
+  for (UInt32 i = 1; i < subdevices_num; ++i) {
+    UInt32 drift_compensation_value = 1;
+    rv = AudioObjectSetPropertyData(sub_devices[i],
+                                    &address_drift,
+                                    0,
+                                    nullptr,
+                                    sizeof(UInt32),
+                                    &drift_compensation_value);
+    if (rv != noErr) {
+      LOG("AudioObjectSetPropertyData/kAudioSubDevicePropertyDriftCompensation, rv=%d", rv);
+      return CUBEB_OK;
+    }
+  }
+  return CUBEB_OK;
+}
+
+static int audiounit_destroy_aggregate_device(AudioObjectID plugin_id, AudioDeviceID * aggregate_device_id);
+static void audiounit_get_available_samplerate(AudioObjectID devid, AudioObjectPropertyScope scope,
+                                   uint32_t * min, uint32_t * max, uint32_t * def);
+static int
+audiounit_create_device_from_hwdev(cubeb_device_info * dev_info, AudioObjectID devid, cubeb_device_type type);
+static void audiounit_device_destroy(cubeb_device_info * device);
+
+static void
+audiounit_workaround_for_airpod(cubeb_stream * stm)
+{
+  cubeb_device_info input_device_info;
+  audiounit_create_device_from_hwdev(&input_device_info, stm->input_device.id, CUBEB_DEVICE_TYPE_INPUT);
+
+  cubeb_device_info output_device_info;
+  audiounit_create_device_from_hwdev(&output_device_info, stm->output_device.id, CUBEB_DEVICE_TYPE_OUTPUT);
+
+  std::string input_name_str(input_device_info.friendly_name);
+  std::string output_name_str(output_device_info.friendly_name);
+
+  if(input_name_str.find("AirPods") != std::string::npos &&
+     output_name_str.find("AirPods") != std::string::npos) {
+    uint32_t input_min_rate = 0;
+    uint32_t input_max_rate = 0;
+    uint32_t input_nominal_rate = 0;
+    audiounit_get_available_samplerate(stm->input_device.id, kAudioObjectPropertyScopeGlobal,
+                                       &input_min_rate, &input_max_rate, &input_nominal_rate);
+    LOG("(%p) Input device %u, name: %s, min: %u, max: %u, nominal rate: %u", stm, stm->input_device.id
+    , input_device_info.friendly_name, input_min_rate, input_max_rate, input_nominal_rate);
+    uint32_t output_min_rate = 0;
+    uint32_t output_max_rate = 0;
+    uint32_t output_nominal_rate = 0;
+    audiounit_get_available_samplerate(stm->output_device.id, kAudioObjectPropertyScopeGlobal,
+                                       &output_min_rate, &output_max_rate, &output_nominal_rate);
+    LOG("(%p) Output device %u, name: %s, min: %u, max: %u, nominal rate: %u", stm, stm->output_device.id
+    , output_device_info.friendly_name, output_min_rate, output_max_rate, output_nominal_rate);
+
+    Float64 rate = input_nominal_rate;
+    AudioObjectPropertyAddress addr = {kAudioDevicePropertyNominalSampleRate,
+                                       kAudioObjectPropertyScopeGlobal,
+                                       kAudioObjectPropertyElementMaster};
+
+    OSStatus rv = AudioObjectSetPropertyData(stm->aggregate_device_id,
+                                             &addr,
+                                             0,
+                                             nullptr,
+                                             sizeof(Float64),
+                                             &rate);
+    if (rv != noErr) {
+      LOG("Non fatal error, AudioObjectSetPropertyData/kAudioDevicePropertyNominalSampleRate, rv=%d", rv);
+    }
+  }
+  audiounit_device_destroy(&input_device_info);
+  audiounit_device_destroy(&output_device_info);
+}
+
+/*
+ * Aggregate Device is a virtual audio interface which utilizes inputs and outputs
+ * of one or more physical audio interfaces. It is possible to use the clock of
+ * one of the devices as a master clock for all the combined devices and enable
+ * drift compensation for the devices that are not designated clock master.
+ *
+ * Creating a new aggregate device programmatically requires [0][1]:
+ * 1. Locate the base plug-in ("com.apple.audio.CoreAudio")
+ * 2. Create a dictionary that describes the aggregate device
+ *    (don't add sub-devices in that step, prone to fail [0])
+ * 3. Ask the base plug-in to create the aggregate device (blank)
+ * 4. Add the array of sub-devices.
+ * 5. Set the master device (1st output device in our case)
+ * 6. Enable drift compensation for the non-master devices
+ *
+ * [0] https://lists.apple.com/archives/coreaudio-api/2006/Apr/msg00092.html
+ * [1] https://lists.apple.com/archives/coreaudio-api/2005/Jul/msg00150.html
+ * [2] CoreAudio.framework/Headers/AudioHardware.h
+ * */
+static int
+audiounit_create_aggregate_device(cubeb_stream * stm)
+{
+  int r = audiounit_create_blank_aggregate_device(&stm->plugin_id, &stm->aggregate_device_id);
+  if (r != CUBEB_OK) {
+    LOG("(%p) Failed to create blank aggregate device", stm);
+    return CUBEB_ERROR;
+  }
+
+  r = audiounit_set_aggregate_sub_device_list(stm->aggregate_device_id, stm->input_device.id, stm->output_device.id);
+  if (r != CUBEB_OK) {
+    LOG("(%p) Failed to set aggregate sub-device list", stm);
+    audiounit_destroy_aggregate_device(stm->plugin_id, &stm->aggregate_device_id);
+    return CUBEB_ERROR;
+  }
+
+  r = audiounit_set_master_aggregate_device(stm->aggregate_device_id);
+  if (r != CUBEB_OK) {
+    LOG("(%p) Failed to set master sub-device for aggregate device", stm);
+    audiounit_destroy_aggregate_device(stm->plugin_id, &stm->aggregate_device_id);
+    return  CUBEB_ERROR;
+  }
+
+  r = audiounit_activate_clock_drift_compensation(stm->aggregate_device_id);
+  if (r != CUBEB_OK) {
+    LOG("(%p) Failed to activate clock drift compensation for aggregate device", stm);
+    audiounit_destroy_aggregate_device(stm->plugin_id, &stm->aggregate_device_id);
+    return  CUBEB_ERROR;
+  }
+
+  audiounit_workaround_for_airpod(stm);
+
+  return CUBEB_OK;
+}
+
+static int
+audiounit_destroy_aggregate_device(AudioObjectID plugin_id, AudioDeviceID * aggregate_device_id)
+{
+  assert(aggregate_device_id &&
+         *aggregate_device_id != kAudioDeviceUnknown &&
+         plugin_id != kAudioObjectUnknown);
+  AudioObjectPropertyAddress destroy_aggregate_device_addr = { kAudioPlugInDestroyAggregateDevice,
+                                                               kAudioObjectPropertyScopeGlobal,
+                                                               kAudioObjectPropertyElementMaster};
+  UInt32 size;
+  OSStatus rv = AudioObjectGetPropertyDataSize(plugin_id,
+                                               &destroy_aggregate_device_addr,
+                                               0,
+                                               NULL,
+                                               &size);
+  if (rv != noErr) {
+    LOG("AudioObjectGetPropertyDataSize/kAudioPlugInDestroyAggregateDevice, rv=%d", rv);
+    return CUBEB_ERROR;
+  }
+
+  rv = AudioObjectGetPropertyData(plugin_id,
+                                  &destroy_aggregate_device_addr,
+                                  0,
+                                  NULL,
+                                  &size,
+                                  aggregate_device_id);
+  if (rv != noErr) {
+    LOG("AudioObjectGetPropertyData/kAudioPlugInDestroyAggregateDevice, rv=%d", rv);
+    return CUBEB_ERROR;
+  }
+
+  LOG("Destroyed aggregate device %d", *aggregate_device_id);
+  *aggregate_device_id = kAudioObjectUnknown;
+  return CUBEB_OK;
+}
+
+static int
+audiounit_new_unit_instance(AudioUnit * unit, device_info * device)
+{
+  AudioComponentDescription desc;
+  AudioComponent comp;
+  OSStatus rv;
+
+  desc.componentType = kAudioUnitType_Output;
+#if TARGET_OS_IPHONE
+  desc.componentSubType = kAudioUnitSubType_RemoteIO;
+#else
+  // Use the DefaultOutputUnit for output when no device is specified
+  // so we retain automatic output device switching when the default
+  // changes.  Once we have complete support for device notifications
+  // and switching, we can use the AUHAL for everything.
+  if ((device->flags & DEV_SYSTEM_DEFAULT) &&
+      (device->flags & DEV_OUTPUT)) {
+    desc.componentSubType = kAudioUnitSubType_DefaultOutput;
+  } else {
+    desc.componentSubType = kAudioUnitSubType_HALOutput;
+  }
+#endif
+  desc.componentManufacturer = kAudioUnitManufacturer_Apple;
+  desc.componentFlags = 0;
+  desc.componentFlagsMask = 0;
+  comp = AudioComponentFindNext(NULL, &desc);
+  if (comp == NULL) {
+    LOG("Could not find matching audio hardware.");
+    return CUBEB_ERROR;
+  }
+
+  rv = AudioComponentInstanceNew(comp, unit);
+  if (rv != noErr) {
+    LOG("AudioComponentInstanceNew rv=%d", rv);
+    return CUBEB_ERROR;
+  }
+  return CUBEB_OK;
+}
+
+enum enable_state {
+  DISABLE,
+  ENABLE,
+};
+
+static int
+audiounit_enable_unit_scope(AudioUnit * unit, io_side side, enable_state state)
+{
+  OSStatus rv;
+  UInt32 enable = state;
+  rv = AudioUnitSetProperty(*unit, kAudioOutputUnitProperty_EnableIO,
+                            (side == io_side::INPUT) ? kAudioUnitScope_Input : kAudioUnitScope_Output,
+                            (side == io_side::INPUT) ? AU_IN_BUS : AU_OUT_BUS,
+                            &enable,
+                            sizeof(UInt32));
+  if (rv != noErr) {
+    LOG("AudioUnitSetProperty/kAudioOutputUnitProperty_EnableIO rv=%d", rv);
+    return CUBEB_ERROR;
+  }
+  return CUBEB_OK;
+}
+
+static int
+audiounit_create_unit(AudioUnit * unit, device_info * device)
+{
+  assert(*unit == nullptr);
+  assert(device);
+
+  OSStatus rv;
+  int r;
+
+  r = audiounit_new_unit_instance(unit, device);
+  if (r != CUBEB_OK) {
+    return r;
+  }
+  assert(*unit);
+
+  if ((device->flags & DEV_SYSTEM_DEFAULT) &&
+      (device->flags & DEV_OUTPUT)) {
+    return CUBEB_OK;
+  }
+
+
+  if (device->flags & DEV_INPUT) {
+    r = audiounit_enable_unit_scope(unit, io_side::INPUT, ENABLE);
+    if (r != CUBEB_OK) {
+      LOG("Failed to enable audiounit input scope");
+      return r;
+    }
+    r = audiounit_enable_unit_scope(unit, io_side::OUTPUT, DISABLE);
+    if (r != CUBEB_OK) {
+      LOG("Failed to disable audiounit output scope");
+      return r;
+    }
+  } else if (device->flags & DEV_OUTPUT) {
+    r = audiounit_enable_unit_scope(unit, io_side::OUTPUT, ENABLE);
+    if (r != CUBEB_OK) {
+      LOG("Failed to enable audiounit output scope");
+      return r;
+    }
+    r = audiounit_enable_unit_scope(unit, io_side::INPUT, DISABLE);
+    if (r != CUBEB_OK) {
+      LOG("Failed to disable audiounit input scope");
+      return r;
+    }
+  } else {
+    assert(false);
+  }
+
+  rv = AudioUnitSetProperty(*unit,
+                            kAudioOutputUnitProperty_CurrentDevice,
+                            kAudioUnitScope_Global,
+                            0,
+                            &device->id, sizeof(AudioDeviceID));
+  if (rv != noErr) {
+    LOG("AudioUnitSetProperty/kAudioOutputUnitProperty_CurrentDevice rv=%d", rv);
+    return CUBEB_ERROR;
+  }
+
+  return CUBEB_OK;
+}
+
+static int
+audiounit_init_input_linear_buffer(cubeb_stream * stream, uint32_t capacity)
+{
+  uint32_t size = capacity * stream->latency_frames * stream->input_desc.mChannelsPerFrame;
+  if (stream->input_desc.mFormatFlags & kAudioFormatFlagIsSignedInteger) {
+    stream->input_linear_buffer.reset(new auto_array_wrapper_impl<short>(size));
+  } else {
+    stream->input_linear_buffer.reset(new auto_array_wrapper_impl<float>(size));
+  }
+  assert(stream->input_linear_buffer->length() == 0);
+
+  return CUBEB_OK;
+}
+
+static uint32_t
+audiounit_clamp_latency(cubeb_stream * stm, uint32_t latency_frames)
+{
+  // For the 1st stream set anything within safe min-max
+  assert(audiounit_active_streams(stm->context) > 0);
+  if (audiounit_active_streams(stm->context) == 1) {
+    return max(min<uint32_t>(latency_frames, SAFE_MAX_LATENCY_FRAMES),
+                    SAFE_MIN_LATENCY_FRAMES);
+  }
+  assert(stm->output_unit);
+
+  // If more than one stream operates in parallel
+  // allow only lower values of latency
+  int r;
+  UInt32 output_buffer_size = 0;
+  UInt32 size = sizeof(output_buffer_size);
+  if (stm->output_unit) {
+    r = AudioUnitGetProperty(stm->output_unit,
+                            kAudioDevicePropertyBufferFrameSize,
+                            kAudioUnitScope_Output,
+                            AU_OUT_BUS,
+                            &output_buffer_size,
+                            &size);
+    if (r != noErr) {
+      LOG("AudioUnitGetProperty/output/kAudioDevicePropertyBufferFrameSize rv=%d", r);
+      return 0;
+    }
+
+    output_buffer_size = max(min<uint32_t>(output_buffer_size, SAFE_MAX_LATENCY_FRAMES),
+                                  SAFE_MIN_LATENCY_FRAMES);
+  }
+
+  UInt32 input_buffer_size = 0;
+  if (stm->input_unit) {
+    r = AudioUnitGetProperty(stm->input_unit,
+                            kAudioDevicePropertyBufferFrameSize,
+                            kAudioUnitScope_Input,
+                            AU_IN_BUS,
+                            &input_buffer_size,
+                            &size);
+    if (r != noErr) {
+      LOG("AudioUnitGetProperty/input/kAudioDevicePropertyBufferFrameSize rv=%d", r);
+      return 0;
+    }
+
+    input_buffer_size = max(min<uint32_t>(input_buffer_size, SAFE_MAX_LATENCY_FRAMES),
+                                 SAFE_MIN_LATENCY_FRAMES);
+  }
+
+  // Every following active streams can only set smaller latency
+  UInt32 upper_latency_limit = 0;
+  if (input_buffer_size != 0 && output_buffer_size != 0) {
+    upper_latency_limit = min<uint32_t>(input_buffer_size, output_buffer_size);
+  } else if (input_buffer_size != 0) {
+    upper_latency_limit = input_buffer_size;
+  } else if (output_buffer_size != 0) {
+    upper_latency_limit = output_buffer_size;
+  } else {
+    upper_latency_limit = SAFE_MAX_LATENCY_FRAMES;
+  }
+
+  return max(min<uint32_t>(latency_frames, upper_latency_limit),
+                  SAFE_MIN_LATENCY_FRAMES);
+}
+
+/*
+ * Change buffer size is prone to deadlock thus we change it
+ * following the steps:
+ * - register a listener for the buffer size property
+ * - change the property
+ * - wait until the listener is executed
+ * - property has changed, remove the listener
+ * */
+static void
+buffer_size_changed_callback(void * inClientData,
+                             AudioUnit inUnit,
+                             AudioUnitPropertyID inPropertyID,
+                             AudioUnitScope inScope,
+                             AudioUnitElement inElement)
+{
+  cubeb_stream * stm = (cubeb_stream *)inClientData;
+
+  AudioUnit au = inUnit;
+  AudioUnitScope au_scope = kAudioUnitScope_Input;
+  AudioUnitElement au_element = inElement;
+  char const * au_type = "output";
+
+  if (AU_IN_BUS == inElement) {
+    au_scope = kAudioUnitScope_Output;
+    au_type = "input";
+  }
+
+  switch (inPropertyID) {
+
+    case kAudioDevicePropertyBufferFrameSize: {
+      if (inScope != au_scope) {
+        break;
+      }
+      UInt32 new_buffer_size;
+      UInt32 outSize = sizeof(UInt32);
+      OSStatus r = AudioUnitGetProperty(au,
+                                        kAudioDevicePropertyBufferFrameSize,
+                                        au_scope,
+                                        au_element,
+                                        &new_buffer_size,
+                                        &outSize);
+      if (r != noErr) {
+        LOG("(%p) Event: kAudioDevicePropertyBufferFrameSize: Cannot get current buffer size", stm);
+      } else {
+        LOG("(%p) Event: kAudioDevicePropertyBufferFrameSize: New %s buffer size = %d for scope %d", stm,
+            au_type, new_buffer_size, inScope);
+      }
+      stm->buffer_size_change_state = true;
+      break;
+    }
+  }
+}
+
+static int
+audiounit_set_buffer_size(cubeb_stream * stm, uint32_t new_size_frames, io_side side)
+{
+  AudioUnit au = stm->output_unit;
+  AudioUnitScope au_scope = kAudioUnitScope_Input;
+  AudioUnitElement au_element = AU_OUT_BUS;
+
+  if (side == io_side::INPUT) {
+    au = stm->input_unit;
+    au_scope = kAudioUnitScope_Output;
+    au_element = AU_IN_BUS;
+  }
+
+  uint32_t buffer_frames = 0;
+  UInt32 size = sizeof(buffer_frames);
+  int r = AudioUnitGetProperty(au,
+                               kAudioDevicePropertyBufferFrameSize,
+                               au_scope,
+                               au_element,
+                               &buffer_frames,
+                               &size);
+  if (r != noErr) {
+    LOG("AudioUnitGetProperty/%s/kAudioDevicePropertyBufferFrameSize rv=%d", to_string(side), r);
+    return CUBEB_ERROR;
+  }
+
+  if (new_size_frames == buffer_frames) {
+    LOG("(%p) No need to update %s buffer size already %u frames", stm, to_string(side), buffer_frames);
+    return CUBEB_OK;
+  }
+
+  r = AudioUnitAddPropertyListener(au,
+                                   kAudioDevicePropertyBufferFrameSize,
+                                   buffer_size_changed_callback,
+                                   stm);
+  if (r != noErr) {
+    LOG("AudioUnitAddPropertyListener/%s/kAudioDevicePropertyBufferFrameSize rv=%d", to_string(side), r);
+    return CUBEB_ERROR;
+  }
+
+  stm->buffer_size_change_state = false;
+
+  r = AudioUnitSetProperty(au,
+                           kAudioDevicePropertyBufferFrameSize,
+                           au_scope,
+                           au_element,
+                           &new_size_frames,
+                           sizeof(new_size_frames));
+  if (r != noErr) {
+    LOG("AudioUnitSetProperty/%s/kAudioDevicePropertyBufferFrameSize rv=%d", to_string(side), r);
+
+    r = AudioUnitRemovePropertyListenerWithUserData(au,
+                                                    kAudioDevicePropertyBufferFrameSize,
+                                                    buffer_size_changed_callback,
+                                                    stm);
+    if (r != noErr) {
+      LOG("AudioUnitAddPropertyListener/%s/kAudioDevicePropertyBufferFrameSize rv=%d", to_string(side), r);
+    }
+
+    return CUBEB_ERROR;
+  }
+
+  int count = 0;
+  while (!stm->buffer_size_change_state && count++ < 30) {
+    struct timespec req, rem;
+    req.tv_sec = 0;
+    req.tv_nsec = 100000000L; // 0.1 sec
+    if (nanosleep(&req , &rem) < 0 ) {
+      LOG("(%p) Warning: nanosleep call failed or interrupted. Remaining time %ld nano secs \n", stm, rem.tv_nsec);
+    }
+    LOG("(%p) audiounit_set_buffer_size : wait count = %d", stm, count);
+  }
+
+  r = AudioUnitRemovePropertyListenerWithUserData(au,
+                                                  kAudioDevicePropertyBufferFrameSize,
+                                                  buffer_size_changed_callback,
+                                                  stm);
+  if (r != noErr) {
+    LOG("AudioUnitAddPropertyListener/%s/kAudioDevicePropertyBufferFrameSize rv=%d", to_string(side), r);
+    return CUBEB_ERROR;
+  }
+
+  if (!stm->buffer_size_change_state && count >= 30) {
+    LOG("(%p) Error, did not get buffer size change callback ...", stm);
+    return CUBEB_ERROR;
+  }
+
+  LOG("(%p) %s buffer size changed to %u frames.", stm, to_string(side), new_size_frames);
+  return CUBEB_OK;
+}
+
+static int
+audiounit_configure_input(cubeb_stream * stm)
+{
+  assert(stm && stm->input_unit);
+
+  int r = 0;
+  UInt32 size;
+  AURenderCallbackStruct aurcbs_in;
+
+  LOG("(%p) Opening input side: rate %u, channels %u, format %d, latency in frames %u.",
+      stm, stm->input_stream_params.rate, stm->input_stream_params.channels,
+      stm->input_stream_params.format, stm->latency_frames);
+
+  /* Get input device sample rate. */
+  AudioStreamBasicDescription input_hw_desc;
+  size = sizeof(AudioStreamBasicDescription);
+  r = AudioUnitGetProperty(stm->input_unit,
+                           kAudioUnitProperty_StreamFormat,
+                           kAudioUnitScope_Input,
+                           AU_IN_BUS,
+                           &input_hw_desc,
+                           &size);
+  if (r != noErr) {
+    LOG("AudioUnitGetProperty/input/kAudioUnitProperty_StreamFormat rv=%d", r);
+    return CUBEB_ERROR;
+  }
+  stm->input_hw_rate = input_hw_desc.mSampleRate;
+  LOG("(%p) Input device sampling rate: %.2f", stm, stm->input_hw_rate);
+
+  /* Set format description according to the input params. */
+  r = audio_stream_desc_init(&stm->input_desc, &stm->input_stream_params);
+  if (r != CUBEB_OK) {
+    LOG("(%p) Setting format description for input failed.", stm);
+    return r;
+  }
+
+  // Use latency to set buffer size
+  r = audiounit_set_buffer_size(stm, stm->latency_frames, io_side::INPUT);
+  if (r != CUBEB_OK) {
+    LOG("(%p) Error in change input buffer size.", stm);
+    return CUBEB_ERROR;
+  }
+
+  AudioStreamBasicDescription src_desc = stm->input_desc;
+  /* Input AudioUnit must be configured with device's sample rate.
+     we will resample inside input callback. */
+  src_desc.mSampleRate = stm->input_hw_rate;
+
+  r = AudioUnitSetProperty(stm->input_unit,
+                           kAudioUnitProperty_StreamFormat,
+                           kAudioUnitScope_Output,
+                           AU_IN_BUS,
+                           &src_desc,
+                           sizeof(AudioStreamBasicDescription));
+  if (r != noErr) {
+    LOG("AudioUnitSetProperty/input/kAudioUnitProperty_StreamFormat rv=%d", r);
+    return CUBEB_ERROR;
+  }
+
+  /* Frames per buffer in the input callback. */
+  r = AudioUnitSetProperty(stm->input_unit,
+                           kAudioUnitProperty_MaximumFramesPerSlice,
+                           kAudioUnitScope_Global,
+                           AU_IN_BUS,
+                           &stm->latency_frames,
+                           sizeof(UInt32));
+  if (r != noErr) {
+    LOG("AudioUnitSetProperty/input/kAudioUnitProperty_MaximumFramesPerSlice rv=%d", r);
+    return CUBEB_ERROR;
+  }
+
+  // Input only capacity
+  unsigned int array_capacity = 1;
+  if (has_output(stm)) {
+    // Full-duplex increase capacity
+    array_capacity = 8;
+  }
+  if (audiounit_init_input_linear_buffer(stm, array_capacity) != CUBEB_OK) {
+    return CUBEB_ERROR;
+  }
+
+  aurcbs_in.inputProc = audiounit_input_callback;
+  aurcbs_in.inputProcRefCon = stm;
+
+  r = AudioUnitSetProperty(stm->input_unit,
+                           kAudioOutputUnitProperty_SetInputCallback,
+                           kAudioUnitScope_Global,
+                           AU_OUT_BUS,
+                           &aurcbs_in,
+                           sizeof(aurcbs_in));
+  if (r != noErr) {
+    LOG("AudioUnitSetProperty/input/kAudioOutputUnitProperty_SetInputCallback rv=%d", r);
+    return CUBEB_ERROR;
+  }
+
+  stm->frames_read = 0;
+
+  LOG("(%p) Input audiounit init successfully.", stm);
+
+  return CUBEB_OK;
+}
+
+static int
+audiounit_configure_output(cubeb_stream * stm)
+{
+  assert(stm && stm->output_unit);
+
+  int r;
+  AURenderCallbackStruct aurcbs_out;
+  UInt32 size;
+
+
+  LOG("(%p) Opening output side: rate %u, channels %u, format %d, latency in frames %u.",
+      stm, stm->output_stream_params.rate, stm->output_stream_params.channels,
+      stm->output_stream_params.format, stm->latency_frames);
+
+  r = audio_stream_desc_init(&stm->output_desc, &stm->output_stream_params);
+  if (r != CUBEB_OK) {
+    LOG("(%p) Could not initialize the audio stream description.", stm);
+    return r;
+  }
+
+  /* Get output device sample rate. */
+  AudioStreamBasicDescription output_hw_desc;
+  size = sizeof(AudioStreamBasicDescription);
+  memset(&output_hw_desc, 0, size);
+  r = AudioUnitGetProperty(stm->output_unit,
+                           kAudioUnitProperty_StreamFormat,
+                           kAudioUnitScope_Output,
+                           AU_OUT_BUS,
+                           &output_hw_desc,
+                           &size);
+  if (r != noErr) {
+    LOG("AudioUnitGetProperty/output/kAudioUnitProperty_StreamFormat rv=%d", r);
+    return CUBEB_ERROR;
+  }
+  stm->output_hw_rate = output_hw_desc.mSampleRate;
+  LOG("(%p) Output device sampling rate: %.2f", stm, output_hw_desc.mSampleRate);
+  stm->context->channels = output_hw_desc.mChannelsPerFrame;
+
+  // Set the input layout to match the output device layout.
+  audiounit_layout_init(stm, io_side::OUTPUT);
+  if (stm->context->channels != stm->output_stream_params.channels ||
+      stm->context->layout != stm->output_stream_params.layout) {
+    LOG("Incompatible channel layouts detected, setting up remixer");
+    audiounit_init_mixer(stm);
+    // We will be remixing the data before it reaches the output device.
+    // We need to adjust the number of channels and other
+    // AudioStreamDescription details.
+    stm->output_desc.mChannelsPerFrame = stm->context->channels;
+    stm->output_desc.mBytesPerFrame = (stm->output_desc.mBitsPerChannel / 8) *
+                                      stm->output_desc.mChannelsPerFrame;
+    stm->output_desc.mBytesPerPacket =
+      stm->output_desc.mBytesPerFrame * stm->output_desc.mFramesPerPacket;
+  } else {
+    stm->mixer = nullptr;
+  }
+
+  r = AudioUnitSetProperty(stm->output_unit,
+                           kAudioUnitProperty_StreamFormat,
+                           kAudioUnitScope_Input,
+                           AU_OUT_BUS,
+                           &stm->output_desc,
+                           sizeof(AudioStreamBasicDescription));
+  if (r != noErr) {
+    LOG("AudioUnitSetProperty/output/kAudioUnitProperty_StreamFormat rv=%d", r);
+    return CUBEB_ERROR;
+  }
+
+  r = audiounit_set_buffer_size(stm, stm->latency_frames, io_side::OUTPUT);
+  if (r != CUBEB_OK) {
+    LOG("(%p) Error in change output buffer size.", stm);
+    return CUBEB_ERROR;
+  }
+
+  /* Frames per buffer in the input callback. */
+  r = AudioUnitSetProperty(stm->output_unit,
+                           kAudioUnitProperty_MaximumFramesPerSlice,
+                           kAudioUnitScope_Global,
+                           AU_OUT_BUS,
+                           &stm->latency_frames,
+                           sizeof(UInt32));
+  if (r != noErr) {
+    LOG("AudioUnitSetProperty/output/kAudioUnitProperty_MaximumFramesPerSlice rv=%d", r);
+    return CUBEB_ERROR;
+  }
+
+  aurcbs_out.inputProc = audiounit_output_callback;
+  aurcbs_out.inputProcRefCon = stm;
+  r = AudioUnitSetProperty(stm->output_unit,
+                           kAudioUnitProperty_SetRenderCallback,
+                           kAudioUnitScope_Global,
+                           AU_OUT_BUS,
+                           &aurcbs_out,
+                           sizeof(aurcbs_out));
+  if (r != noErr) {
+    LOG("AudioUnitSetProperty/output/kAudioUnitProperty_SetRenderCallback rv=%d", r);
+    return CUBEB_ERROR;
+  }
+
+  stm->frames_written = 0;
+
+  LOG("(%p) Output audiounit init successfully.", stm);
+  return CUBEB_OK;
+}
+
+static int
+audiounit_setup_stream(cubeb_stream * stm)
+{
+  stm->mutex.assert_current_thread_owns();
+
+  if ((stm->input_stream_params.prefs & CUBEB_STREAM_PREF_LOOPBACK) ||
+      (stm->output_stream_params.prefs & CUBEB_STREAM_PREF_LOOPBACK)) {
+    LOG("(%p) Loopback not supported for audiounit.", stm);
+    return CUBEB_ERROR_NOT_SUPPORTED;
+  }
+
+  int r = 0;
+
+  device_info in_dev_info = stm->input_device;
+  device_info out_dev_info = stm->output_device;
+
+  if (has_input(stm) && has_output(stm) &&
+      stm->input_device.id != stm->output_device.id) {
+    r = audiounit_create_aggregate_device(stm);
+    if (r != CUBEB_OK) {
+      stm->aggregate_device_id = kAudioObjectUnknown;
+      LOG("(%p) Create aggregate devices failed.", stm);
+      // !!!NOTE: It is not necessary to return here. If it does not
+      // return it will fallback to the old implementation. The intention
+      // is to investigate how often it fails. I plan to remove
+      // it after a couple of weeks.
+      return r;
+    } else {
+      in_dev_info.id = out_dev_info.id = stm->aggregate_device_id;
+      in_dev_info.flags = DEV_INPUT;
+      out_dev_info.flags = DEV_OUTPUT;
+    }
+  }
+
+  if (has_input(stm)) {
+    r = audiounit_create_unit(&stm->input_unit, &in_dev_info);
+    if (r != CUBEB_OK) {
+      LOG("(%p) AudioUnit creation for input failed.", stm);
+      return r;
+    }
+  }
+
+  if (has_output(stm)) {
+    r = audiounit_create_unit(&stm->output_unit, &out_dev_info);
+    if (r != CUBEB_OK) {
+      LOG("(%p) AudioUnit creation for output failed.", stm);
+      return r;
+    }
+  }
+
+  /* Latency cannot change if another stream is operating in parallel. In this case
+   * latency is set to the other stream value. */
+  if (audiounit_active_streams(stm->context) > 1) {
+    LOG("(%p) More than one active stream, use global latency.", stm);
+    stm->latency_frames = stm->context->global_latency_frames;
+  } else {
+    /* Silently clamp the latency down to the platform default, because we
+     * synthetize the clock from the callbacks, and we want the clock to update
+     * often. */
+    stm->latency_frames = audiounit_clamp_latency(stm, stm->latency_frames);
+    assert(stm->latency_frames); // Ugly error check
+    audiounit_set_global_latency(stm->context, stm->latency_frames);
+  }
+
+  /* Configure I/O stream */
+  if (has_input(stm)) {
+    r = audiounit_configure_input(stm);
+    if (r != CUBEB_OK) {
+      LOG("(%p) Configure audiounit input failed.", stm);
+      return r;
+    }
+  }
+
+  if (has_output(stm)) {
+    r = audiounit_configure_output(stm);
+    if (r != CUBEB_OK) {
+      LOG("(%p) Configure audiounit output failed.", stm);
+      return r;
+    }
+  }
+
+  // Setting the latency doesn't work well for USB headsets (eg. plantronics).
+  // Keep the default latency for now.
+#if 0
+  buffer_size = latency;
+
+  /* Get the range of latency this particular device can work with, and clamp
+   * the requested latency to this acceptable range. */
+#if !TARGET_OS_IPHONE
+  if (audiounit_get_acceptable_latency_range(&latency_range) != CUBEB_OK) {
+    return CUBEB_ERROR;
+  }
+
+  if (buffer_size < (unsigned int) latency_range.mMinimum) {
+    buffer_size = (unsigned int) latency_range.mMinimum;
+  } else if (buffer_size > (unsigned int) latency_range.mMaximum) {
+    buffer_size = (unsigned int) latency_range.mMaximum;
+  }
+
+  /**
+   * Get the default buffer size. If our latency request is below the default,
+   * set it. Otherwise, use the default latency.
+   **/
+  size = sizeof(default_buffer_size);
+  if (AudioUnitGetProperty(stm->output_unit, kAudioDevicePropertyBufferFrameSize,
+        kAudioUnitScope_Output, 0, &default_buffer_size, &size) != 0) {
+    return CUBEB_ERROR;
+  }
+
+  if (buffer_size < default_buffer_size) {
+    /* Set the maximum number of frame that the render callback will ask for,
+     * effectively setting the latency of the stream. This is process-wide. */
+    if (AudioUnitSetProperty(stm->output_unit, kAudioDevicePropertyBufferFrameSize,
+          kAudioUnitScope_Output, 0, &buffer_size, sizeof(buffer_size)) != 0) {
+      return CUBEB_ERROR;
+    }
+  }
+#else  // TARGET_OS_IPHONE
+  //TODO: [[AVAudioSession sharedInstance] inputLatency]
+  // http://stackoverflow.com/questions/13157523/kaudiodevicepropertybufferframesize-replacement-for-ios
+#endif
+#endif
+
+  /* We use a resampler because input AudioUnit operates
+   * reliable only in the capture device sample rate.
+   * Resampler will convert it to the user sample rate
+   * and deliver it to the callback. */
+  uint32_t target_sample_rate;
+  if (has_input(stm)) {
+    target_sample_rate = stm->input_stream_params.rate;
+  } else {
+    assert(has_output(stm));
+    target_sample_rate = stm->output_stream_params.rate;
+  }
+
+  cubeb_stream_params input_unconverted_params;
+  if (has_input(stm)) {
+    input_unconverted_params = stm->input_stream_params;
+    /* Use the rate of the input device. */
+    input_unconverted_params.rate = stm->input_hw_rate;
+  }
+
+  /* Create resampler. Output params are unchanged
+   * because we do not need conversion on the output. */
+  stm->resampler.reset(cubeb_resampler_create(stm,
+                                              has_input(stm) ? &input_unconverted_params : NULL,
+                                              has_output(stm) ? &stm->output_stream_params : NULL,
+                                              target_sample_rate,
+                                              stm->data_callback,
+                                              stm->user_ptr,
+                                              CUBEB_RESAMPLER_QUALITY_DESKTOP));
+  if (!stm->resampler) {
+    LOG("(%p) Could not create resampler.", stm);
+    return CUBEB_ERROR;
+  }
+
+  if (stm->input_unit != NULL) {
+    r = AudioUnitInitialize(stm->input_unit);
+    if (r != noErr) {
+      LOG("AudioUnitInitialize/input rv=%d", r);
+      return CUBEB_ERROR;
+    }
+  }
+
+  if (stm->output_unit != NULL) {
+    r = AudioUnitInitialize(stm->output_unit);
+    if (r != noErr) {
+      LOG("AudioUnitInitialize/output rv=%d", r);
+      return CUBEB_ERROR;
+    }
+
+    stm->current_latency_frames = audiounit_get_device_presentation_latency(stm->output_device.id, kAudioDevicePropertyScopeOutput);
+
+    Float64 unit_s;
+    UInt32 size = sizeof(unit_s);
+    if (AudioUnitGetProperty(stm->output_unit, kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0, &unit_s, &size) == noErr) {
+      stm->current_latency_frames += static_cast<uint32_t>(unit_s * stm->output_desc.mSampleRate);
+    }
+  }
+
+  if (stm->input_unit && stm->output_unit) {
+    // According to the I/O hardware rate it is expected a specific pattern of callbacks
+    // for example is input is 44100 and output is 48000 we expected no more than 2
+    // out callback in a row.
+    stm->expected_output_callbacks_in_a_row = ceilf(stm->output_hw_rate / stm->input_hw_rate);
+  }
+
+  r = audiounit_install_device_changed_callback(stm);
+  if (r != CUBEB_OK) {
+    LOG("(%p) Could not install all device change callback.", stm);
+  }
+
+
+  return CUBEB_OK;
+}
+
+cubeb_stream::cubeb_stream(cubeb * context)
+  : context(context)
+  , resampler(nullptr, cubeb_resampler_destroy)
+  , mixer(nullptr, cubeb_mixer_destroy)
+{
+  PodZero(&input_desc, 1);
+  PodZero(&output_desc, 1);
+}
+
+static void audiounit_stream_destroy_internal(cubeb_stream * stm);
+
+static int
+audiounit_stream_init(cubeb * context,
+                      cubeb_stream ** stream,
+                      char const * /* stream_name */,
+                      cubeb_devid input_device,
+                      cubeb_stream_params * input_stream_params,
+                      cubeb_devid output_device,
+                      cubeb_stream_params * output_stream_params,
+                      unsigned int latency_frames,
+                      cubeb_data_callback data_callback,
+                      cubeb_state_callback state_callback,
+                      void * user_ptr)
+{
+  assert(context);
+  auto_lock context_lock(context->mutex);
+  audiounit_increment_active_streams(context);
+  unique_ptr<cubeb_stream, decltype(&audiounit_stream_destroy)> stm(new cubeb_stream(context),
+                                                                    audiounit_stream_destroy_internal);
+  int r;
+  *stream = NULL;
+  assert(latency_frames > 0);
+
+  /* These could be different in the future if we have both
+   * full-duplex stream and different devices for input vs output. */
+  stm->data_callback = data_callback;
+  stm->state_callback = state_callback;
+  stm->user_ptr = user_ptr;
+  stm->latency_frames = latency_frames;
+
+  if ((input_device && !input_stream_params) ||
+      (output_device && !output_stream_params)) {
+    return CUBEB_ERROR_INVALID_PARAMETER;
+  }
+  if (input_stream_params) {
+    stm->input_stream_params = *input_stream_params;
+    r = audiounit_set_device_info(stm.get(), reinterpret_cast<uintptr_t>(input_device), io_side::INPUT);
+    if (r != CUBEB_OK) {
+      LOG("(%p) Fail to set device info for input.", stm.get());
+      return r;
+    }
+  }
+  if (output_stream_params) {
+    stm->output_stream_params = *output_stream_params;
+    r = audiounit_set_device_info(stm.get(), reinterpret_cast<uintptr_t>(output_device), io_side::OUTPUT);
+    if (r != CUBEB_OK) {
+      LOG("(%p) Fail to set device info for output.", stm.get());
+      return r;
+    }
+  }
+
+  {
+    // It's not critical to lock here, because no other thread has been started
+    // yet, but it allows to assert that the lock has been taken in
+    // `audiounit_setup_stream`.
+    auto_lock lock(stm->mutex);
+    r = audiounit_setup_stream(stm.get());
+  }
+
+  if (r != CUBEB_OK) {
+    LOG("(%p) Could not setup the audiounit stream.", stm.get());
+    return r;
+  }
+
+  r = audiounit_install_system_changed_callback(stm.get());
+  if (r != CUBEB_OK) {
+    LOG("(%p) Could not install the device change callback.", stm.get());
+    return r;
+  }
+
+  *stream = stm.release();
+  LOG("(%p) Cubeb stream init successful.", *stream);
+  return CUBEB_OK;
+}
+
+static void
+audiounit_close_stream(cubeb_stream *stm)
+{
+  stm->mutex.assert_current_thread_owns();
+
+  if (stm->input_unit) {
+    AudioUnitUninitialize(stm->input_unit);
+    AudioComponentInstanceDispose(stm->input_unit);
+    stm->input_unit = nullptr;
+  }
+
+  stm->input_linear_buffer.reset();
+
+  if (stm->output_unit) {
+    AudioUnitUninitialize(stm->output_unit);
+    AudioComponentInstanceDispose(stm->output_unit);
+    stm->output_unit = nullptr;
+  }
+
+  stm->resampler.reset();
+  stm->mixer.reset();
+
+  if (stm->aggregate_device_id != kAudioObjectUnknown) {
+    audiounit_destroy_aggregate_device(stm->plugin_id, &stm->aggregate_device_id);
+    stm->aggregate_device_id = kAudioObjectUnknown;
+  }
+}
+
+static void
+audiounit_stream_destroy_internal(cubeb_stream *stm)
+{
+  stm->context->mutex.assert_current_thread_owns();
+
+  int r = audiounit_uninstall_system_changed_callback(stm);
+  if (r != CUBEB_OK) {
+    LOG("(%p) Could not uninstall the device changed callback", stm);
+  }
+  r = audiounit_uninstall_device_changed_callback(stm);
+  if (r != CUBEB_OK) {
+    LOG("(%p) Could not uninstall all device change listeners", stm);
+  }
+
+  auto_lock lock(stm->mutex);
+  audiounit_close_stream(stm);
+  assert(audiounit_active_streams(stm->context) >= 1);
+  audiounit_decrement_active_streams(stm->context);
+}
+
+static void
+audiounit_stream_destroy(cubeb_stream * stm)
+{
+  if (!stm->shutdown.load()){
+    auto_lock context_lock(stm->context->mutex);
+    audiounit_stream_stop_internal(stm);
+    stm->shutdown = true;
+  }
+
+  stm->destroy_pending = true;
+  // Execute close in serial queue to avoid collision
+  // with reinit when un/plug devices
+  dispatch_sync(stm->context->serial_queue, ^() {
+    auto_lock context_lock(stm->context->mutex);
+    audiounit_stream_destroy_internal(stm);
+  });
+
+  LOG("Cubeb stream (%p) destroyed successful.", stm);
+  delete stm;
+}
+
+static int
+audiounit_stream_start_internal(cubeb_stream * stm)
+{
+  OSStatus r;
+  if (stm->input_unit != NULL) {
+    r = AudioOutputUnitStart(stm->input_unit);
+    if (r != noErr) {
+      LOG("AudioOutputUnitStart (input) rv=%d", r);
+      return CUBEB_ERROR;
+    }
+  }
+  if (stm->output_unit != NULL) {
+    r = AudioOutputUnitStart(stm->output_unit);
+    if (r != noErr) {
+      LOG("AudioOutputUnitStart (output) rv=%d", r);
+      return CUBEB_ERROR;
+    }
+  }
+  return CUBEB_OK;
+}
+
+static int
+audiounit_stream_start(cubeb_stream * stm)
+{
+  auto_lock context_lock(stm->context->mutex);
+  stm->shutdown = false;
+  stm->draining = false;
+
+  int r = audiounit_stream_start_internal(stm);
+  if (r != CUBEB_OK) {
+    return r;
+  }
+
+  stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
+
+  LOG("Cubeb stream (%p) started successfully.", stm);
+  return CUBEB_OK;
+}
+
+void
+audiounit_stream_stop_internal(cubeb_stream * stm)
+{
+  OSStatus r;
+  if (stm->input_unit != NULL) {
+    r = AudioOutputUnitStop(stm->input_unit);
+    assert(r == 0);
+  }
+  if (stm->output_unit != NULL) {
+    r = AudioOutputUnitStop(stm->output_unit);
+    assert(r == 0);
+  }
+}
+
+static int
+audiounit_stream_stop(cubeb_stream * stm)
+{
+  auto_lock context_lock(stm->context->mutex);
+  stm->shutdown = true;
+
+  audiounit_stream_stop_internal(stm);
+
+  stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
+
+  LOG("Cubeb stream (%p) stopped successfully.", stm);
+  return CUBEB_OK;
+}
+
+static int
+audiounit_stream_get_position(cubeb_stream * stm, uint64_t * position)
+{
+  assert(stm);
+  if (stm->current_latency_frames > stm->frames_played) {
+    *position = 0;
+  } else {
+    *position = stm->frames_played - stm->current_latency_frames;
+  }
+  return CUBEB_OK;
+}
+
+int
+audiounit_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
+{
+#if TARGET_OS_IPHONE
+  //TODO
+  return CUBEB_ERROR_NOT_SUPPORTED;
+#else
+  *latency = stm->total_output_latency_frames;
+  return CUBEB_OK;
+#endif
+}
+
+static int
+audiounit_stream_get_volume(cubeb_stream * stm, float * volume)
+{
+  assert(stm->output_unit);
+  OSStatus r = AudioUnitGetParameter(stm->output_unit,
+                                     kHALOutputParam_Volume,
+                                     kAudioUnitScope_Global,
+                                     0, volume);
+  if (r != noErr) {
+    LOG("AudioUnitGetParameter/kHALOutputParam_Volume rv=%d", r);
+    return CUBEB_ERROR;
+  }
+  return CUBEB_OK;
+}
+
+static int
+audiounit_stream_set_volume(cubeb_stream * stm, float volume)
+{
+  assert(stm->output_unit);
+  OSStatus r;
+  r = AudioUnitSetParameter(stm->output_unit,
+                            kHALOutputParam_Volume,
+                            kAudioUnitScope_Global,
+                            0, volume, 0);
+
+  if (r != noErr) {
+    LOG("AudioUnitSetParameter/kHALOutputParam_Volume rv=%d", r);
+    return CUBEB_ERROR;
+  }
+  return CUBEB_OK;
+}
+
+unique_ptr<char[]> convert_uint32_into_string(UInt32 data)
+{
+  // Simply create an empty string if no data.
+  size_t size = data == 0 ? 0 : 4; // 4 bytes for uint32.
+  auto str = unique_ptr<char[]> { new char[size + 1] }; // + 1 for '\0'.
+  str[size] = '\0';
+  if (size < 4) {
+    return str;
+  }
+
+  // Reverse 0xWXYZ into 0xZYXW.
+  str[0] = (char)(data >> 24);
+  str[1] = (char)(data >> 16);
+  str[2] = (char)(data >> 8);
+  str[3] = (char)(data);
+  return str;
+}
+
+int audiounit_get_default_device_datasource(cubeb_device_type type,
+                                            UInt32 * data)
+{
+  AudioDeviceID id = audiounit_get_default_device_id(type);
+  if (id == kAudioObjectUnknown) {
+    return CUBEB_ERROR;
+  }
+
+  UInt32 size = sizeof(*data);
+  /* This fails with some USB headsets (e.g., Plantronic .Audio 628). */
+  OSStatus r = AudioObjectGetPropertyData(id,
+                                          type == CUBEB_DEVICE_TYPE_INPUT ?
+                                            &INPUT_DATA_SOURCE_PROPERTY_ADDRESS :
+                                            &OUTPUT_DATA_SOURCE_PROPERTY_ADDRESS,
+                                          0, NULL, &size, data);
+  if (r != noErr) {
+    *data = 0;
+  }
+
+  return CUBEB_OK;
+}
+
+int audiounit_get_default_device_name(cubeb_stream * stm,
+                                      cubeb_device * const device,
+                                      cubeb_device_type type)
+{
+  assert(stm);
+  assert(device);
+
+  UInt32 data;
+  int r = audiounit_get_default_device_datasource(type, &data);
+  if (r != CUBEB_OK) {
+    return r;
+  }
+  char ** name = type == CUBEB_DEVICE_TYPE_INPUT ?
+    &device->input_name : &device->output_name;
+  *name = convert_uint32_into_string(data).release();
+  if (!strlen(*name)) { // empty string.
+    LOG("(%p) name of %s device is empty!", stm,
+        type == CUBEB_DEVICE_TYPE_INPUT ? "input" : "output");
+  }
+  return CUBEB_OK;
+}
+
+
+int audiounit_stream_get_current_device(cubeb_stream * stm,
+                                        cubeb_device ** const device)
+{
+#if TARGET_OS_IPHONE
+  //TODO
+  return CUBEB_ERROR_NOT_SUPPORTED;
+#else
+  *device = new cubeb_device;
+  if (!*device) {
+    return CUBEB_ERROR;
+  }
+  PodZero(*device, 1);
+
+  int r = audiounit_get_default_device_name(stm, *device,
+                                            CUBEB_DEVICE_TYPE_OUTPUT);
+  if (r != CUBEB_OK) {
+    return r;
+  }
+
+  r = audiounit_get_default_device_name(stm, *device,
+                                        CUBEB_DEVICE_TYPE_INPUT);
+  if (r != CUBEB_OK) {
+    return r;
+  }
+
+  return CUBEB_OK;
+#endif
+}
+
+int audiounit_stream_device_destroy(cubeb_stream * /* stream */,
+                                    cubeb_device * device)
+{
+  delete [] device->output_name;
+  delete [] device->input_name;
+  delete device;
+  return CUBEB_OK;
+}
+
+int audiounit_stream_register_device_changed_callback(cubeb_stream * stream,
+                                                      cubeb_device_changed_callback device_changed_callback)
+{
+  auto_lock dev_cb_lock(stream->device_changed_callback_lock);
+  /* Note: second register without unregister first causes 'nope' error.
+   * Current implementation requires unregister before register a new cb. */
+  assert(!device_changed_callback || !stream->device_changed_callback);
+  stream->device_changed_callback = device_changed_callback;
+  return CUBEB_OK;
+}
+
+static char *
+audiounit_strref_to_cstr_utf8(CFStringRef strref)
+{
+  CFIndex len, size;
+  char * ret;
+  if (strref == NULL) {
+    return NULL;
+  }
+
+  len = CFStringGetLength(strref);
+  // Add 1 to size to allow for '\0' termination character.
+  size = CFStringGetMaximumSizeForEncoding(len, kCFStringEncodingUTF8) + 1;
+  ret = new char[size];
+
+  if (!CFStringGetCString(strref, ret, size, kCFStringEncodingUTF8)) {
+    delete [] ret;
+    ret = NULL;
+  }
+
+  return ret;
+}
+
+static uint32_t
+audiounit_get_channel_count(AudioObjectID devid, AudioObjectPropertyScope scope)
+{
+  AudioObjectPropertyAddress adr = { 0, scope, kAudioObjectPropertyElementMaster };
+  UInt32 size = 0;
+  uint32_t i, ret = 0;
+
+  adr.mSelector = kAudioDevicePropertyStreamConfiguration;
+
+  if (AudioObjectGetPropertyDataSize(devid, &adr, 0, NULL, &size) == noErr && size > 0) {
+    AudioBufferList * list = static_cast<AudioBufferList *>(alloca(size));
+    if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, list) == noErr) {
+      for (i = 0; i < list->mNumberBuffers; i++)
+        ret += list->mBuffers[i].mNumberChannels;
+    }
+  }
+
+  return ret;
+}
+
+static void
+audiounit_get_available_samplerate(AudioObjectID devid, AudioObjectPropertyScope scope,
+                                   uint32_t * min, uint32_t * max, uint32_t * def)
+{
+  AudioObjectPropertyAddress adr = { 0, scope, kAudioObjectPropertyElementMaster };
+
+  adr.mSelector = kAudioDevicePropertyNominalSampleRate;
+  if (AudioObjectHasProperty(devid, &adr)) {
+    UInt32 size = sizeof(Float64);
+    Float64 fvalue = 0.0;
+    if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &fvalue) == noErr) {
+      *def = fvalue;
+    }
+  }
+
+  adr.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
+  UInt32 size = 0;
+  AudioValueRange range;
+  if (AudioObjectHasProperty(devid, &adr) &&
+      AudioObjectGetPropertyDataSize(devid, &adr, 0, NULL, &size) == noErr) {
+    uint32_t count = size / sizeof(AudioValueRange);
+    vector<AudioValueRange> ranges(count);
+    range.mMinimum = 9999999999.0;
+    range.mMaximum = 0.0;
+    if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, ranges.data()) == noErr) {
+      for (uint32_t i = 0; i < count; i++) {
+        if (ranges[i].mMaximum > range.mMaximum)
+          range.mMaximum = ranges[i].mMaximum;
+        if (ranges[i].mMinimum < range.mMinimum)
+          range.mMinimum = ranges[i].mMinimum;
+      }
+    }
+    *max = static_cast<uint32_t>(range.mMaximum);
+    *min = static_cast<uint32_t>(range.mMinimum);
+  } else {
+    *min = *max = 0;
+  }
+
+}
+
+static UInt32
+audiounit_get_device_presentation_latency(AudioObjectID devid, AudioObjectPropertyScope scope)
+{
+  AudioObjectPropertyAddress adr = { 0, scope, kAudioObjectPropertyElementMaster };
+  UInt32 size, dev, stream = 0;
+  AudioStreamID sid[1];
+
+  adr.mSelector = kAudioDevicePropertyLatency;
+  size = sizeof(UInt32);
+  if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &dev) != noErr) {
+    dev = 0;
+  }
+
+  adr.mSelector = kAudioDevicePropertyStreams;
+  size = sizeof(sid);
+  if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, sid) == noErr) {
+    adr.mSelector = kAudioStreamPropertyLatency;
+    size = sizeof(UInt32);
+    AudioObjectGetPropertyData(sid[0], &adr, 0, NULL, &size, &stream);
+  }
+
+  return dev + stream;
+}
+
+static int
+audiounit_create_device_from_hwdev(cubeb_device_info * dev_info, AudioObjectID devid, cubeb_device_type type)
+{
+  AudioObjectPropertyAddress adr = { 0, 0, kAudioObjectPropertyElementMaster };
+  UInt32 size;
+
+  if (type == CUBEB_DEVICE_TYPE_OUTPUT) {
+    adr.mScope = kAudioDevicePropertyScopeOutput;
+  } else if (type == CUBEB_DEVICE_TYPE_INPUT) {
+    adr.mScope = kAudioDevicePropertyScopeInput;
+  } else {
+    return CUBEB_ERROR;
+  }
+
+  UInt32 ch = audiounit_get_channel_count(devid, adr.mScope);
+  if (ch == 0) {
+    return CUBEB_ERROR;
+  }
+
+  PodZero(dev_info, 1);
+
+  CFStringRef device_id_str = nullptr;
+  size = sizeof(CFStringRef);
+  adr.mSelector = kAudioDevicePropertyDeviceUID;
+  OSStatus ret = AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &device_id_str);
+  if ( ret == noErr && device_id_str != NULL) {
+    dev_info->device_id = audiounit_strref_to_cstr_utf8(device_id_str);
+    static_assert(sizeof(cubeb_devid) >= sizeof(decltype(devid)), "cubeb_devid can't represent devid");
+    dev_info->devid = reinterpret_cast<cubeb_devid>(devid);
+    dev_info->group_id = dev_info->device_id;
+    CFRelease(device_id_str);
+  }
+
+  CFStringRef friendly_name_str = nullptr;
+  UInt32 ds;
+  size = sizeof(UInt32);
+  adr.mSelector = kAudioDevicePropertyDataSource;
+  ret = AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &ds);
+  if (ret == noErr) {
+    AudioValueTranslation trl = { &ds, sizeof(ds), &friendly_name_str, sizeof(CFStringRef) };
+    adr.mSelector = kAudioDevicePropertyDataSourceNameForIDCFString;
+    size = sizeof(AudioValueTranslation);
+    AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &trl);
+  }
+
+  // If there is no datasource for this device, fall back to the
+  // device name.
+  if (!friendly_name_str) {
+    size = sizeof(CFStringRef);
+    adr.mSelector = kAudioObjectPropertyName;
+    AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &friendly_name_str);
+  }
+
+  if (friendly_name_str) {
+    dev_info->friendly_name = audiounit_strref_to_cstr_utf8(friendly_name_str);
+    CFRelease(friendly_name_str);
+  } else {
+    // Couldn't get a datasource name nor a device name, return a
+    // valid string of length 0.
+    char * fallback_name = new char[1];
+    fallback_name[0] = '\0';
+    dev_info->friendly_name = fallback_name;
+  }
+
+  CFStringRef vendor_name_str = nullptr;
+  size = sizeof(CFStringRef);
+  adr.mSelector = kAudioObjectPropertyManufacturer;
+  ret = AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &vendor_name_str);
+  if (ret == noErr && vendor_name_str != NULL) {
+    dev_info->vendor_name = audiounit_strref_to_cstr_utf8(vendor_name_str);
+    CFRelease(vendor_name_str);
+  }
+
+  dev_info->type = type;
+  dev_info->state = CUBEB_DEVICE_STATE_ENABLED;
+  dev_info->preferred = (devid == audiounit_get_default_device_id(type)) ?
+    CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE;
+
+  dev_info->max_channels = ch;
+  dev_info->format = (cubeb_device_fmt)CUBEB_DEVICE_FMT_ALL; /* CoreAudio supports All! */
+  /* kAudioFormatFlagsAudioUnitCanonical is deprecated, prefer floating point */
+  dev_info->default_format = CUBEB_DEVICE_FMT_F32NE;
+  audiounit_get_available_samplerate(devid, adr.mScope,
+                                     &dev_info->min_rate, &dev_info->max_rate, &dev_info->default_rate);
+
+  UInt32 latency = audiounit_get_device_presentation_latency(devid, adr.mScope);
+
+  AudioValueRange range;
+  adr.mSelector = kAudioDevicePropertyBufferFrameSizeRange;
+  size = sizeof(AudioValueRange);
+  ret = AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &range);
+  if (ret == noErr) {
+    dev_info->latency_lo = latency + range.mMinimum;
+    dev_info->latency_hi = latency + range.mMaximum;
+  } else {
+    dev_info->latency_lo = 10 * dev_info->default_rate / 1000;  /* Default to 10ms */
+    dev_info->latency_hi = 100 * dev_info->default_rate / 1000; /* Default to 100ms */
+  }
+
+  return CUBEB_OK;
+}
+
+bool
+is_aggregate_device(cubeb_device_info * device_info)
+{
+  assert(device_info->friendly_name);
+  return !strncmp(device_info->friendly_name, PRIVATE_AGGREGATE_DEVICE_NAME,
+                  strlen(PRIVATE_AGGREGATE_DEVICE_NAME));
+}
+
+static int
+audiounit_enumerate_devices(cubeb * /* context */, cubeb_device_type type,
+                            cubeb_device_collection * collection)
+{
+  vector<AudioObjectID> input_devs;
+  vector<AudioObjectID> output_devs;
+
+  // Count number of input and output devices.  This is not
+  // necessarily the same as the count of raw devices supported by the
+  // system since, for example, with Soundflower installed, some
+  // devices may report as being both input *and* output and cubeb
+  // separates those into two different devices.
+
+  if (type & CUBEB_DEVICE_TYPE_OUTPUT) {
+    output_devs = audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_OUTPUT);
+  }
+
+  if (type & CUBEB_DEVICE_TYPE_INPUT) {
+    input_devs = audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_INPUT);
+  }
+
+  auto devices = new cubeb_device_info[output_devs.size() + input_devs.size()];
+  collection->count = 0;
+
+  if (type & CUBEB_DEVICE_TYPE_OUTPUT) {
+    for (auto dev: output_devs) {
+      auto device = &devices[collection->count];
+      auto err = audiounit_create_device_from_hwdev(device, dev, CUBEB_DEVICE_TYPE_OUTPUT);
+      if (err != CUBEB_OK || is_aggregate_device(device)) {
+        continue;
+      }
+      collection->count += 1;
+    }
+  }
+
+  if (type & CUBEB_DEVICE_TYPE_INPUT) {
+    for (auto dev: input_devs) {
+      auto device = &devices[collection->count];
+      auto err = audiounit_create_device_from_hwdev(device, dev, CUBEB_DEVICE_TYPE_INPUT);
+      if (err != CUBEB_OK || is_aggregate_device(device)) {
+        continue;
+      }
+      collection->count += 1;
+    }
+  }
+
+  if (collection->count > 0) {
+    collection->device = devices;
+  } else {
+    delete [] devices;
+    collection->device = NULL;
+  }
+
+  return CUBEB_OK;
+}
+
+static void
+audiounit_device_destroy(cubeb_device_info * device)
+{
+  delete [] device->device_id;
+  delete [] device->friendly_name;
+  delete [] device->vendor_name;
+}
+
+static int
+audiounit_device_collection_destroy(cubeb * /* context */,
+                                    cubeb_device_collection * collection)
+{
+  for (size_t i = 0; i < collection->count; i++) {
+    audiounit_device_destroy(&collection->device[i]);
+  }
+  delete [] collection->device;
+
+  return CUBEB_OK;
+}
+
+static vector<AudioObjectID>
+audiounit_get_devices_of_type(cubeb_device_type devtype)
+{
+  UInt32 size = 0;
+  OSStatus ret = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
+                                                &DEVICES_PROPERTY_ADDRESS, 0,
+                                                NULL, &size);
+  if (ret != noErr) {
+    return vector<AudioObjectID>();
+  }
+  vector<AudioObjectID> devices(size / sizeof(AudioObjectID));
+  ret = AudioObjectGetPropertyData(kAudioObjectSystemObject,
+                                   &DEVICES_PROPERTY_ADDRESS, 0, NULL, &size,
+                                   devices.data());
+  if (ret != noErr) {
+    return vector<AudioObjectID>();
+  }
+
+  // Remove the aggregate device from the list of devices (if any).
+  for (auto it = devices.begin(); it != devices.end();) {
+    CFStringRef name = get_device_name(*it);
+    if (name && CFStringFind(name, CFSTR("CubebAggregateDevice"), 0).location !=
+        kCFNotFound) {
+      it = devices.erase(it);
+    } else {
+      it++;
+    }
+    if (name) {
+      CFRelease(name);
+    }
+  }
+
+  /* Expected sorted but did not find anything in the docs. */
+  sort(devices.begin(), devices.end(), [](AudioObjectID a, AudioObjectID b) {
+      return a < b;
+    });
+
+  if (devtype == (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) {
+    return devices;
+  }
+
+  AudioObjectPropertyScope scope = (devtype == CUBEB_DEVICE_TYPE_INPUT) ?
+                                         kAudioDevicePropertyScopeInput :
+                                         kAudioDevicePropertyScopeOutput;
+
+  vector<AudioObjectID> devices_in_scope;
+  for (uint32_t i = 0; i < devices.size(); ++i) {
+    /* For device in the given scope channel must be > 0. */
+    if (audiounit_get_channel_count(devices[i], scope) > 0) {
+      devices_in_scope.push_back(devices[i]);
+    }
+  }
+
+  return devices_in_scope;
+}
+
+static OSStatus
+audiounit_collection_changed_callback(AudioObjectID /* inObjectID */,
+                                      UInt32 /* inNumberAddresses */,
+                                      const AudioObjectPropertyAddress * /* inAddresses */,
+                                      void * inClientData)
+{
+  cubeb * context = static_cast<cubeb *>(inClientData);
+
+  // This can be called from inside an AudioUnit function, dispatch to another queue.
+  dispatch_async(context->serial_queue, ^() {
+    auto_lock lock(context->mutex);
+    if (!context->input_collection_changed_callback &&
+      !context->output_collection_changed_callback) {
+      /* Listener removed while waiting in mutex, abort. */
+      return;
+    }
+    if (context->input_collection_changed_callback) {
+      vector<AudioObjectID> devices = audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_INPUT);
+      /* Elements in the vector expected sorted. */
+      if (context->input_device_array != devices) {
+        context->input_device_array = devices;
+        context->input_collection_changed_callback(context, context->input_collection_changed_user_ptr);
+      }
+    }
+    if (context->output_collection_changed_callback) {
+      vector<AudioObjectID> devices = audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_OUTPUT);
+      /* Elements in the vector expected sorted. */
+      if (context->output_device_array != devices) {
+        context->output_device_array = devices;
+        context->output_collection_changed_callback(context, context->output_collection_changed_user_ptr);
+      }
+    }
+  });
+  return noErr;
+}
+
+static OSStatus
+audiounit_add_device_listener(cubeb * context,
+                              cubeb_device_type devtype,
+                              cubeb_device_collection_changed_callback collection_changed_callback,
+                              void * user_ptr)
+{
+  context->mutex.assert_current_thread_owns();
+  assert(devtype & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT));
+  /* Note: second register without unregister first causes 'nope' error.
+   * Current implementation requires unregister before register a new cb. */
+  assert((devtype & CUBEB_DEVICE_TYPE_INPUT) && !context->input_collection_changed_callback ||
+         (devtype & CUBEB_DEVICE_TYPE_OUTPUT) && !context->output_collection_changed_callback);
+
+  if (!context->input_collection_changed_callback &&
+      !context->output_collection_changed_callback) {
+    OSStatus ret = AudioObjectAddPropertyListener(kAudioObjectSystemObject,
+                                                  &DEVICES_PROPERTY_ADDRESS,
+                                                  audiounit_collection_changed_callback,
+                                                  context);
+    if (ret != noErr) {
+      return ret;
+    }
+  }
+  if (devtype & CUBEB_DEVICE_TYPE_INPUT) {
+    /* Expected empty after unregister. */
+    assert(context->input_device_array.empty());
+    context->input_device_array = audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_INPUT);
+    context->input_collection_changed_callback = collection_changed_callback;
+    context->input_collection_changed_user_ptr = user_ptr;
+  }
+  if (devtype & CUBEB_DEVICE_TYPE_OUTPUT) {
+    /* Expected empty after unregister. */
+    assert(context->output_device_array.empty());
+    context->output_device_array = audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_OUTPUT);
+    context->output_collection_changed_callback = collection_changed_callback;
+    context->output_collection_changed_user_ptr = user_ptr;
+  }
+  return noErr;
+}
+
+static OSStatus
+audiounit_remove_device_listener(cubeb * context, cubeb_device_type devtype)
+{
+  context->mutex.assert_current_thread_owns();
+
+  if (devtype & CUBEB_DEVICE_TYPE_INPUT) {
+    context->input_collection_changed_callback = nullptr;
+    context->input_collection_changed_user_ptr = nullptr;
+    context->input_device_array.clear();
+  }
+  if (devtype & CUBEB_DEVICE_TYPE_OUTPUT) {
+    context->output_collection_changed_callback = nullptr;
+    context->output_collection_changed_user_ptr = nullptr;
+    context->output_device_array.clear();
+  }
+
+  if (context->input_collection_changed_callback ||
+      context->output_collection_changed_callback) {
+    return noErr;
+  }
+  /* Note: unregister a non registered cb is not a problem, not checking. */
+  return AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
+                                           &DEVICES_PROPERTY_ADDRESS,
+                                           audiounit_collection_changed_callback,
+                                           context);
+}
+
+int audiounit_register_device_collection_changed(cubeb * context,
+                                                 cubeb_device_type devtype,
+                                                 cubeb_device_collection_changed_callback collection_changed_callback,
+                                                 void * user_ptr)
+{
+  if (devtype == CUBEB_DEVICE_TYPE_UNKNOWN) {
+    return CUBEB_ERROR_INVALID_PARAMETER;
+  }
+  OSStatus ret;
+  auto_lock lock(context->mutex);
+  if (collection_changed_callback) {
+    ret = audiounit_add_device_listener(context,
+                                        devtype,
+                                        collection_changed_callback,
+                                        user_ptr);
+  } else {
+    ret = audiounit_remove_device_listener(context, devtype);
+  }
+  return (ret == noErr) ? CUBEB_OK : CUBEB_ERROR;
+}
+
+cubeb_ops const audiounit_ops = {
+  /*.init =*/ audiounit_init,
+  /*.get_backend_id =*/ audiounit_get_backend_id,
+  /*.get_max_channel_count =*/ audiounit_get_max_channel_count,
+  /*.get_min_latency =*/ audiounit_get_min_latency,
+  /*.get_preferred_sample_rate =*/ audiounit_get_preferred_sample_rate,
+  /*.enumerate_devices =*/ audiounit_enumerate_devices,
+  /*.device_collection_destroy =*/ audiounit_device_collection_destroy,
+  /*.destroy =*/ audiounit_destroy,
+  /*.stream_init =*/ audiounit_stream_init,
+  /*.stream_destroy =*/ audiounit_stream_destroy,
+  /*.stream_start =*/ audiounit_stream_start,
+  /*.stream_stop =*/ audiounit_stream_stop,
+  /*.stream_reset_default_device =*/ nullptr,
+  /*.stream_get_position =*/ audiounit_stream_get_position,
+  /*.stream_get_latency =*/ audiounit_stream_get_latency,
+  /*.stream_set_volume =*/ audiounit_stream_set_volume,
+  /*.stream_get_current_device =*/ audiounit_stream_get_current_device,
+  /*.stream_device_destroy =*/ audiounit_stream_device_destroy,
+  /*.stream_register_device_changed_callback =*/ audiounit_stream_register_device_changed_callback,
+  /*.register_device_collection_changed =*/ audiounit_register_device_collection_changed
+};
diff --git a/dep/cubeb/src/cubeb_jack.cpp b/dep/cubeb/src/cubeb_jack.cpp
new file mode 100644
index 000000000..9fc97a4b8
--- /dev/null
+++ b/dep/cubeb/src/cubeb_jack.cpp
@@ -0,0 +1,1075 @@
+/*
+ * Copyright © 2012 David Richards
+ * Copyright © 2013 Sebastien Alaiwan
+ * Copyright © 2016 Damien Zammit
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+#define _DEFAULT_SOURCE
+#define _BSD_SOURCE
+#ifndef __FreeBSD__
+#define _POSIX_SOURCE
+#endif
+#include <dlfcn.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <math.h>
+#include "cubeb/cubeb.h"
+#include "cubeb-internal.h"
+#include "cubeb_resampler.h"
+#include "cubeb_utils.h"
+
+#include <jack/jack.h>
+#include <jack/statistics.h>
+
+#define JACK_API_VISIT(X)                       \
+  X(jack_activate)                              \
+  X(jack_client_close)                          \
+  X(jack_client_open)                           \
+  X(jack_connect)                               \
+  X(jack_free)                                  \
+  X(jack_get_ports)                             \
+  X(jack_get_sample_rate)                       \
+  X(jack_get_xrun_delayed_usecs)                \
+  X(jack_get_buffer_size)                       \
+  X(jack_port_get_buffer)                       \
+  X(jack_port_name)                             \
+  X(jack_port_register)                         \
+  X(jack_port_unregister)                       \
+  X(jack_port_get_latency_range)                \
+  X(jack_set_process_callback)                  \
+  X(jack_set_xrun_callback)                     \
+  X(jack_set_graph_order_callback)              \
+  X(jack_set_error_function)                    \
+  X(jack_set_info_function)
+
+#define IMPORT_FUNC(x) static decltype(x) * api_##x;
+JACK_API_VISIT(IMPORT_FUNC);
+
+#define JACK_DEFAULT_IN "JACK capture"
+#define JACK_DEFAULT_OUT "JACK playback"
+
+static const int MAX_STREAMS = 16;
+static const int MAX_CHANNELS  = 8;
+static const int FIFO_SIZE = 4096 * sizeof(float);
+
+enum devstream {
+  NONE = 0,
+  IN_ONLY,
+  OUT_ONLY,
+  DUPLEX,
+};
+
+static void
+s16ne_to_float(float * dst, const int16_t * src, size_t n)
+{
+  for (size_t i = 0; i < n; i++)
+    *(dst++) = (float)((float)*(src++) / 32767.0f);
+}
+
+static void
+float_to_s16ne(int16_t * dst, float * src, size_t n)
+{
+  for (size_t i = 0; i < n; i++) {
+    if (*src > 1.f) *src = 1.f;
+    if (*src < -1.f) *src = -1.f;
+    *(dst++) = (int16_t)((int16_t)(*(src++) * 32767));
+  }
+}
+
+extern "C"
+{
+/*static*/ int jack_init (cubeb ** context, char const * context_name);
+}
+static char const * cbjack_get_backend_id(cubeb * context);
+static int cbjack_get_max_channel_count(cubeb * ctx, uint32_t * max_channels);
+static int cbjack_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames);
+static int cbjack_get_latency(cubeb_stream * stm, unsigned int * latency_frames);
+static int cbjack_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate);
+static void cbjack_destroy(cubeb * context);
+static void cbjack_interleave_capture(cubeb_stream * stream, float **in, jack_nframes_t nframes, bool format_mismatch);
+static void cbjack_deinterleave_playback_refill_s16ne(cubeb_stream * stream, short **bufs_in, float **bufs_out, jack_nframes_t nframes);
+static void cbjack_deinterleave_playback_refill_float(cubeb_stream * stream, float **bufs_in, float **bufs_out, jack_nframes_t nframes);
+static int cbjack_stream_device_destroy(cubeb_stream * stream,
+                                        cubeb_device * device);
+static int cbjack_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const device);
+static int cbjack_enumerate_devices(cubeb * context, cubeb_device_type type,
+                                    cubeb_device_collection * collection);
+static int cbjack_device_collection_destroy(cubeb * context,
+                                            cubeb_device_collection * collection);
+static int cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
+                              cubeb_devid input_device,
+                              cubeb_stream_params * input_stream_params,
+                              cubeb_devid output_device,
+                              cubeb_stream_params * output_stream_params,
+                              unsigned int latency_frames,
+                              cubeb_data_callback data_callback,
+                              cubeb_state_callback state_callback,
+                              void * user_ptr);
+static void cbjack_stream_destroy(cubeb_stream * stream);
+static int cbjack_stream_start(cubeb_stream * stream);
+static int cbjack_stream_stop(cubeb_stream * stream);
+static int cbjack_stream_get_position(cubeb_stream * stream, uint64_t * position);
+static int cbjack_stream_set_volume(cubeb_stream * stm, float volume);
+
+static struct cubeb_ops const cbjack_ops = {
+  .init = jack_init,
+  .get_backend_id = cbjack_get_backend_id,
+  .get_max_channel_count = cbjack_get_max_channel_count,
+  .get_min_latency = cbjack_get_min_latency,
+  .get_preferred_sample_rate = cbjack_get_preferred_sample_rate,
+  .enumerate_devices = cbjack_enumerate_devices,
+  .device_collection_destroy = cbjack_device_collection_destroy,
+  .destroy = cbjack_destroy,
+  .stream_init = cbjack_stream_init,
+  .stream_destroy = cbjack_stream_destroy,
+  .stream_start = cbjack_stream_start,
+  .stream_stop = cbjack_stream_stop,
+  .stream_reset_default_device = NULL,
+  .stream_get_position = cbjack_stream_get_position,
+  .stream_get_latency = cbjack_get_latency,
+  .stream_set_volume = cbjack_stream_set_volume,
+  .stream_get_current_device = cbjack_stream_get_current_device,
+  .stream_device_destroy = cbjack_stream_device_destroy,
+  .stream_register_device_changed_callback = NULL,
+  .register_device_collection_changed = NULL
+};
+
+struct cubeb_stream {
+  /* Note: Must match cubeb_stream layout in cubeb.c. */
+  cubeb * context;
+  void * user_ptr;
+  /**/
+
+  /**< Mutex for each stream */
+  pthread_mutex_t mutex;
+
+  bool in_use; /**< Set to false iff the stream is free */
+  bool ports_ready; /**< Set to true iff the JACK ports are ready */
+
+  cubeb_data_callback data_callback;
+  cubeb_state_callback state_callback;
+  cubeb_stream_params in_params;
+  cubeb_stream_params out_params;
+
+  cubeb_resampler * resampler;
+
+  uint64_t position;
+  bool pause;
+  float ratio;
+  enum devstream devs;
+  char stream_name[256];
+  jack_port_t * output_ports[MAX_CHANNELS];
+  jack_port_t * input_ports[MAX_CHANNELS];
+  float volume;
+};
+
+struct cubeb {
+  struct cubeb_ops const * ops;
+  void * libjack;
+
+  /**< Mutex for whole context */
+  pthread_mutex_t mutex;
+
+  /**< Audio buffers, converted to float */
+  float in_float_interleaved_buffer[FIFO_SIZE * MAX_CHANNELS];
+  float out_float_interleaved_buffer[FIFO_SIZE * MAX_CHANNELS];
+
+  /**< Audio buffer, at the sampling rate of the output */
+  float in_resampled_interleaved_buffer_float[FIFO_SIZE * MAX_CHANNELS * 3];
+  int16_t in_resampled_interleaved_buffer_s16ne[FIFO_SIZE * MAX_CHANNELS * 3];
+  float out_resampled_interleaved_buffer_float[FIFO_SIZE * MAX_CHANNELS * 3];
+  int16_t out_resampled_interleaved_buffer_s16ne[FIFO_SIZE * MAX_CHANNELS * 3];
+
+  cubeb_stream streams[MAX_STREAMS];
+  unsigned int active_streams;
+
+  cubeb_device_collection_changed_callback collection_changed_callback;
+
+  bool active;
+  unsigned int jack_sample_rate;
+  unsigned int jack_latency;
+  unsigned int jack_xruns;
+  unsigned int jack_buffer_size;
+  unsigned int fragment_size;
+  unsigned int output_bytes_per_frame;
+  jack_client_t * jack_client;
+};
+
+static int
+load_jack_lib(cubeb * context)
+{
+#ifdef __APPLE__
+  context->libjack = dlopen("libjack.0.dylib", RTLD_LAZY);
+  context->libjack = dlopen("/usr/local/lib/libjack.0.dylib", RTLD_LAZY);
+#elif defined(__WIN32__)
+# ifdef _WIN64
+   context->libjack = LoadLibrary("libjack64.dll");
+# else
+   context->libjack = LoadLibrary("libjack.dll");
+# endif
+#else
+  context->libjack = dlopen("libjack.so.0", RTLD_LAZY);
+  if (!context->libjack) {
+    context->libjack = dlopen("libjack.so", RTLD_LAZY);
+  }
+#endif
+  if (!context->libjack) {
+    return CUBEB_ERROR;
+  }
+
+#define LOAD(x)                                           \
+  {                                                       \
+    api_##x = (decltype(x)*)dlsym(context->libjack, #x);  \
+    if (!api_##x) {                                       \
+      dlclose(context->libjack);                          \
+      return CUBEB_ERROR;                                 \
+    }                                                     \
+  }
+
+  JACK_API_VISIT(LOAD);
+#undef LOAD
+
+  return CUBEB_OK;
+}
+
+static int
+cbjack_connect_ports (cubeb_stream * stream)
+{
+  int r = CUBEB_ERROR;
+  const char ** phys_in_ports = api_jack_get_ports (stream->context->jack_client,
+                                                   NULL, NULL,
+                                                   JackPortIsInput
+                                                   | JackPortIsPhysical);
+  const char ** phys_out_ports = api_jack_get_ports (stream->context->jack_client,
+                                                    NULL, NULL,
+                                                    JackPortIsOutput
+                                                    | JackPortIsPhysical);
+
+ if (phys_in_ports == NULL || *phys_in_ports == NULL) {
+    goto skipplayback;
+  }
+
+  // Connect outputs to playback
+  for (unsigned int c = 0; c < stream->out_params.channels && phys_in_ports[c] != NULL; c++) {
+    const char *src_port = api_jack_port_name (stream->output_ports[c]);
+
+    api_jack_connect (stream->context->jack_client, src_port, phys_in_ports[c]);
+  }
+  r = CUBEB_OK;
+
+skipplayback:
+  if (phys_out_ports == NULL || *phys_out_ports == NULL) {
+    goto end;
+  }
+  // Connect inputs to capture
+  for (unsigned int c = 0; c < stream->in_params.channels && phys_out_ports[c] != NULL; c++) {
+    const char *src_port = api_jack_port_name (stream->input_ports[c]);
+
+    api_jack_connect (stream->context->jack_client, phys_out_ports[c], src_port);
+  }
+  r = CUBEB_OK;
+end:
+  if (phys_out_ports) {
+    api_jack_free(phys_out_ports);
+  }
+  if (phys_in_ports) {
+    api_jack_free(phys_in_ports);
+  }
+  return r;
+}
+
+static int
+cbjack_xrun_callback(void * arg)
+{
+  cubeb * ctx = (cubeb *)arg;
+
+  float delay = api_jack_get_xrun_delayed_usecs(ctx->jack_client);
+  int fragments = (int)ceilf( ((delay / 1000000.0) * ctx->jack_sample_rate )
+                             / (float)(ctx->jack_buffer_size) );
+  ctx->jack_xruns += fragments;
+  return 0;
+}
+
+static int
+cbjack_graph_order_callback(void * arg)
+{
+  cubeb * ctx = (cubeb *)arg;
+  int i;
+  jack_latency_range_t latency_range;
+  jack_nframes_t port_latency, max_latency = 0;
+
+  for (int j = 0; j < MAX_STREAMS; j++) {
+    cubeb_stream *stm = &ctx->streams[j];
+
+    if (!stm->in_use)
+      continue;
+    if (!stm->ports_ready)
+      continue;
+
+    for (i = 0; i < (int)stm->out_params.channels; ++i) {
+      api_jack_port_get_latency_range(stm->output_ports[i], JackPlaybackLatency, &latency_range);
+      port_latency = latency_range.max;
+      if (port_latency > max_latency)
+          max_latency = port_latency;
+    }
+    /* Cap minimum latency to 128 frames */
+    if (max_latency < 128)
+      max_latency = 128;
+  }
+
+  ctx->jack_latency = max_latency;
+
+  return 0;
+}
+
+static int
+cbjack_process(jack_nframes_t nframes, void * arg)
+{
+  cubeb * ctx = (cubeb *)arg;
+  int t_jack_xruns = ctx->jack_xruns;
+  int i;
+
+  for (int j = 0; j < MAX_STREAMS; j++) {
+    cubeb_stream *stm = &ctx->streams[j];
+    float *bufs_out[stm->out_params.channels];
+    float *bufs_in[stm->in_params.channels];
+
+    if (!stm->in_use)
+      continue;
+
+    // handle xruns by skipping audio that should have been played
+    for (i = 0; i < t_jack_xruns; i++) {
+        stm->position += ctx->fragment_size * stm->ratio;
+    }
+    ctx->jack_xruns -= t_jack_xruns;
+
+    if (!stm->ports_ready)
+      continue;
+
+    if (stm->devs & OUT_ONLY) {
+      // get jack output buffers
+      for (i = 0; i < (int)stm->out_params.channels; i++)
+        bufs_out[i] = (float*)api_jack_port_get_buffer(stm->output_ports[i], nframes);
+    }
+    if (stm->devs & IN_ONLY) {
+      // get jack input buffers
+      for (i = 0; i < (int)stm->in_params.channels; i++)
+        bufs_in[i] = (float*)api_jack_port_get_buffer(stm->input_ports[i], nframes);
+    }
+    if (stm->pause) {
+      // paused, play silence on output
+      if (stm->devs & OUT_ONLY) {
+        for (unsigned int c = 0; c < stm->out_params.channels; c++) {
+          float* buffer_out = bufs_out[c];
+          for (long f = 0; f < nframes; f++) {
+            buffer_out[f] = 0.f;
+          }
+        }
+      }
+      if (stm->devs & IN_ONLY) {
+        // paused, capture silence
+        for (unsigned int c = 0; c < stm->in_params.channels; c++) {
+          float* buffer_in = bufs_in[c];
+          for (long f = 0; f < nframes; f++) {
+            buffer_in[f] = 0.f;
+          }
+        }
+      }
+    } else {
+
+      // try to lock stream mutex
+      if (pthread_mutex_trylock(&stm->mutex) == 0) {
+
+        int16_t *in_s16ne = stm->context->in_resampled_interleaved_buffer_s16ne;
+        float *in_float = stm->context->in_resampled_interleaved_buffer_float;
+
+        // unpaused, play audio
+        if (stm->devs == DUPLEX) {
+          if (stm->out_params.format == CUBEB_SAMPLE_S16NE) {
+            cbjack_interleave_capture(stm, bufs_in, nframes, true);
+            cbjack_deinterleave_playback_refill_s16ne(stm, &in_s16ne, bufs_out, nframes);
+          } else if (stm->out_params.format == CUBEB_SAMPLE_FLOAT32NE) {
+            cbjack_interleave_capture(stm, bufs_in, nframes, false);
+            cbjack_deinterleave_playback_refill_float(stm, &in_float, bufs_out, nframes);
+          }
+        } else if (stm->devs == IN_ONLY) {
+          if (stm->in_params.format == CUBEB_SAMPLE_S16NE) {
+            cbjack_interleave_capture(stm, bufs_in, nframes, true);
+            cbjack_deinterleave_playback_refill_s16ne(stm, &in_s16ne, nullptr, nframes);
+          } else if (stm->in_params.format == CUBEB_SAMPLE_FLOAT32NE) {
+            cbjack_interleave_capture(stm, bufs_in, nframes, false);
+            cbjack_deinterleave_playback_refill_float(stm, &in_float, nullptr, nframes);
+          }
+        } else if (stm->devs == OUT_ONLY) {
+          if (stm->out_params.format == CUBEB_SAMPLE_S16NE) {
+            cbjack_deinterleave_playback_refill_s16ne(stm, nullptr, bufs_out, nframes);
+          } else if (stm->out_params.format == CUBEB_SAMPLE_FLOAT32NE) {
+            cbjack_deinterleave_playback_refill_float(stm, nullptr, bufs_out, nframes);
+          }
+        }
+        // unlock stream mutex
+        pthread_mutex_unlock(&stm->mutex);
+
+      } else {
+        // could not lock mutex
+        // output silence
+        if (stm->devs & OUT_ONLY) {
+          for (unsigned int c = 0; c < stm->out_params.channels; c++) {
+            float* buffer_out = bufs_out[c];
+            for (long f = 0; f < nframes; f++) {
+              buffer_out[f] = 0.f;
+            }
+          }
+        }
+        if (stm->devs & IN_ONLY) {
+          // capture silence
+          for (unsigned int c = 0; c < stm->in_params.channels; c++) {
+            float* buffer_in = bufs_in[c];
+            for (long f = 0; f < nframes; f++) {
+              buffer_in[f] = 0.f;
+            }
+          }
+        }
+      }
+    }
+  }
+  return 0;
+}
+
+static void
+cbjack_deinterleave_playback_refill_float(cubeb_stream * stream, float ** in, float ** bufs_out, jack_nframes_t nframes)
+{
+  float * out_interleaved_buffer = nullptr;
+
+  float * inptr = (in != NULL) ? *in : nullptr;
+  float * outptr = (bufs_out != NULL) ? *bufs_out : nullptr;
+
+  long needed_frames = (bufs_out != NULL) ? nframes : 0;
+  long done_frames = 0;
+  long input_frames_count = (in != NULL) ? nframes : 0;
+
+  done_frames = cubeb_resampler_fill(stream->resampler,
+                                     inptr,
+                                     &input_frames_count,
+                                     (bufs_out != NULL) ? stream->context->out_resampled_interleaved_buffer_float : NULL,
+                                     needed_frames);
+
+  out_interleaved_buffer = stream->context->out_resampled_interleaved_buffer_float;
+
+  if (outptr) {
+    // convert interleaved output buffers to contiguous buffers
+    for (unsigned int c = 0; c < stream->out_params.channels; c++) {
+      float* buffer = bufs_out[c];
+      for (long f = 0; f < done_frames; f++) {
+        buffer[f] = out_interleaved_buffer[(f * stream->out_params.channels) + c] * stream->volume;
+      }
+      if (done_frames < needed_frames) {
+        // draining
+        for (long f = done_frames; f < needed_frames; f++) {
+          buffer[f] = 0.f;
+        }
+      }
+      if (done_frames == 0) {
+        // stop, but first zero out the existing buffer
+        for (long f = 0; f < needed_frames; f++) {
+          buffer[f] = 0.f;
+        }
+      }
+    }
+  }
+
+  if (done_frames >= 0 && done_frames < needed_frames) {
+    // set drained
+    stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_DRAINED);
+    // stop stream
+    cbjack_stream_stop(stream);
+  }
+  if (done_frames > 0 && done_frames <= needed_frames) {
+    // advance stream position
+    stream->position += done_frames * stream->ratio;
+  }
+  if (done_frames < 0 || done_frames > needed_frames) {
+    // stream error
+    stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_ERROR);
+  }
+}
+
+static void
+cbjack_deinterleave_playback_refill_s16ne(cubeb_stream * stream, short ** in, float ** bufs_out, jack_nframes_t nframes)
+{
+  float * out_interleaved_buffer = nullptr;
+
+  short * inptr = (in != NULL) ? *in : nullptr;
+  float * outptr = (bufs_out != NULL) ? *bufs_out : nullptr;
+
+  long needed_frames = (bufs_out != NULL) ? nframes : 0;
+  long done_frames = 0;
+  long input_frames_count = (in != NULL) ? nframes : 0;
+
+  done_frames = cubeb_resampler_fill(stream->resampler,
+                                     inptr,
+                                     &input_frames_count,
+                                     (bufs_out != NULL) ? stream->context->out_resampled_interleaved_buffer_s16ne : NULL,
+                                     needed_frames);
+
+  s16ne_to_float(stream->context->out_resampled_interleaved_buffer_float, stream->context->out_resampled_interleaved_buffer_s16ne, done_frames * stream->out_params.channels);
+
+  out_interleaved_buffer = stream->context->out_resampled_interleaved_buffer_float;
+
+  if (outptr) {
+    // convert interleaved output buffers to contiguous buffers
+    for (unsigned int c = 0; c < stream->out_params.channels; c++) {
+      float* buffer = bufs_out[c];
+      for (long f = 0; f < done_frames; f++) {
+        buffer[f] = out_interleaved_buffer[(f * stream->out_params.channels) + c] * stream->volume;
+      }
+      if (done_frames < needed_frames) {
+        // draining
+        for (long f = done_frames; f < needed_frames; f++) {
+          buffer[f] = 0.f;
+        }
+      }
+      if (done_frames == 0) {
+        // stop, but first zero out the existing buffer
+        for (long f = 0; f < needed_frames; f++) {
+          buffer[f] = 0.f;
+        }
+      }
+    }
+  }
+
+  if (done_frames >= 0 && done_frames < needed_frames) {
+    // set drained
+    stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_DRAINED);
+    // stop stream
+    cbjack_stream_stop(stream);
+  }
+  if (done_frames > 0 && done_frames <= needed_frames) {
+    // advance stream position
+    stream->position += done_frames * stream->ratio;
+  }
+  if (done_frames < 0 || done_frames > needed_frames) {
+    // stream error
+    stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_ERROR);
+  }
+}
+
+static void
+cbjack_interleave_capture(cubeb_stream * stream, float **in, jack_nframes_t nframes, bool format_mismatch)
+{
+  float *in_buffer = stream->context->in_float_interleaved_buffer;
+
+  for (unsigned int c = 0; c < stream->in_params.channels; c++) {
+    for (long f = 0; f < nframes; f++) {
+      in_buffer[(f * stream->in_params.channels) + c] = in[c][f] * stream->volume;
+    }
+  }
+  if (format_mismatch) {
+    float_to_s16ne(stream->context->in_resampled_interleaved_buffer_s16ne, in_buffer, nframes * stream->in_params.channels);
+  } else {
+    memset(stream->context->in_resampled_interleaved_buffer_float, 0, (FIFO_SIZE * MAX_CHANNELS * 3) * sizeof(float));
+    memcpy(stream->context->in_resampled_interleaved_buffer_float, in_buffer, (FIFO_SIZE * MAX_CHANNELS * 2) * sizeof(float));
+  }
+}
+
+static void
+silent_jack_error_callback(char const * /*msg*/)
+{
+}
+
+/*static*/ int
+jack_init (cubeb ** context, char const * context_name)
+{
+  int r;
+
+  *context = NULL;
+
+  cubeb * ctx = (cubeb *)calloc(1, sizeof(*ctx));
+  if (ctx == NULL) {
+    return CUBEB_ERROR;
+  }
+
+  r = load_jack_lib(ctx);
+  if (r != 0) {
+    cbjack_destroy(ctx);
+    return CUBEB_ERROR;
+  }
+
+  api_jack_set_error_function(silent_jack_error_callback);
+  api_jack_set_info_function(silent_jack_error_callback);
+
+  ctx->ops = &cbjack_ops;
+
+  ctx->mutex = PTHREAD_MUTEX_INITIALIZER;
+  for (r = 0; r < MAX_STREAMS; r++) {
+    ctx->streams[r].mutex = PTHREAD_MUTEX_INITIALIZER;
+  }
+
+  const char * jack_client_name = "cubeb";
+  if (context_name)
+    jack_client_name = context_name;
+
+  ctx->jack_client = api_jack_client_open(jack_client_name,
+                                          JackNoStartServer,
+                                          NULL);
+
+  if (ctx->jack_client == NULL) {
+    cbjack_destroy(ctx);
+    return CUBEB_ERROR;
+  }
+
+  ctx->jack_xruns = 0;
+
+  api_jack_set_process_callback (ctx->jack_client, cbjack_process, ctx);
+  api_jack_set_xrun_callback (ctx->jack_client, cbjack_xrun_callback, ctx);
+  api_jack_set_graph_order_callback (ctx->jack_client, cbjack_graph_order_callback, ctx);
+
+  if (api_jack_activate (ctx->jack_client)) {
+    cbjack_destroy(ctx);
+    return CUBEB_ERROR;
+  }
+
+  ctx->jack_sample_rate = api_jack_get_sample_rate(ctx->jack_client);
+  ctx->jack_latency = 128 * 1000 / ctx->jack_sample_rate;
+
+  ctx->active = true;
+  *context = ctx;
+
+  return CUBEB_OK;
+}
+
+static char const *
+cbjack_get_backend_id(cubeb * /*context*/)
+{
+  return "jack";
+}
+
+static int
+cbjack_get_max_channel_count(cubeb * /*ctx*/, uint32_t * max_channels)
+{
+  *max_channels = MAX_CHANNELS;
+  return CUBEB_OK;
+}
+
+static int
+cbjack_get_latency(cubeb_stream * stm, unsigned int * latency_ms)
+{
+  *latency_ms = stm->context->jack_latency;
+  return CUBEB_OK;
+}
+
+static int
+cbjack_get_min_latency(cubeb * ctx, cubeb_stream_params /*params*/, uint32_t * latency_ms)
+{
+  *latency_ms = ctx->jack_latency;
+  return CUBEB_OK;
+}
+
+static int
+cbjack_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
+{
+  if (!ctx->jack_client) {
+    jack_client_t * testclient = api_jack_client_open("test-samplerate",
+                                                 JackNoStartServer,
+                                                 NULL);
+    if (!testclient) {
+      return CUBEB_ERROR;
+    }
+
+    *rate = api_jack_get_sample_rate(testclient);
+    api_jack_client_close(testclient);
+
+  } else {
+    *rate = api_jack_get_sample_rate(ctx->jack_client);
+  }
+  return CUBEB_OK;
+}
+
+static void
+cbjack_destroy(cubeb * context)
+{
+  context->active = false;
+
+  if (context->jack_client != NULL)
+    api_jack_client_close (context->jack_client);
+
+  if (context->libjack)
+    dlclose(context->libjack);
+
+  free(context);
+}
+
+static cubeb_stream *
+context_alloc_stream(cubeb * context, char const * stream_name)
+{
+  for (int i = 0; i < MAX_STREAMS; i++) {
+    if (!context->streams[i].in_use) {
+      cubeb_stream * stm = &context->streams[i];
+      stm->in_use = true;
+      snprintf(stm->stream_name, 255, "%s_%u", stream_name, i);
+      return stm;
+    }
+  }
+  return NULL;
+}
+
+static int
+cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
+                   cubeb_devid input_device,
+                   cubeb_stream_params * input_stream_params,
+                   cubeb_devid output_device,
+                   cubeb_stream_params * output_stream_params,
+                   unsigned int /*latency_frames*/,
+                   cubeb_data_callback data_callback,
+                   cubeb_state_callback state_callback,
+                   void * user_ptr)
+{
+  int stream_actual_rate = 0;
+  int jack_rate = api_jack_get_sample_rate(context->jack_client);
+
+  if (output_stream_params
+     && (output_stream_params->format != CUBEB_SAMPLE_FLOAT32NE &&
+         output_stream_params->format != CUBEB_SAMPLE_S16NE)
+     ) {
+    return CUBEB_ERROR_INVALID_FORMAT;
+  }
+
+  if (input_stream_params
+     && (input_stream_params->format != CUBEB_SAMPLE_FLOAT32NE &&
+         input_stream_params->format != CUBEB_SAMPLE_S16NE)
+     ) {
+    return CUBEB_ERROR_INVALID_FORMAT;
+  }
+
+  if ((input_device && input_device != JACK_DEFAULT_IN) ||
+      (output_device && output_device != JACK_DEFAULT_OUT)) {
+    return CUBEB_ERROR_NOT_SUPPORTED;
+  }
+
+  // Loopback is unsupported
+  if ((input_stream_params && (input_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK)) ||
+      (output_stream_params && (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK))) {
+    return CUBEB_ERROR_NOT_SUPPORTED;
+  }
+
+  *stream = NULL;
+
+  // Find a free stream.
+  pthread_mutex_lock(&context->mutex);
+  cubeb_stream * stm = context_alloc_stream(context, stream_name);
+
+  // No free stream?
+  if (stm == NULL) {
+    pthread_mutex_unlock(&context->mutex);
+    return CUBEB_ERROR;
+  }
+
+  // unlock context mutex
+  pthread_mutex_unlock(&context->mutex);
+
+  // Lock active stream
+  pthread_mutex_lock(&stm->mutex);
+
+  stm->ports_ready = false;
+  stm->user_ptr = user_ptr;
+  stm->context = context;
+  stm->devs = NONE;
+  if (output_stream_params && !input_stream_params) {
+    stm->out_params = *output_stream_params;
+    stream_actual_rate = stm->out_params.rate;
+    stm->out_params.rate = jack_rate;
+    stm->devs = OUT_ONLY;
+    if (stm->out_params.format == CUBEB_SAMPLE_FLOAT32NE) {
+      context->output_bytes_per_frame = sizeof(float);
+    } else {
+      context->output_bytes_per_frame = sizeof(short);
+    }
+  }
+  if (input_stream_params && output_stream_params) {
+    stm->in_params = *input_stream_params;
+    stm->out_params = *output_stream_params;
+    stream_actual_rate = stm->out_params.rate;
+    stm->in_params.rate = jack_rate;
+    stm->out_params.rate = jack_rate;
+    stm->devs = DUPLEX;
+    if (stm->out_params.format == CUBEB_SAMPLE_FLOAT32NE) {
+      context->output_bytes_per_frame = sizeof(float);
+      stm->in_params.format = CUBEB_SAMPLE_FLOAT32NE;
+    } else {
+      context->output_bytes_per_frame = sizeof(short);
+      stm->in_params.format = CUBEB_SAMPLE_S16NE;
+    }
+  } else if (input_stream_params && !output_stream_params) {
+    stm->in_params = *input_stream_params;
+    stream_actual_rate = stm->in_params.rate;
+    stm->in_params.rate = jack_rate;
+    stm->devs = IN_ONLY;
+    if (stm->in_params.format == CUBEB_SAMPLE_FLOAT32NE) {
+      context->output_bytes_per_frame = sizeof(float);
+    } else {
+      context->output_bytes_per_frame = sizeof(short);
+    }
+  }
+
+  stm->ratio = (float)stream_actual_rate / (float)jack_rate;
+
+  stm->data_callback = data_callback;
+  stm->state_callback = state_callback;
+  stm->position = 0;
+  stm->volume = 1.0f;
+  context->jack_buffer_size = api_jack_get_buffer_size(context->jack_client);
+  context->fragment_size = context->jack_buffer_size;
+
+  if (stm->devs == NONE) {
+    pthread_mutex_unlock(&stm->mutex);
+    return CUBEB_ERROR;
+  }
+
+  stm->resampler = NULL;
+
+  if (stm->devs == DUPLEX) {
+    stm->resampler = cubeb_resampler_create(stm,
+                                          &stm->in_params,
+                                          &stm->out_params,
+                                          stream_actual_rate,
+                                          stm->data_callback,
+                                          stm->user_ptr,
+                                          CUBEB_RESAMPLER_QUALITY_DESKTOP);
+  } else if (stm->devs == IN_ONLY) {
+    stm->resampler = cubeb_resampler_create(stm,
+                                          &stm->in_params,
+                                          nullptr,
+                                          stream_actual_rate,
+                                          stm->data_callback,
+                                          stm->user_ptr,
+                                          CUBEB_RESAMPLER_QUALITY_DESKTOP);
+  } else if (stm->devs == OUT_ONLY) {
+    stm->resampler = cubeb_resampler_create(stm,
+                                          nullptr,
+                                          &stm->out_params,
+                                          stream_actual_rate,
+                                          stm->data_callback,
+                                          stm->user_ptr,
+                                          CUBEB_RESAMPLER_QUALITY_DESKTOP);
+  }
+
+  if (!stm->resampler) {
+    stm->in_use = false;
+    pthread_mutex_unlock(&stm->mutex);
+    return CUBEB_ERROR;
+  }
+
+  if (stm->devs == DUPLEX || stm->devs == OUT_ONLY) {
+    for (unsigned int c = 0; c < stm->out_params.channels; c++) {
+      char portname[256];
+      snprintf(portname, 255, "%s_out_%d", stm->stream_name, c);
+      stm->output_ports[c] = api_jack_port_register(stm->context->jack_client,
+                                                    portname,
+                                                    JACK_DEFAULT_AUDIO_TYPE,
+                                                    JackPortIsOutput,
+                                                    0);
+    }
+  }
+
+  if (stm->devs == DUPLEX || stm->devs == IN_ONLY) {
+    for (unsigned int c = 0; c < stm->in_params.channels; c++) {
+      char portname[256];
+      snprintf(portname, 255, "%s_in_%d", stm->stream_name, c);
+      stm->input_ports[c] = api_jack_port_register(stm->context->jack_client,
+                                                    portname,
+                                                    JACK_DEFAULT_AUDIO_TYPE,
+                                                    JackPortIsInput,
+                                                    0);
+    }
+  }
+
+  if (cbjack_connect_ports(stm) != CUBEB_OK) {
+    pthread_mutex_unlock(&stm->mutex);
+    cbjack_stream_destroy(stm);
+    return CUBEB_ERROR;
+  }
+
+  *stream = stm;
+
+  stm->ports_ready = true;
+  stm->pause = true;
+  pthread_mutex_unlock(&stm->mutex);
+
+  return CUBEB_OK;
+}
+
+static void
+cbjack_stream_destroy(cubeb_stream * stream)
+{
+  pthread_mutex_lock(&stream->mutex);
+  stream->ports_ready = false;
+
+  if (stream->devs == DUPLEX || stream->devs == OUT_ONLY) {
+    for (unsigned int c = 0; c < stream->out_params.channels; c++) {
+      if (stream->output_ports[c]) {
+        api_jack_port_unregister (stream->context->jack_client, stream->output_ports[c]);
+        stream->output_ports[c] = NULL;
+      }
+    }
+  }
+
+  if (stream->devs == DUPLEX || stream->devs == IN_ONLY) {
+    for (unsigned int c = 0; c < stream->in_params.channels; c++) {
+      if (stream->input_ports[c]) {
+        api_jack_port_unregister (stream->context->jack_client, stream->input_ports[c]);
+        stream->input_ports[c] = NULL;
+      }
+    }
+  }
+
+  if (stream->resampler) {
+    cubeb_resampler_destroy(stream->resampler);
+    stream->resampler = NULL;
+  }
+  stream->in_use = false;
+  pthread_mutex_unlock(&stream->mutex);
+}
+
+static int
+cbjack_stream_start(cubeb_stream * stream)
+{
+  stream->pause = false;
+  stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_STARTED);
+  return CUBEB_OK;
+}
+
+static int
+cbjack_stream_stop(cubeb_stream * stream)
+{
+  stream->pause = true;
+  stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_STOPPED);
+  return CUBEB_OK;
+}
+
+static int
+cbjack_stream_get_position(cubeb_stream * stream, uint64_t * position)
+{
+  *position = stream->position;
+  return CUBEB_OK;
+}
+
+static int
+cbjack_stream_set_volume(cubeb_stream * stm, float volume)
+{
+  stm->volume = volume;
+  return CUBEB_OK;
+}
+
+static int
+cbjack_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const device)
+{
+  *device = (cubeb_device *)calloc(1, sizeof(cubeb_device));
+  if (*device == NULL)
+    return CUBEB_ERROR;
+
+  const char * j_in = JACK_DEFAULT_IN;
+  const char * j_out = JACK_DEFAULT_OUT;
+  const char * empty = "";
+
+  if (stm->devs == DUPLEX) {
+    (*device)->input_name = strdup(j_in);
+    (*device)->output_name = strdup(j_out);
+  } else if (stm->devs == IN_ONLY) {
+    (*device)->input_name = strdup(j_in);
+    (*device)->output_name = strdup(empty);
+  } else if (stm->devs == OUT_ONLY) {
+    (*device)->input_name = strdup(empty);
+    (*device)->output_name = strdup(j_out);
+  }
+
+  return CUBEB_OK;
+}
+
+static int
+cbjack_stream_device_destroy(cubeb_stream * /*stream*/,
+                             cubeb_device * device)
+{
+  if (device->input_name)
+    free(device->input_name);
+  if (device->output_name)
+    free(device->output_name);
+  free(device);
+  return CUBEB_OK;
+}
+
+static int
+cbjack_enumerate_devices(cubeb * context, cubeb_device_type type,
+                         cubeb_device_collection * collection)
+{
+  if (!context)
+    return CUBEB_ERROR;
+
+  uint32_t rate;
+  cbjack_get_preferred_sample_rate(context, &rate);
+
+  cubeb_device_info * devices = new cubeb_device_info[2];
+  if (!devices)
+    return CUBEB_ERROR;
+  PodZero(devices, 2);
+  collection->count = 0;
+
+  if (type & CUBEB_DEVICE_TYPE_OUTPUT) {
+    cubeb_device_info * cur = &devices[collection->count];
+    cur->device_id = JACK_DEFAULT_OUT;
+    cur->devid = (cubeb_devid) cur->device_id;
+    cur->friendly_name = JACK_DEFAULT_OUT;
+    cur->group_id = JACK_DEFAULT_OUT;
+    cur->vendor_name = JACK_DEFAULT_OUT;
+    cur->type = CUBEB_DEVICE_TYPE_OUTPUT;
+    cur->state = CUBEB_DEVICE_STATE_ENABLED;
+    cur->preferred = CUBEB_DEVICE_PREF_ALL;
+    cur->format = CUBEB_DEVICE_FMT_F32NE;
+    cur->default_format = CUBEB_DEVICE_FMT_F32NE;
+    cur->max_channels = MAX_CHANNELS;
+    cur->min_rate = rate;
+    cur->max_rate = rate;
+    cur->default_rate = rate;
+    cur->latency_lo = 0;
+    cur->latency_hi = 0;
+    collection->count +=1 ;
+  }
+
+  if (type & CUBEB_DEVICE_TYPE_INPUT) {
+    cubeb_device_info * cur = &devices[collection->count];
+    cur->device_id = JACK_DEFAULT_IN;
+    cur->devid = (cubeb_devid) cur->device_id;
+    cur->friendly_name = JACK_DEFAULT_IN;
+    cur->group_id = JACK_DEFAULT_IN;
+    cur->vendor_name = JACK_DEFAULT_IN;
+    cur->type = CUBEB_DEVICE_TYPE_INPUT;
+    cur->state = CUBEB_DEVICE_STATE_ENABLED;
+    cur->preferred = CUBEB_DEVICE_PREF_ALL;
+    cur->format = CUBEB_DEVICE_FMT_F32NE;
+    cur->default_format = CUBEB_DEVICE_FMT_F32NE;
+    cur->max_channels = MAX_CHANNELS;
+    cur->min_rate = rate;
+    cur->max_rate = rate;
+    cur->default_rate = rate;
+    cur->latency_lo = 0;
+    cur->latency_hi = 0;
+    collection->count += 1;
+  }
+
+  collection->device = devices;
+
+  return CUBEB_OK;
+}
+
+static int
+cbjack_device_collection_destroy(cubeb * /*ctx*/,
+                                 cubeb_device_collection * collection)
+{
+  XASSERT(collection);
+  delete [] collection->device;
+  return CUBEB_OK;
+}
diff --git a/dep/cubeb/src/cubeb_kai.c b/dep/cubeb/src/cubeb_kai.c
new file mode 100644
index 000000000..5333968c8
--- /dev/null
+++ b/dep/cubeb/src/cubeb_kai.c
@@ -0,0 +1,369 @@
+/*
+ * Copyright © 2015 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <sys/fmutex.h>
+
+#include <kai.h>
+
+#include "cubeb/cubeb.h"
+#include "cubeb-internal.h"
+
+/* We don't support more than 2 channels in KAI */
+#define MAX_CHANNELS 2
+
+#define NBUFS 2
+#define FRAME_SIZE 2048
+
+struct cubeb_stream_item {
+  cubeb_stream * stream;
+};
+
+static struct cubeb_ops const kai_ops;
+
+struct cubeb {
+  struct cubeb_ops const * ops;
+};
+
+struct cubeb_stream {
+  /* Note: Must match cubeb_stream layout in cubeb.c. */
+  cubeb * context;
+  void * user_ptr;
+  /**/
+  cubeb_stream_params params;
+  cubeb_data_callback data_callback;
+  cubeb_state_callback state_callback;
+
+  HKAI hkai;
+  KAISPEC spec;
+  uint64_t total_frames;
+  float soft_volume;
+  _fmutex mutex;
+  float float_buffer[FRAME_SIZE * MAX_CHANNELS];
+};
+
+static inline long
+frames_to_bytes(long frames, cubeb_stream_params params)
+{
+  return frames * 2 * params.channels; /* 2 bytes per frame */
+}
+
+static inline long
+bytes_to_frames(long bytes, cubeb_stream_params params)
+{
+  return bytes / 2 / params.channels; /* 2 bytes per frame */
+}
+
+static void kai_destroy(cubeb * ctx);
+
+/*static*/ int
+kai_init(cubeb ** context, char const * context_name)
+{
+  cubeb * ctx;
+
+  XASSERT(context);
+  *context = NULL;
+
+  if (kaiInit(KAIM_AUTO))
+    return CUBEB_ERROR;
+
+  ctx = calloc(1, sizeof(*ctx));
+  XASSERT(ctx);
+
+  ctx->ops = &kai_ops;
+
+  *context = ctx;
+
+  return CUBEB_OK;
+}
+
+static char const *
+kai_get_backend_id(cubeb * ctx)
+{
+  return "kai";
+}
+
+static void
+kai_destroy(cubeb * ctx)
+{
+  kaiDone();
+
+  free(ctx);
+}
+
+static void
+float_to_s16ne(int16_t *dst, float *src, size_t n)
+{
+  long l;
+
+  while (n--) {
+    l = lrintf(*src++ * 0x8000);
+    if (l > 32767)
+      l = 32767;
+    if (l < -32768)
+      l = -32768;
+    *dst++ = (int16_t)l;
+  }
+}
+
+static ULONG APIENTRY
+kai_callback(PVOID cbdata, PVOID buffer, ULONG len)
+{
+  cubeb_stream * stm = cbdata;
+  void *p;
+  long wanted_frames;
+  long frames;
+  float soft_volume;
+  int elements = len / sizeof(int16_t);
+
+  p = stm->params.format == CUBEB_SAMPLE_FLOAT32NE
+      ? stm->float_buffer : buffer;
+
+  wanted_frames = bytes_to_frames(len, stm->params);
+  frames = stm->data_callback(stm, stm->user_ptr, NULL, p, wanted_frames);
+
+  _fmutex_request(&stm->mutex, 0);
+  stm->total_frames += frames;
+  soft_volume = stm->soft_volume;
+  _fmutex_release(&stm->mutex);
+
+  if (frames < wanted_frames)
+    stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
+
+  if (stm->params.format == CUBEB_SAMPLE_FLOAT32NE)
+    float_to_s16ne(buffer, p, elements);
+
+  if (soft_volume != -1.0f) {
+    int16_t *b = buffer;
+    int i;
+
+    for (i = 0; i < elements; i++)
+      *b++ *= soft_volume;
+  }
+
+  return frames_to_bytes(frames, stm->params);
+}
+
+static void kai_stream_destroy(cubeb_stream * stm);
+
+static int
+kai_stream_init(cubeb * context, cubeb_stream ** stream,
+                char const * stream_name,
+                cubeb_devid input_device,
+                cubeb_stream_params * input_stream_params,
+                cubeb_devid output_device,
+                cubeb_stream_params * output_stream_params,
+                unsigned int latency, cubeb_data_callback data_callback,
+                cubeb_state_callback state_callback, void * user_ptr)
+{
+  cubeb_stream * stm;
+  KAISPEC wanted_spec;
+
+  XASSERT(!input_stream_params && "not supported.");
+  if (input_device || output_device) {
+    /* Device selection not yet implemented. */
+    return CUBEB_ERROR_DEVICE_UNAVAILABLE;
+  }
+
+  if (!output_stream_params)
+    return CUBEB_ERROR_INVALID_PARAMETER;
+
+  // Loopback is unsupported
+  if (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
+    return CUBEB_ERROR_NOT_SUPPORTED;
+  }
+
+  if (output_stream_params->channels < 1 ||
+      output_stream_params->channels > MAX_CHANNELS)
+    return CUBEB_ERROR_INVALID_FORMAT;
+
+  XASSERT(context);
+  XASSERT(stream);
+
+  *stream = NULL;
+
+  stm = calloc(1, sizeof(*stm));
+  XASSERT(stm);
+
+  stm->context = context;
+  stm->params = *output_stream_params;
+  stm->data_callback = data_callback;
+  stm->state_callback = state_callback;
+  stm->user_ptr = user_ptr;
+  stm->soft_volume = -1.0f;
+
+  if (_fmutex_create(&stm->mutex, 0)) {
+    free(stm);
+    return CUBEB_ERROR;
+  }
+
+  wanted_spec.usDeviceIndex   = 0;
+  wanted_spec.ulType          = KAIT_PLAY;
+  wanted_spec.ulBitsPerSample = BPS_16;
+  wanted_spec.ulSamplingRate  = stm->params.rate;
+  wanted_spec.ulDataFormat    = MCI_WAVE_FORMAT_PCM;
+  wanted_spec.ulChannels      = stm->params.channels;
+  wanted_spec.ulNumBuffers    = NBUFS;
+  wanted_spec.ulBufferSize    = frames_to_bytes(FRAME_SIZE, stm->params);
+  wanted_spec.fShareable      = TRUE;
+  wanted_spec.pfnCallBack     = kai_callback;
+  wanted_spec.pCallBackData   = stm;
+
+  if (kaiOpen(&wanted_spec, &stm->spec, &stm->hkai)) {
+    _fmutex_close(&stm->mutex);
+    free(stm);
+    return CUBEB_ERROR;
+  }
+
+  *stream = stm;
+
+  return CUBEB_OK;
+}
+
+static void
+kai_stream_destroy(cubeb_stream * stm)
+{
+  kaiClose(stm->hkai);
+  _fmutex_close(&stm->mutex);
+  free(stm);
+}
+
+static int
+kai_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
+{
+  XASSERT(ctx && max_channels);
+
+  *max_channels = MAX_CHANNELS;
+
+  return CUBEB_OK;
+}
+
+static int
+kai_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency)
+{
+  /* We have at least two buffers. One is being played, the other one is being
+     filled. So there is as much latency as one buffer. */
+  *latency = FRAME_SIZE;
+
+  return CUBEB_OK;
+}
+
+static int
+kai_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
+{
+  cubeb_stream_params params;
+  KAISPEC wanted_spec;
+  KAISPEC spec;
+  HKAI hkai;
+
+  params.format = CUBEB_SAMPLE_S16NE;
+  params.rate = 48000;
+  params.channels = 2;
+
+  wanted_spec.usDeviceIndex   = 0;
+  wanted_spec.ulType          = KAIT_PLAY;
+  wanted_spec.ulBitsPerSample = BPS_16;
+  wanted_spec.ulSamplingRate  = params.rate;
+  wanted_spec.ulDataFormat    = MCI_WAVE_FORMAT_PCM;
+  wanted_spec.ulChannels      = params.channels;
+  wanted_spec.ulNumBuffers    = NBUFS;
+  wanted_spec.ulBufferSize    = frames_to_bytes(FRAME_SIZE, params);
+  wanted_spec.fShareable      = TRUE;
+  wanted_spec.pfnCallBack     = kai_callback;
+  wanted_spec.pCallBackData   = NULL;
+
+  /* Test 48KHz */
+  if (kaiOpen(&wanted_spec, &spec, &hkai)) {
+    /* Not supported. Fall back to 44.1KHz */
+    params.rate = 44100;
+  } else {
+    /* Supported. Use 48KHz */
+    kaiClose(hkai);
+  }
+
+  *rate = params.rate;
+
+  return CUBEB_OK;
+}
+
+static int
+kai_stream_start(cubeb_stream * stm)
+{
+  if (kaiPlay(stm->hkai))
+    return CUBEB_ERROR;
+
+  stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
+
+  return CUBEB_OK;
+}
+
+static int
+kai_stream_stop(cubeb_stream * stm)
+{
+  if (kaiStop(stm->hkai))
+    return CUBEB_ERROR;
+
+  stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
+
+  return CUBEB_OK;
+}
+
+static int
+kai_stream_get_position(cubeb_stream * stm, uint64_t * position)
+{
+  _fmutex_request(&stm->mutex, 0);
+  *position = stm->total_frames;
+  _fmutex_release(&stm->mutex);
+
+  return CUBEB_OK;
+}
+
+static int
+kai_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
+{
+  /* Out of buffers, one is being played, the others are being filled.
+     So there is as much latency as total buffers - 1. */
+  *latency = bytes_to_frames(stm->spec.ulBufferSize, stm->params)
+             * (stm->spec.ulNumBuffers - 1);
+
+  return CUBEB_OK;
+}
+
+static int
+kai_stream_set_volume(cubeb_stream * stm, float volume)
+{
+  _fmutex_request(&stm->mutex, 0);
+  stm->soft_volume = volume;
+  _fmutex_release(&stm->mutex);
+
+  return CUBEB_OK;
+}
+
+static struct cubeb_ops const kai_ops = {
+  /*.init =*/ kai_init,
+  /*.get_backend_id =*/ kai_get_backend_id,
+  /*.get_max_channel_count=*/ kai_get_max_channel_count,
+  /*.get_min_latency=*/ kai_get_min_latency,
+  /*.get_preferred_sample_rate =*/ kai_get_preferred_sample_rate,
+  /*.get_preferred_channel_layout =*/ NULL,
+  /*.enumerate_devices =*/ NULL,
+  /*.device_collection_destroy =*/ NULL,
+  /*.destroy =*/ kai_destroy,
+  /*.stream_init =*/ kai_stream_init,
+  /*.stream_destroy =*/ kai_stream_destroy,
+  /*.stream_start =*/ kai_stream_start,
+  /*.stream_stop =*/ kai_stream_stop,
+  /*.stream_reset_default_device =*/ NULL,
+  /*.stream_get_position =*/ kai_stream_get_position,
+  /*.stream_get_latency = */ kai_stream_get_latency,
+  /*.stream_set_volume =*/ kai_stream_set_volume,
+  /*.stream_get_current_device =*/ NULL,
+  /*.stream_device_destroy =*/ NULL,
+  /*.stream_register_device_changed_callback=*/ NULL,
+  /*.register_device_collection_changed=*/ NULL
+};
diff --git a/dep/cubeb/src/cubeb_log.cpp b/dep/cubeb/src/cubeb_log.cpp
new file mode 100644
index 000000000..54c7f4a15
--- /dev/null
+++ b/dep/cubeb/src/cubeb_log.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright © 2016 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+#define NOMINMAX
+
+#include "cubeb_log.h"
+#include "cubeb_ringbuffer.h"
+#include <cstdarg>
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <time.h>
+#endif
+
+cubeb_log_level g_cubeb_log_level;
+cubeb_log_callback g_cubeb_log_callback;
+
+/** The maximum size of a log message, after having been formatted. */
+const size_t CUBEB_LOG_MESSAGE_MAX_SIZE = 256;
+/** The maximum number of log messages that can be queued before dropping
+ * messages. */
+const size_t CUBEB_LOG_MESSAGE_QUEUE_DEPTH = 40;
+/** Number of milliseconds to wait before dequeuing log messages. */
+#define CUBEB_LOG_BATCH_PRINT_INTERVAL_MS 10
+
+/**
+  * This wraps an inline buffer, that represents a log message, that must be
+  * null-terminated.
+  * This class should not use system calls or other potentially blocking code.
+  */
+class cubeb_log_message
+{
+public:
+  cubeb_log_message()
+  {
+    *storage = '\0';
+  }
+  cubeb_log_message(char const str[CUBEB_LOG_MESSAGE_MAX_SIZE])
+  {
+    size_t length = strlen(str);
+    /* paranoia against malformed message */
+    assert(length < CUBEB_LOG_MESSAGE_MAX_SIZE);
+    if (length > CUBEB_LOG_MESSAGE_MAX_SIZE - 1) {
+      return;
+    }
+    PodCopy(storage, str, length);
+    storage[length] = '\0';
+  }
+  char const * get() {
+    return storage;
+  }
+private:
+  char storage[CUBEB_LOG_MESSAGE_MAX_SIZE];
+};
+
+/** Lock-free asynchronous logger, made so that logging from a
+ *  real-time audio callback does not block the audio thread. */
+class cubeb_async_logger
+{
+public:
+  /* This is thread-safe since C++11 */
+  static cubeb_async_logger & get() {
+    static cubeb_async_logger instance;
+    return instance;
+  }
+  void push(char const str[CUBEB_LOG_MESSAGE_MAX_SIZE])
+  {
+    cubeb_log_message msg(str);
+    msg_queue.enqueue(msg);
+  }
+  void run()
+  {
+    std::thread([this]() {
+      while (true) {
+        cubeb_log_message msg;
+        while (msg_queue.dequeue(&msg, 1)) {
+          LOGV("%s", msg.get());
+        }
+#ifdef _WIN32
+        Sleep(CUBEB_LOG_BATCH_PRINT_INTERVAL_MS);
+#else
+        timespec sleep_duration = sleep_for;
+        timespec remainder;
+        do {
+          if (nanosleep(&sleep_duration, &remainder) == 0 ||
+              errno != EINTR) {
+            break;
+          }
+          sleep_duration = remainder;
+        } while (remainder.tv_sec || remainder.tv_nsec);
+#endif
+      }
+    }).detach();
+  }
+  // Tell the underlying queue the producer thread has changed, so it does not
+  // assert in debug. This should be called with the thread stopped.
+  void reset_producer_thread()
+  {
+    msg_queue.reset_thread_ids();
+  }
+private:
+#ifndef _WIN32
+  const struct timespec sleep_for = {
+    CUBEB_LOG_BATCH_PRINT_INTERVAL_MS/1000,
+    (CUBEB_LOG_BATCH_PRINT_INTERVAL_MS%1000)*1000*1000
+  };
+#endif
+  cubeb_async_logger()
+    : msg_queue(CUBEB_LOG_MESSAGE_QUEUE_DEPTH)
+  {
+    run();
+  }
+  /** This is quite a big data structure, but is only instantiated if the
+   * asynchronous logger is used.*/
+  lock_free_queue<cubeb_log_message> msg_queue;
+};
+
+
+void cubeb_async_log(char const * fmt, ...)
+{
+  if (!g_cubeb_log_callback) {
+    return;
+  }
+  // This is going to copy a 256 bytes array around, which is fine.
+  // We don't want to allocate memory here, because this is made to
+  // be called from a real-time callback.
+  va_list args;
+  va_start(args, fmt);
+  char msg[CUBEB_LOG_MESSAGE_MAX_SIZE];
+  vsnprintf(msg, CUBEB_LOG_MESSAGE_MAX_SIZE, fmt, args);
+  cubeb_async_logger::get().push(msg);
+  va_end(args);
+}
+
+void cubeb_async_log_reset_threads()
+{
+  if (!g_cubeb_log_callback) {
+    return;
+  }
+  cubeb_async_logger::get().reset_producer_thread();
+}
diff --git a/dep/cubeb/src/cubeb_log.h b/dep/cubeb/src/cubeb_log.h
new file mode 100644
index 000000000..a79976bb3
--- /dev/null
+++ b/dep/cubeb/src/cubeb_log.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright © 2016 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+
+#ifndef CUBEB_LOG
+#define CUBEB_LOG
+
+#include "cubeb/cubeb.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(__GNUC__) || defined(__clang__)
+#define PRINTF_FORMAT(fmt, args) __attribute__((format(printf, fmt, args)))
+#else
+#define PRINTF_FORMAT(fmt, args)
+#endif
+
+extern cubeb_log_level g_cubeb_log_level;
+extern cubeb_log_callback g_cubeb_log_callback PRINTF_FORMAT(1, 2);
+void cubeb_async_log(const char * fmt, ...);
+void cubeb_async_log_reset_threads();
+
+#ifdef __cplusplus
+}
+#endif
+
+#define LOGV(msg, ...) LOG_INTERNAL(CUBEB_LOG_VERBOSE, msg, ##__VA_ARGS__)
+#define LOG(msg, ...) LOG_INTERNAL(CUBEB_LOG_NORMAL, msg, ##__VA_ARGS__)
+
+#define LOG_INTERNAL(level, fmt, ...) do {                                   \
+    if (g_cubeb_log_callback && level <= g_cubeb_log_level) {                            \
+      g_cubeb_log_callback("%s:%d: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__); \
+    }                                                                        \
+  } while(0)
+
+/* Asynchronous verbose logging, to log in real-time callbacks. */
+#define ALOGV(fmt, ...)                   \
+do {                                      \
+  cubeb_async_log(fmt, ##__VA_ARGS__);    \
+} while(0)
+
+#endif // CUBEB_LOG
diff --git a/dep/cubeb/src/cubeb_mixer.cpp b/dep/cubeb/src/cubeb_mixer.cpp
new file mode 100644
index 000000000..2ab7f673a
--- /dev/null
+++ b/dep/cubeb/src/cubeb_mixer.cpp
@@ -0,0 +1,663 @@
+/*
+ * Copyright © 2016 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ *
+ * Adapted from code based on libswresample's rematrix.c
+ */
+
+#define NOMINMAX
+
+#include <algorithm>
+#include <cassert>
+#include <climits>
+#include <cmath>
+#include <cstdlib>
+#include <memory>
+#include <type_traits>
+#include "cubeb-internal.h"
+#include "cubeb_mixer.h"
+#include "cubeb_utils.h"
+
+#ifndef FF_ARRAY_ELEMS
+#define FF_ARRAY_ELEMS(a) (sizeof(a) / sizeof((a)[0]))
+#endif
+
+#define CHANNELS_MAX 32
+#define FRONT_LEFT             0
+#define FRONT_RIGHT            1
+#define FRONT_CENTER           2
+#define LOW_FREQUENCY          3
+#define BACK_LEFT              4
+#define BACK_RIGHT             5
+#define FRONT_LEFT_OF_CENTER   6
+#define FRONT_RIGHT_OF_CENTER  7
+#define BACK_CENTER            8
+#define SIDE_LEFT              9
+#define SIDE_RIGHT             10
+#define TOP_CENTER             11
+#define TOP_FRONT_LEFT         12
+#define TOP_FRONT_CENTER       13
+#define TOP_FRONT_RIGHT        14
+#define TOP_BACK_LEFT          15
+#define TOP_BACK_CENTER        16
+#define TOP_BACK_RIGHT         17
+#define NUM_NAMED_CHANNELS     18
+
+#ifndef M_SQRT1_2
+#define M_SQRT1_2      0.70710678118654752440  /* 1/sqrt(2) */
+#endif
+#ifndef M_SQRT2
+#define M_SQRT2        1.41421356237309504880  /* sqrt(2) */
+#endif
+#define SQRT3_2      1.22474487139158904909  /* sqrt(3/2) */
+
+#define  C30DB  M_SQRT2
+#define  C15DB  1.189207115
+#define C__0DB  1.0
+#define C_15DB  0.840896415
+#define C_30DB  M_SQRT1_2
+#define C_45DB  0.594603558
+#define C_60DB  0.5
+
+static cubeb_channel_layout
+cubeb_channel_layout_check(cubeb_channel_layout l, uint32_t c)
+{
+    if (l == CUBEB_LAYOUT_UNDEFINED) {
+      switch (c) {
+        case 1: return CUBEB_LAYOUT_MONO;
+        case 2: return CUBEB_LAYOUT_STEREO;
+      }
+    }
+    return l;
+}
+
+unsigned int cubeb_channel_layout_nb_channels(cubeb_channel_layout x)
+{
+#if __GNUC__ || __clang__
+  return __builtin_popcount (x);
+#else
+  x -= (x >> 1) & 0x55555555;
+  x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
+  x = (x + (x >> 4)) & 0x0F0F0F0F;
+  x += x >> 8;
+  return (x + (x >> 16)) & 0x3F;
+#endif
+}
+
+struct MixerContext {
+  MixerContext(cubeb_sample_format f,
+               uint32_t in_channels,
+               cubeb_channel_layout in,
+               uint32_t out_channels,
+               cubeb_channel_layout out)
+    : _format(f)
+    , _in_ch_layout(cubeb_channel_layout_check(in, in_channels))
+    , _out_ch_layout(cubeb_channel_layout_check(out, out_channels))
+    , _in_ch_count(in_channels)
+    , _out_ch_count(out_channels)
+  {
+    if (in_channels != cubeb_channel_layout_nb_channels(in) ||
+        out_channels != cubeb_channel_layout_nb_channels(out)) {
+      // Mismatch between channels and layout, aborting.
+      return;
+    }
+    _valid = init() >= 0;
+  }
+
+  static bool even(cubeb_channel_layout layout)
+  {
+    if (!layout) {
+      return true;
+    }
+    if (layout & (layout - 1)) {
+      return true;
+    }
+    return false;
+  }
+
+  // Ensure that the layout is sane (that is have symmetrical left/right
+  // channels), if not, layout will be treated as mono.
+  static cubeb_channel_layout clean_layout(cubeb_channel_layout layout)
+  {
+    if (layout && layout != CHANNEL_FRONT_LEFT && !(layout & (layout - 1))) {
+      LOG("Treating layout as mono");
+      return CHANNEL_FRONT_CENTER;
+    }
+
+    return layout;
+  }
+
+  static bool sane_layout(cubeb_channel_layout layout)
+  {
+    if (!(layout & CUBEB_LAYOUT_3F)) { // at least 1 front speaker
+      return false;
+    }
+    if (!even(layout & (CHANNEL_FRONT_LEFT |
+                        CHANNEL_FRONT_RIGHT))) { // no asymetric front
+      return false;
+    }
+    if (!even(layout &
+              (CHANNEL_SIDE_LEFT | CHANNEL_SIDE_RIGHT))) { // no asymetric side
+      return false;
+    }
+    if (!even(layout & (CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT))) {
+      return false;
+    }
+    if (!even(layout &
+              (CHANNEL_FRONT_LEFT_OF_CENTER | CHANNEL_FRONT_RIGHT_OF_CENTER))) {
+      return false;
+    }
+    if (cubeb_channel_layout_nb_channels(layout) >= CHANNELS_MAX) {
+      return false;
+    }
+    return true;
+  }
+
+  int auto_matrix();
+  int init();
+
+  const cubeb_sample_format _format;
+  const cubeb_channel_layout _in_ch_layout;              ///< input channel layout
+  const cubeb_channel_layout _out_ch_layout;             ///< output channel layout
+  const uint32_t _in_ch_count;                           ///< input channel count
+  const uint32_t _out_ch_count;                          ///< output channel count
+  const float _surround_mix_level = C_30DB;              ///< surround mixing level
+  const float _center_mix_level = C_30DB;                ///< center mixing level
+  const float _lfe_mix_level = 1;                        ///< LFE mixing level
+  double _matrix[CHANNELS_MAX][CHANNELS_MAX] = {{ 0 }};        ///< floating point rematrixing coefficients
+  float _matrix_flt[CHANNELS_MAX][CHANNELS_MAX] = {{ 0 }};     ///< single precision floating point rematrixing coefficients
+  int32_t _matrix32[CHANNELS_MAX][CHANNELS_MAX] = {{ 0 }};     ///< 17.15 fixed point rematrixing coefficients
+  uint8_t _matrix_ch[CHANNELS_MAX][CHANNELS_MAX+1] = {{ 0 }};  ///< Lists of input channels per output channel that have non zero rematrixing coefficients
+  bool _clipping = false;                          ///< Set to true if clipping detection is required
+  bool _valid = false;                             ///< Set to true if context is valid.
+};
+
+int MixerContext::auto_matrix()
+{
+  double matrix[NUM_NAMED_CHANNELS][NUM_NAMED_CHANNELS] = { { 0 } };
+  double maxcoef = 0;
+  float maxval;
+
+  cubeb_channel_layout in_ch_layout = clean_layout(_in_ch_layout);
+  cubeb_channel_layout out_ch_layout = clean_layout(_out_ch_layout);
+
+  if (!sane_layout(in_ch_layout)) {
+    // Channel Not Supported
+    LOG("Input Layout %x is not supported", _in_ch_layout);
+    return -1;
+  }
+
+  if (!sane_layout(out_ch_layout)) {
+    LOG("Output Layout %x is not supported", _out_ch_layout);
+    return -1;
+  }
+
+  for (uint32_t i = 0; i < FF_ARRAY_ELEMS(matrix); i++) {
+    if (in_ch_layout & out_ch_layout & (1U << i)) {
+      matrix[i][i] = 1.0;
+    }
+  }
+
+  cubeb_channel_layout unaccounted = in_ch_layout & ~out_ch_layout;
+
+  // Rematrixing is done via a matrix of coefficient that should be applied to
+  // all channels. Channels are treated as pair and must be symmetrical (if a
+  // left channel exists, the corresponding right should exist too) unless the
+  // output layout has similar layout. Channels are then mixed toward the front
+  // center or back center if they exist with a slight bias toward the front.
+
+  if (unaccounted & CHANNEL_FRONT_CENTER) {
+    if ((out_ch_layout & CUBEB_LAYOUT_STEREO) == CUBEB_LAYOUT_STEREO) {
+      if (in_ch_layout & CUBEB_LAYOUT_STEREO) {
+        matrix[FRONT_LEFT][FRONT_CENTER] += _center_mix_level;
+        matrix[FRONT_RIGHT][FRONT_CENTER] += _center_mix_level;
+      } else {
+        matrix[FRONT_LEFT][FRONT_CENTER] += M_SQRT1_2;
+        matrix[FRONT_RIGHT][FRONT_CENTER] += M_SQRT1_2;
+      }
+    }
+  }
+  if (unaccounted & CUBEB_LAYOUT_STEREO) {
+    if (out_ch_layout & CHANNEL_FRONT_CENTER) {
+      matrix[FRONT_CENTER][FRONT_LEFT] += M_SQRT1_2;
+      matrix[FRONT_CENTER][FRONT_RIGHT] += M_SQRT1_2;
+      if (in_ch_layout & CHANNEL_FRONT_CENTER)
+        matrix[FRONT_CENTER][FRONT_CENTER] = _center_mix_level * M_SQRT2;
+    }
+  }
+
+  if (unaccounted & CHANNEL_BACK_CENTER) {
+    if (out_ch_layout & CHANNEL_BACK_LEFT) {
+      matrix[BACK_LEFT][BACK_CENTER] += M_SQRT1_2;
+      matrix[BACK_RIGHT][BACK_CENTER] += M_SQRT1_2;
+    } else if (out_ch_layout & CHANNEL_SIDE_LEFT) {
+      matrix[SIDE_LEFT][BACK_CENTER] += M_SQRT1_2;
+      matrix[SIDE_RIGHT][BACK_CENTER] += M_SQRT1_2;
+    } else if (out_ch_layout & CHANNEL_FRONT_LEFT) {
+      matrix[FRONT_LEFT][BACK_CENTER] += _surround_mix_level * M_SQRT1_2;
+      matrix[FRONT_RIGHT][BACK_CENTER] += _surround_mix_level * M_SQRT1_2;
+    } else if (out_ch_layout & CHANNEL_FRONT_CENTER) {
+      matrix[FRONT_CENTER][BACK_CENTER] +=
+        _surround_mix_level * M_SQRT1_2;
+    }
+  }
+  if (unaccounted & CHANNEL_BACK_LEFT) {
+    if (out_ch_layout & CHANNEL_BACK_CENTER) {
+      matrix[BACK_CENTER][BACK_LEFT] += M_SQRT1_2;
+      matrix[BACK_CENTER][BACK_RIGHT] += M_SQRT1_2;
+    } else if (out_ch_layout & CHANNEL_SIDE_LEFT) {
+      if (in_ch_layout & CHANNEL_SIDE_LEFT) {
+        matrix[SIDE_LEFT][BACK_LEFT] += M_SQRT1_2;
+        matrix[SIDE_RIGHT][BACK_RIGHT] += M_SQRT1_2;
+      } else {
+        matrix[SIDE_LEFT][BACK_LEFT] += 1.0;
+        matrix[SIDE_RIGHT][BACK_RIGHT] += 1.0;
+      }
+    } else if (out_ch_layout & CHANNEL_FRONT_LEFT) {
+      matrix[FRONT_LEFT][BACK_LEFT] += _surround_mix_level;
+      matrix[FRONT_RIGHT][BACK_RIGHT] += _surround_mix_level;
+    } else if (out_ch_layout & CHANNEL_FRONT_CENTER) {
+      matrix[FRONT_CENTER][BACK_LEFT] += _surround_mix_level * M_SQRT1_2;
+      matrix[FRONT_CENTER][BACK_RIGHT] += _surround_mix_level * M_SQRT1_2;
+    }
+  }
+
+  if (unaccounted & CHANNEL_SIDE_LEFT) {
+    if (out_ch_layout & CHANNEL_BACK_LEFT) {
+      /* if back channels do not exist in the input, just copy side
+         channels to back channels, otherwise mix side into back */
+      if (in_ch_layout & CHANNEL_BACK_LEFT) {
+        matrix[BACK_LEFT][SIDE_LEFT] += M_SQRT1_2;
+        matrix[BACK_RIGHT][SIDE_RIGHT] += M_SQRT1_2;
+      } else {
+        matrix[BACK_LEFT][SIDE_LEFT] += 1.0;
+        matrix[BACK_RIGHT][SIDE_RIGHT] += 1.0;
+      }
+    } else if (out_ch_layout & CHANNEL_BACK_CENTER) {
+      matrix[BACK_CENTER][SIDE_LEFT] += M_SQRT1_2;
+      matrix[BACK_CENTER][SIDE_RIGHT] += M_SQRT1_2;
+    } else if (out_ch_layout & CHANNEL_FRONT_LEFT) {
+      matrix[FRONT_LEFT][SIDE_LEFT] += _surround_mix_level;
+      matrix[FRONT_RIGHT][SIDE_RIGHT] += _surround_mix_level;
+    } else if (out_ch_layout & CHANNEL_FRONT_CENTER) {
+      matrix[FRONT_CENTER][SIDE_LEFT] += _surround_mix_level * M_SQRT1_2;
+      matrix[FRONT_CENTER][SIDE_RIGHT] += _surround_mix_level * M_SQRT1_2;
+    }
+  }
+
+  if (unaccounted & CHANNEL_FRONT_LEFT_OF_CENTER) {
+    if (out_ch_layout & CHANNEL_FRONT_LEFT) {
+      matrix[FRONT_LEFT][FRONT_LEFT_OF_CENTER] += 1.0;
+      matrix[FRONT_RIGHT][FRONT_RIGHT_OF_CENTER] += 1.0;
+    } else if (out_ch_layout & CHANNEL_FRONT_CENTER) {
+      matrix[FRONT_CENTER][FRONT_LEFT_OF_CENTER] += M_SQRT1_2;
+      matrix[FRONT_CENTER][FRONT_RIGHT_OF_CENTER] += M_SQRT1_2;
+    }
+  }
+  /* mix LFE into front left/right or center */
+  if (unaccounted & CHANNEL_LOW_FREQUENCY) {
+    if (out_ch_layout & CHANNEL_FRONT_CENTER) {
+      matrix[FRONT_CENTER][LOW_FREQUENCY] += _lfe_mix_level;
+    } else if (out_ch_layout & CHANNEL_FRONT_LEFT) {
+      matrix[FRONT_LEFT][LOW_FREQUENCY] += _lfe_mix_level * M_SQRT1_2;
+      matrix[FRONT_RIGHT][LOW_FREQUENCY] += _lfe_mix_level * M_SQRT1_2;
+    }
+  }
+
+  // Normalize the conversion matrix.
+  for (uint32_t out_i = 0, i = 0; i < CHANNELS_MAX; i++) {
+    double sum = 0;
+    int in_i = 0;
+    if ((out_ch_layout & (1U << i)) == 0) {
+      continue;
+    }
+    for (uint32_t j = 0; j < CHANNELS_MAX; j++) {
+      if ((in_ch_layout & (1U << j)) == 0) {
+        continue;
+      }
+      if (i < FF_ARRAY_ELEMS(matrix) && j < FF_ARRAY_ELEMS(matrix[0])) {
+        _matrix[out_i][in_i] = matrix[i][j];
+      } else {
+        _matrix[out_i][in_i] =
+          i == j && (in_ch_layout & out_ch_layout & (1U << i));
+      }
+      sum += fabs(_matrix[out_i][in_i]);
+      in_i++;
+    }
+    maxcoef = std::max(maxcoef, sum);
+    out_i++;
+  }
+
+  if (_format == CUBEB_SAMPLE_S16NE) {
+    maxval = 1.0;
+  } else {
+    maxval = INT_MAX;
+  }
+
+  // Normalize matrix if needed.
+  if (maxcoef > maxval) {
+    maxcoef /= maxval;
+    for (uint32_t i = 0; i < CHANNELS_MAX; i++)
+      for (uint32_t j = 0; j < CHANNELS_MAX; j++) {
+        _matrix[i][j] /= maxcoef;
+      }
+  }
+
+  if (_format == CUBEB_SAMPLE_FLOAT32NE) {
+    for (uint32_t i = 0; i < FF_ARRAY_ELEMS(_matrix); i++) {
+      for (uint32_t j = 0; j < FF_ARRAY_ELEMS(_matrix[0]); j++) {
+        _matrix_flt[i][j] = _matrix[i][j];
+      }
+    }
+  }
+
+  return 0;
+}
+
+int MixerContext::init()
+{
+  int r = auto_matrix();
+  if (r) {
+    return r;
+  }
+
+  // Determine if matrix operation would overflow
+  if (_format == CUBEB_SAMPLE_S16NE) {
+    int maxsum = 0;
+    for (uint32_t i = 0; i < _out_ch_count; i++) {
+      double rem = 0;
+      int sum = 0;
+
+      for (uint32_t j = 0; j < _in_ch_count; j++) {
+        double target = _matrix[i][j] * 32768 + rem;
+        int value = lrintf(target);
+        rem += target - value;
+        sum += std::abs(value);
+      }
+      maxsum = std::max(maxsum, sum);
+    }
+    if (maxsum > 32768) {
+      _clipping = true;
+    }
+  }
+
+  // FIXME quantize for integers
+  for (uint32_t i = 0; i < CHANNELS_MAX; i++) {
+    int ch_in = 0;
+    for (uint32_t j = 0; j < CHANNELS_MAX; j++) {
+      _matrix32[i][j] = lrintf(_matrix[i][j] * 32768);
+      if (_matrix[i][j]) {
+        _matrix_ch[i][++ch_in] = j;
+      }
+    }
+    _matrix_ch[i][0] = ch_in;
+  }
+
+  return 0;
+}
+
+template<typename TYPE_SAMPLE, typename TYPE_COEFF, typename F>
+void
+sum2(TYPE_SAMPLE * out,
+     uint32_t stride_out,
+     const TYPE_SAMPLE * in1,
+     const TYPE_SAMPLE * in2,
+     uint32_t stride_in,
+     TYPE_COEFF coeff1,
+     TYPE_COEFF coeff2,
+     F&& operand,
+     uint32_t frames)
+{
+  static_assert(
+    std::is_same<TYPE_COEFF,
+                 typename std::result_of<F(TYPE_COEFF)>::type>::value,
+    "function must return the same type as used by matrix_coeff");
+  for (uint32_t i = 0; i < frames; i++) {
+    *out = operand(coeff1 * *in1 + coeff2 * *in2);
+    out += stride_out;
+    in1 += stride_in;
+    in2 += stride_in;
+  }
+}
+
+template<typename TYPE_SAMPLE, typename TYPE_COEFF, typename F>
+void
+copy(TYPE_SAMPLE * out,
+     uint32_t stride_out,
+     const TYPE_SAMPLE * in,
+     uint32_t stride_in,
+     TYPE_COEFF coeff,
+     F&& operand,
+     uint32_t frames)
+{
+  static_assert(
+    std::is_same<TYPE_COEFF,
+                 typename std::result_of<F(TYPE_COEFF)>::type>::value,
+    "function must return the same type as used by matrix_coeff");
+  for (uint32_t i = 0; i < frames; i++) {
+    *out = operand(coeff * *in);
+    out += stride_out;
+    in += stride_in;
+  }
+}
+
+template <typename TYPE, typename TYPE_COEFF, size_t COLS, typename F>
+static int rematrix(const MixerContext * s, TYPE * aOut, const TYPE * aIn,
+                    const TYPE_COEFF (&matrix_coeff)[COLS][COLS],
+                    F&& aF, uint32_t frames)
+{
+  static_assert(
+    std::is_same<TYPE_COEFF,
+                 typename std::result_of<F(TYPE_COEFF)>::type>::value,
+    "function must return the same type as used by matrix_coeff");
+
+  for (uint32_t out_i = 0; out_i < s->_out_ch_count; out_i++) {
+    TYPE* out = aOut + out_i;
+    switch (s->_matrix_ch[out_i][0]) {
+      case 0:
+        for (uint32_t i = 0; i < frames; i++) {
+          out[i * s->_out_ch_count] = 0;
+        }
+        break;
+      case 1: {
+        int in_i = s->_matrix_ch[out_i][1];
+        copy(out,
+             s->_out_ch_count,
+             aIn + in_i,
+             s->_in_ch_count,
+             matrix_coeff[out_i][in_i],
+             aF,
+             frames);
+      } break;
+      case 2:
+        sum2(out,
+             s->_out_ch_count,
+             aIn + s->_matrix_ch[out_i][1],
+             aIn + s->_matrix_ch[out_i][2],
+             s->_in_ch_count,
+             matrix_coeff[out_i][s->_matrix_ch[out_i][1]],
+             matrix_coeff[out_i][s->_matrix_ch[out_i][2]],
+             aF,
+             frames);
+        break;
+      default:
+        for (uint32_t i = 0; i < frames; i++) {
+          TYPE_COEFF v = 0;
+          for (uint32_t j = 0; j < s->_matrix_ch[out_i][0]; j++) {
+            uint32_t in_i = s->_matrix_ch[out_i][1 + j];
+            v +=
+              *(aIn + in_i + i * s->_in_ch_count) * matrix_coeff[out_i][in_i];
+          }
+          out[i * s->_out_ch_count] = aF(v);
+        }
+        break;
+    }
+  }
+  return 0;
+}
+
+struct cubeb_mixer
+{
+  cubeb_mixer(cubeb_sample_format format,
+              uint32_t in_channels,
+              cubeb_channel_layout in_layout,
+              uint32_t out_channels,
+              cubeb_channel_layout out_layout)
+    : _context(format, in_channels, in_layout, out_channels, out_layout)
+  {
+  }
+
+  template<typename T>
+  void copy_and_trunc(size_t frames,
+                      const T * input_buffer,
+                      T * output_buffer) const
+  {
+    if (_context._in_ch_count <= _context._out_ch_count) {
+      // Not enough channels to copy, fill the gaps with silence.
+      if (_context._in_ch_count == 1 && _context._out_ch_count >= 2) {
+        // Special case for upmixing mono input to stereo and more. We will
+        // duplicate the mono channel to the first two channels. On most system,
+        // the first two channels are for left and right. It is commonly
+        // expected that mono will on both left+right channels
+        for (uint32_t i = 0; i < frames; i++) {
+          output_buffer[0] = output_buffer[1] = *input_buffer;
+          PodZero(output_buffer + 2, _context._out_ch_count - 2);
+          output_buffer += _context._out_ch_count;
+          input_buffer++;
+        }
+        return;
+      }
+      for (uint32_t i = 0; i < frames; i++) {
+        PodCopy(output_buffer, input_buffer, _context._in_ch_count);
+        output_buffer += _context._in_ch_count;
+        input_buffer += _context._in_ch_count;
+        PodZero(output_buffer, _context._out_ch_count - _context._in_ch_count);
+        output_buffer += _context._out_ch_count - _context._in_ch_count;
+      }
+    } else {
+      for (uint32_t i = 0; i < frames; i++) {
+        PodCopy(output_buffer, input_buffer, _context._out_ch_count);
+        output_buffer += _context._out_ch_count;
+        input_buffer += _context._in_ch_count;
+      }
+    }
+  }
+
+  int mix(size_t frames,
+          const void * input_buffer,
+          size_t input_buffer_size,
+          void * output_buffer,
+          size_t output_buffer_size) const
+  {
+    if (frames <= 0 || _context._out_ch_count == 0) {
+      return 0;
+    }
+
+    // Check if output buffer is of sufficient size.
+    size_t size_read_needed =
+      frames * _context._in_ch_count * cubeb_sample_size(_context._format);
+    if (input_buffer_size < size_read_needed) {
+      // We don't have enough data to read!
+      return -1;
+    }
+    if (output_buffer_size * _context._in_ch_count <
+        size_read_needed * _context._out_ch_count) {
+      return -1;
+    }
+
+    if (!valid()) {
+      // The channel layouts were invalid or unsupported, instead we will simply
+      // either drop the extra channels, or fill with silence the missing ones
+      if (_context._format == CUBEB_SAMPLE_FLOAT32NE) {
+        copy_and_trunc(frames,
+                       static_cast<const float*>(input_buffer),
+                       static_cast<float*>(output_buffer));
+      } else {
+        assert(_context._format == CUBEB_SAMPLE_S16NE);
+        copy_and_trunc(frames,
+                       static_cast<const int16_t*>(input_buffer),
+                       reinterpret_cast<int16_t*>(output_buffer));
+      }
+      return 0;
+    }
+
+    switch (_context._format)
+    {
+      case CUBEB_SAMPLE_FLOAT32NE: {
+        auto f = [](float x) { return x; };
+        return rematrix(&_context,
+                        static_cast<float*>(output_buffer),
+                        static_cast<const float*>(input_buffer),
+                        _context._matrix_flt,
+                        f,
+                        frames);
+      }
+      case CUBEB_SAMPLE_S16NE:
+        if (_context._clipping) {
+          auto f = [](int x) {
+            int y = (x + 16384) >> 15;
+            // clip the signed integer value into the -32768,32767 range.
+            if ((y + 0x8000U) & ~0xFFFF) {
+              return (y >> 31) ^ 0x7FFF;
+            }
+            return y;
+          };
+          return rematrix(&_context,
+                          static_cast<int16_t*>(output_buffer),
+                          static_cast<const int16_t*>(input_buffer),
+                          _context._matrix32,
+                          f,
+                          frames);
+        } else {
+          auto f = [](int x) { return (x + 16384) >> 15; };
+          return rematrix(&_context,
+                          static_cast<int16_t*>(output_buffer),
+                          static_cast<const int16_t*>(input_buffer),
+                          _context._matrix32,
+                          f,
+                          frames);
+        }
+        break;
+      default:
+        assert(false);
+        break;
+    }
+
+    return -1;
+  }
+
+  // Return false if any of the input or ouput layout were invalid.
+  bool valid() const { return _context._valid; }
+
+  virtual ~cubeb_mixer(){};
+
+  MixerContext _context;
+};
+
+cubeb_mixer* cubeb_mixer_create(cubeb_sample_format format,
+                                uint32_t in_channels,
+                                cubeb_channel_layout in_layout,
+                                uint32_t out_channels,
+                                cubeb_channel_layout out_layout)
+{
+  return new cubeb_mixer(
+    format, in_channels, in_layout, out_channels, out_layout);
+}
+
+void cubeb_mixer_destroy(cubeb_mixer * mixer)
+{
+  delete mixer;
+}
+
+int cubeb_mixer_mix(cubeb_mixer * mixer,
+                    size_t frames,
+                    const void * input_buffer,
+                    size_t input_buffer_size,
+                    void * output_buffer,
+                    size_t output_buffer_size)
+{
+  return mixer->mix(
+    frames, input_buffer, input_buffer_size, output_buffer, output_buffer_size);
+}
diff --git a/dep/cubeb/src/cubeb_mixer.h b/dep/cubeb/src/cubeb_mixer.h
new file mode 100644
index 000000000..d43a237f9
--- /dev/null
+++ b/dep/cubeb/src/cubeb_mixer.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright © 2016 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+
+#ifndef CUBEB_MIXER
+#define CUBEB_MIXER
+
+#include "cubeb/cubeb.h" // for cubeb_channel_layout and cubeb_stream_params.
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef struct cubeb_mixer cubeb_mixer;
+cubeb_mixer * cubeb_mixer_create(cubeb_sample_format format,
+                                 uint32_t in_channels,
+                                 cubeb_channel_layout in_layout,
+                                 uint32_t out_channels,
+                                 cubeb_channel_layout out_layout);
+void cubeb_mixer_destroy(cubeb_mixer * mixer);
+int cubeb_mixer_mix(cubeb_mixer * mixer,
+                    size_t frames,
+                    const void * input_buffer,
+                    size_t input_buffer_size,
+                    void * output_buffer,
+                    size_t output_buffer_size);
+
+unsigned int cubeb_channel_layout_nb_channels(cubeb_channel_layout channel_layout);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif // CUBEB_MIXER
diff --git a/dep/cubeb/src/cubeb_opensl.c b/dep/cubeb/src/cubeb_opensl.c
new file mode 100644
index 000000000..021390e66
--- /dev/null
+++ b/dep/cubeb/src/cubeb_opensl.c
@@ -0,0 +1,1760 @@
+/*
+ * Copyright © 2012 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+#undef NDEBUG
+#include <assert.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <errno.h>
+#include <SLES/OpenSLES.h>
+#include <math.h>
+#include <time.h>
+#if defined(__ANDROID__)
+#include <dlfcn.h>
+#include <sys/system_properties.h>
+#include "android/sles_definitions.h"
+#include <SLES/OpenSLES_Android.h>
+#include <android/log.h>
+#include <android/api-level.h>
+#endif
+#include "cubeb/cubeb.h"
+#include "cubeb-internal.h"
+#include "cubeb_resampler.h"
+#include "cubeb-sles.h"
+#include "cubeb_array_queue.h"
+#include "android/cubeb-output-latency.h"
+
+#if defined(__ANDROID__)
+#ifdef LOG
+#undef LOG
+#endif
+//#define LOGGING_ENABLED
+#ifdef LOGGING_ENABLED
+#define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "Cubeb_OpenSL" , ## args)
+#else
+#define LOG(...)
+#endif
+
+//#define TIMESTAMP_ENABLED
+#ifdef TIMESTAMP_ENABLED
+#define FILENAME (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
+#define LOG_TS(args...)  __android_log_print(ANDROID_LOG_INFO, "Cubeb_OpenSL ES: Timestamp(usec)" , ## args)
+#define TIMESTAMP(msg) do {                           \
+  struct timeval timestamp;                           \
+  int ts_ret = gettimeofday(&timestamp, NULL);        \
+  if (ts_ret == 0) {                                  \
+    LOG_TS("%lld: %s (%s %s:%d)", timestamp.tv_sec * 1000000LL + timestamp.tv_usec, msg, __FUNCTION__, FILENAME, __LINE__);\
+  } else {                                            \
+    LOG_TS("Error: %s (%s %s:%d) - %s", msg, __FUNCTION__, FILENAME, __LINE__);\
+  }                                                   \
+} while(0)
+#else
+#define TIMESTAMP(...)
+#endif
+
+#define ANDROID_VERSION_GINGERBREAD_MR1 10
+#define ANDROID_VERSION_JELLY_BEAN 18
+#define ANDROID_VERSION_LOLLIPOP 21
+#define ANDROID_VERSION_MARSHMALLOW 23
+#define ANDROID_VERSION_N_MR1 25
+#endif
+
+#define DEFAULT_SAMPLE_RATE 48000
+#define DEFAULT_NUM_OF_FRAMES 480
+// If the latency requested is above this threshold, this stream is considered
+// intended for playback (vs. real-time). Tell Android it should favor saving
+// power over performance or latency.
+// This is around 100ms at 44100 or 48000
+#define POWERSAVE_LATENCY_FRAMES_THRESHOLD 4000
+
+static struct cubeb_ops const opensl_ops;
+
+struct cubeb {
+  struct cubeb_ops const * ops;
+  void * lib;
+  SLInterfaceID SL_IID_BUFFERQUEUE;
+  SLInterfaceID SL_IID_PLAY;
+#if defined(__ANDROID__)
+  SLInterfaceID SL_IID_ANDROIDCONFIGURATION;
+  SLInterfaceID SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
+#endif
+  SLInterfaceID SL_IID_VOLUME;
+  SLInterfaceID SL_IID_RECORD;
+  SLObjectItf engObj;
+  SLEngineItf eng;
+  SLObjectItf outmixObj;
+  output_latency_function * p_output_latency_function;
+};
+
+#define NELEMS(A) (sizeof(A) / sizeof A[0])
+#define NBUFS 2
+
+struct cubeb_stream {
+  /* Note: Must match cubeb_stream layout in cubeb.c. */
+  cubeb * context;
+  void * user_ptr;
+  /**/
+  pthread_mutex_t mutex;
+  SLObjectItf playerObj;
+  SLPlayItf play;
+  SLBufferQueueItf bufq;
+  SLVolumeItf volume;
+  void ** queuebuf;
+  uint32_t queuebuf_capacity;
+  int queuebuf_idx;
+  long queuebuf_len;
+  long bytespersec;
+  long framesize;
+  /* Total number of played frames.
+   * Synchronized by stream::mutex lock. */
+  long written;
+  /* Flag indicating draining. Synchronized
+   * by stream::mutex lock. */
+  int draining;
+  /* Flags to determine in/out.*/
+  uint32_t input_enabled;
+  uint32_t output_enabled;
+  /* Recorder abstract object. */
+  SLObjectItf recorderObj;
+  /* Recorder Itf for input capture. */
+  SLRecordItf recorderItf;
+  /* Buffer queue for input capture. */
+  SLAndroidSimpleBufferQueueItf recorderBufferQueueItf;
+  /* Store input buffers. */
+  void ** input_buffer_array;
+  /* The capacity of the array.
+   * On capture only can be small (4).
+   * On full duplex is calculated to
+   * store 1 sec of data buffers. */
+  uint32_t input_array_capacity;
+  /* Current filled index of input buffer array.
+   * It is initiated to -1 indicating buffering
+   * have not started yet. */
+  int input_buffer_index;
+  /* Length of input buffer.*/
+  uint32_t input_buffer_length;
+  /* Input frame size */
+  uint32_t input_frame_size;
+  /* Device sampling rate. If user rate is not
+   * accepted an compatible rate is set. If it is
+   * accepted this is equal to params.rate. */
+  uint32_t input_device_rate;
+  /* Exchange input buffers between input
+   * and full duplex threads. */
+  array_queue * input_queue;
+  /* Silent input buffer used on full duplex. */
+  void * input_silent_buffer;
+  /* Number of input frames from the start of the stream*/
+  uint32_t input_total_frames;
+  /* Flag to stop the execution of user callback and
+   * close all working threads. Synchronized by
+   * stream::mutex lock. */
+  uint32_t shutdown;
+  /* Store user callback. */
+  cubeb_data_callback data_callback;
+  /* Store state callback. */
+  cubeb_state_callback state_callback;
+
+  cubeb_resampler * resampler;
+  unsigned int user_output_rate;
+  unsigned int output_configured_rate;
+  unsigned int buffer_size_frames;
+  // Audio output latency used in cubeb_stream_get_position().
+  unsigned int output_latency_ms;
+  int64_t lastPosition;
+  int64_t lastPositionTimeStamp;
+  int64_t lastCompensativePosition;
+  int voice;
+};
+
+/* Forward declaration. */
+static int opensl_stop_player(cubeb_stream * stm);
+static int opensl_stop_recorder(cubeb_stream * stm);
+
+static int
+opensl_get_draining(cubeb_stream * stm)
+{
+#ifdef DEBUG
+  int r = pthread_mutex_trylock(&stm->mutex);
+  assert((r == EDEADLK || r == EBUSY) && "get_draining: mutex should be locked but it's not.");
+#endif
+  return stm->draining;
+}
+
+static void
+opensl_set_draining(cubeb_stream * stm, int value)
+{
+#ifdef DEBUG
+  int r = pthread_mutex_trylock(&stm->mutex);
+  LOG("set draining try r = %d", r);
+  assert((r == EDEADLK || r == EBUSY) && "set_draining: mutex should be locked but it's not.");
+#endif
+  assert(value == 0 || value == 1);
+  stm->draining = value;
+}
+
+static void
+opensl_notify_drained(cubeb_stream * stm)
+{
+  assert(stm);
+  int r = pthread_mutex_lock(&stm->mutex);
+  assert(r == 0);
+  int draining = opensl_get_draining(stm);
+  r = pthread_mutex_unlock(&stm->mutex);
+  assert(r == 0);
+  if (draining) {
+    stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
+    if (stm->play) {
+      LOG("stop player in play_callback");
+      r = opensl_stop_player(stm);
+      assert(r == CUBEB_OK);
+    }
+    if (stm->recorderItf) {
+      r = opensl_stop_recorder(stm);
+      assert(r == CUBEB_OK);
+    }
+  }
+}
+
+static uint32_t
+opensl_get_shutdown(cubeb_stream * stm)
+{
+#ifdef DEBUG
+  int r = pthread_mutex_trylock(&stm->mutex);
+  assert((r == EDEADLK || r == EBUSY) && "get_shutdown: mutex should be locked but it's not.");
+#endif
+  return stm->shutdown;
+}
+
+static void
+opensl_set_shutdown(cubeb_stream * stm, uint32_t value)
+{
+#ifdef DEBUG
+  int r = pthread_mutex_trylock(&stm->mutex);
+  LOG("set shutdown try r = %d", r);
+  assert((r == EDEADLK || r == EBUSY) && "set_shutdown: mutex should be locked but it's not.");
+#endif
+  assert(value == 0 || value == 1);
+  stm->shutdown = value;
+}
+
+static void
+play_callback(SLPlayItf caller, void * user_ptr, SLuint32 event)
+{
+  cubeb_stream * stm = user_ptr;
+  assert(stm);
+  switch (event) {
+    case SL_PLAYEVENT_HEADATMARKER:
+      opensl_notify_drained(stm);
+    break;
+  default:
+    break;
+  }
+}
+
+static void
+recorder_marker_callback (SLRecordItf caller, void * pContext, SLuint32 event)
+{
+  cubeb_stream * stm = pContext;
+  assert(stm);
+
+  if (event == SL_RECORDEVENT_HEADATMARKER) {
+    int r = pthread_mutex_lock(&stm->mutex);
+    assert(r == 0);
+    int draining = opensl_get_draining(stm);
+    r = pthread_mutex_unlock(&stm->mutex);
+    assert(r == 0);
+    if (draining) {
+      stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
+      if (stm->recorderItf) {
+        r = opensl_stop_recorder(stm);
+        assert(r == CUBEB_OK);
+      }
+      if (stm->play) {
+        r = opensl_stop_player(stm);
+        assert(r == CUBEB_OK);
+      }
+    }
+  }
+}
+
+static void
+bufferqueue_callback(SLBufferQueueItf caller, void * user_ptr)
+{
+  cubeb_stream * stm = user_ptr;
+  assert(stm);
+  SLBufferQueueState state;
+  SLresult res;
+  long written = 0;
+
+  res = (*stm->bufq)->GetState(stm->bufq, &state);
+  assert(res == SL_RESULT_SUCCESS);
+
+  if (state.count > 1) {
+    return;
+  }
+
+  uint8_t *buf = stm->queuebuf[stm->queuebuf_idx];
+  written = 0;
+  int r = pthread_mutex_lock(&stm->mutex);
+  assert(r == 0);
+  int draining = opensl_get_draining(stm);
+  uint32_t shutdown = opensl_get_shutdown(stm);
+  r = pthread_mutex_unlock(&stm->mutex);
+  assert(r == 0);
+  if (!draining && !shutdown) {
+    written = cubeb_resampler_fill(stm->resampler,
+                                   NULL, NULL,
+                                   buf, stm->queuebuf_len / stm->framesize);
+    LOG("bufferqueue_callback: resampler fill returned %ld frames", written);
+    if (written < 0 || written * stm->framesize > stm->queuebuf_len) {
+      r = pthread_mutex_lock(&stm->mutex);
+      assert(r == 0);
+      opensl_set_shutdown(stm, 1);
+      r = pthread_mutex_unlock(&stm->mutex);
+      assert(r == 0);
+      opensl_stop_player(stm);
+      stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
+      return;
+    }
+  }
+
+  // Keep sending silent data even in draining mode to prevent the audio
+  // back-end from being stopped automatically by OpenSL/ES.
+  assert(stm->queuebuf_len >= written * stm->framesize);
+  memset(buf + written * stm->framesize, 0, stm->queuebuf_len - written * stm->framesize);
+  res = (*stm->bufq)->Enqueue(stm->bufq, buf, stm->queuebuf_len);
+  assert(res == SL_RESULT_SUCCESS);
+  stm->queuebuf_idx = (stm->queuebuf_idx + 1) % stm->queuebuf_capacity;
+
+  if (written > 0) {
+    pthread_mutex_lock(&stm->mutex);
+    stm->written += written;
+    pthread_mutex_unlock(&stm->mutex);
+  }
+
+  if (!draining && written * stm->framesize < stm->queuebuf_len) {
+    LOG("bufferqueue_callback draining");
+    r = pthread_mutex_lock(&stm->mutex);
+    assert(r == 0);
+    int64_t written_duration = INT64_C(1000) * stm->written * stm->framesize / stm->bytespersec;
+    opensl_set_draining(stm, 1);
+    r = pthread_mutex_unlock(&stm->mutex);
+    assert(r == 0);
+
+    if (written_duration == 0) {
+      // since we didn't write any sample, it's not possible to reach the marker
+      // time and trigger the callback. We should initiative notify drained.
+      opensl_notify_drained(stm);
+    } else {
+      // Use SL_PLAYEVENT_HEADATMARKER event from slPlayCallback of SLPlayItf
+      // to make sure all the data has been processed.
+      (*stm->play)->SetMarkerPosition(stm->play, (SLmillisecond)written_duration);
+    }
+    return;
+  }
+}
+
+static int
+opensl_enqueue_recorder(cubeb_stream * stm, void ** last_filled_buffer)
+{
+  assert(stm);
+
+  int current_index = stm->input_buffer_index;
+  void * last_buffer = NULL;
+
+  if (current_index < 0) {
+    // This is the first enqueue
+    current_index = 0;
+  } else {
+    // The current index hold the last filled buffer get it before advance index.
+    last_buffer = stm->input_buffer_array[current_index];
+    // Advance to get next available buffer
+    current_index = (current_index + 1) % stm->input_array_capacity;
+  }
+  // enqueue next empty buffer to be filled by the recorder
+  SLresult res = (*stm->recorderBufferQueueItf)->Enqueue(stm->recorderBufferQueueItf,
+                                                         stm->input_buffer_array[current_index],
+                                                         stm->input_buffer_length);
+  if (res != SL_RESULT_SUCCESS ) {
+    LOG("Enqueue recorder failed. Error code: %lu", res);
+    return CUBEB_ERROR;
+  }
+  // All good, update buffer and index.
+  stm->input_buffer_index = current_index;
+  if (last_filled_buffer) {
+    *last_filled_buffer = last_buffer;
+  }
+  return CUBEB_OK;
+}
+
+// input data callback
+void recorder_callback(SLAndroidSimpleBufferQueueItf bq, void * context)
+{
+  assert(context);
+  cubeb_stream * stm = context;
+  assert(stm->recorderBufferQueueItf);
+
+  int r = pthread_mutex_lock(&stm->mutex);
+  assert(r == 0);
+  uint32_t shutdown = opensl_get_shutdown(stm);
+  int draining = opensl_get_draining(stm);
+  r = pthread_mutex_unlock(&stm->mutex);
+  assert(r == 0);
+
+  if (shutdown || draining) {
+    // According to the OpenSL ES 1.1 Specification, 8.14 SLBufferQueueItf
+    // page 184, on transition to the SL_RECORDSTATE_STOPPED state,
+    // the application should continue to enqueue buffers onto the queue
+    // to retrieve the residual recorded data in the system.
+    r = opensl_enqueue_recorder(stm, NULL);
+    assert(r == CUBEB_OK);
+    return;
+  }
+
+  // Enqueue next available buffer and get the last filled buffer.
+  void * input_buffer = NULL;
+  r = opensl_enqueue_recorder(stm, &input_buffer);
+  assert(r == CUBEB_OK);
+  assert(input_buffer);
+  // Fill resampler with last input
+  long input_frame_count = stm->input_buffer_length / stm->input_frame_size;
+  long got = cubeb_resampler_fill(stm->resampler,
+                                  input_buffer,
+                                  &input_frame_count,
+                                  NULL,
+                                  0);
+  // Error case
+  if (got < 0 || got > input_frame_count) {
+    r = pthread_mutex_lock(&stm->mutex);
+    assert(r == 0);
+    opensl_set_shutdown(stm, 1);
+    r = pthread_mutex_unlock(&stm->mutex);
+    assert(r == 0);
+    r = opensl_stop_recorder(stm);
+    assert(r == CUBEB_OK);
+    stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
+  }
+
+  // Advance total stream frames
+  stm->input_total_frames += got;
+
+  if (got < input_frame_count) {
+    r = pthread_mutex_lock(&stm->mutex);
+    assert(r == 0);
+    opensl_set_draining(stm, 1);
+    r = pthread_mutex_unlock(&stm->mutex);
+    assert(r == 0);
+    int64_t duration = INT64_C(1000) * stm->input_total_frames / stm->input_device_rate;
+    (*stm->recorderItf)->SetMarkerPosition(stm->recorderItf, (SLmillisecond)duration);
+    return;
+  }
+}
+
+void recorder_fullduplex_callback(SLAndroidSimpleBufferQueueItf bq, void * context)
+{
+  assert(context);
+  cubeb_stream * stm = context;
+  assert(stm->recorderBufferQueueItf);
+
+  int r = pthread_mutex_lock(&stm->mutex);
+  assert(r == 0);
+  int draining = opensl_get_draining(stm);
+  uint32_t shutdown = opensl_get_shutdown(stm);
+  r = pthread_mutex_unlock(&stm->mutex);
+  assert(r == 0);
+
+  if (shutdown || draining) {
+    /* On draining and shutdown the recorder should have been stoped from
+    *  the one set the flags. Accordint to the doc, on transition to
+    *  the SL_RECORDSTATE_STOPPED state, the application should
+    *  continue to enqueue buffers onto the queue to retrieve the residual
+    *  recorded data in the system. */
+    LOG("Input shutdown %d or drain %d", shutdown, draining);
+    int r = opensl_enqueue_recorder(stm, NULL);
+    assert(r == CUBEB_OK);
+    return;
+  }
+
+  // Enqueue next available buffer and get the last filled buffer.
+  void * input_buffer = NULL;
+  r = opensl_enqueue_recorder(stm, &input_buffer);
+  assert(r == CUBEB_OK);
+  assert(input_buffer);
+
+  assert(stm->input_queue);
+  r = array_queue_push(stm->input_queue, input_buffer);
+  if (r == -1) {
+    LOG("Input queue is full, drop input ...");
+    return;
+  }
+
+  LOG("Input pushed in the queue, input array %zu",
+      array_queue_get_size(stm->input_queue));
+}
+
+static void
+player_fullduplex_callback(SLBufferQueueItf caller, void * user_ptr)
+{
+  TIMESTAMP("ENTER");
+  cubeb_stream * stm = user_ptr;
+  assert(stm);
+  SLresult res;
+
+  int r = pthread_mutex_lock(&stm->mutex);
+  assert(r == 0);
+  int draining = opensl_get_draining(stm);
+  uint32_t shutdown = opensl_get_shutdown(stm);
+  r = pthread_mutex_unlock(&stm->mutex);
+  assert(r == 0);
+
+  // Get output
+  void * output_buffer = NULL;
+  r = pthread_mutex_lock(&stm->mutex);
+  assert(r == 0);
+  output_buffer = stm->queuebuf[stm->queuebuf_idx];
+  // Advance the output buffer queue index
+  stm->queuebuf_idx = (stm->queuebuf_idx + 1) % stm->queuebuf_capacity;
+  r = pthread_mutex_unlock(&stm->mutex);
+  assert(r == 0);
+
+  if (shutdown || draining) {
+    LOG("Shutdown/draining, send silent");
+    // Set silent on buffer
+    memset(output_buffer, 0, stm->queuebuf_len);
+
+    // Enqueue data in player buffer queue
+    res = (*stm->bufq)->Enqueue(stm->bufq,
+                                output_buffer,
+                                stm->queuebuf_len);
+    assert(res == SL_RESULT_SUCCESS);
+    return;
+  }
+
+  // Get input.
+  void * input_buffer = array_queue_pop(stm->input_queue);
+  long input_frame_count = stm->input_buffer_length / stm->input_frame_size;
+  long frames_needed = stm->queuebuf_len / stm->framesize;
+  if (!input_buffer) {
+    LOG("Input hole set silent input buffer");
+    input_buffer = stm->input_silent_buffer;
+  }
+
+  long written = 0;
+  // Trigger user callback through resampler
+  written = cubeb_resampler_fill(stm->resampler,
+                                 input_buffer,
+                                 &input_frame_count,
+                                 output_buffer,
+                                 frames_needed);
+
+  LOG("Fill: written %ld, frames_needed %ld, input array size %zu",
+      written, frames_needed, array_queue_get_size(stm->input_queue));
+
+  if (written < 0 || written  > frames_needed) {
+    // Error case
+    r = pthread_mutex_lock(&stm->mutex);
+    assert(r == 0);
+    opensl_set_shutdown(stm, 1);
+    r = pthread_mutex_unlock(&stm->mutex);
+    assert(r == 0);
+    opensl_stop_player(stm);
+    opensl_stop_recorder(stm);
+    stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
+    memset(output_buffer, 0, stm->queuebuf_len);
+
+    // Enqueue data in player buffer queue
+    res = (*stm->bufq)->Enqueue(stm->bufq,
+                                output_buffer,
+                                stm->queuebuf_len);
+    assert(res == SL_RESULT_SUCCESS);
+    return;
+  }
+
+  // Advance total out written  frames counter
+  r = pthread_mutex_lock(&stm->mutex);
+  assert(r == 0);
+  stm->written += written;
+  r = pthread_mutex_unlock(&stm->mutex);
+  assert(r == 0);
+
+  if ( written < frames_needed) {
+    r = pthread_mutex_lock(&stm->mutex);
+    assert(r == 0);
+    int64_t written_duration = INT64_C(1000) * stm->written * stm->framesize / stm->bytespersec;
+    opensl_set_draining(stm, 1);
+    r = pthread_mutex_unlock(&stm->mutex);
+    assert(r == 0);
+
+    // Use SL_PLAYEVENT_HEADATMARKER event from slPlayCallback of SLPlayItf
+    // to make sure all the data has been processed.
+    (*stm->play)->SetMarkerPosition(stm->play, (SLmillisecond)written_duration);
+  }
+
+  // Keep sending silent data even in draining mode to prevent the audio
+  // back-end from being stopped automatically by OpenSL/ES.
+  memset((uint8_t *)output_buffer + written * stm->framesize, 0,
+         stm->queuebuf_len - written * stm->framesize);
+
+  // Enqueue data in player buffer queue
+  res = (*stm->bufq)->Enqueue(stm->bufq,
+                              output_buffer,
+                              stm->queuebuf_len);
+  assert(res == SL_RESULT_SUCCESS);
+  TIMESTAMP("EXIT");
+}
+
+static void opensl_destroy(cubeb * ctx);
+
+#if defined(__ANDROID__)
+#if (__ANDROID_API__ >= ANDROID_VERSION_LOLLIPOP)
+typedef int (system_property_get)(const char*, char*);
+
+static int
+wrap_system_property_get(const char* name, char* value)
+{
+  void* libc = dlopen("libc.so", RTLD_LAZY);
+  if (!libc) {
+    LOG("Failed to open libc.so");
+    return -1;
+  }
+  system_property_get* func = (system_property_get*)
+                              dlsym(libc, "__system_property_get");
+  int ret = -1;
+  if (func) {
+    ret = func(name, value);
+  }
+  dlclose(libc);
+  return ret;
+}
+#endif
+
+static int
+get_android_version(void)
+{
+  char version_string[PROP_VALUE_MAX];
+
+  memset(version_string, 0, PROP_VALUE_MAX);
+
+#if (__ANDROID_API__ >= ANDROID_VERSION_LOLLIPOP)
+  int len = wrap_system_property_get("ro.build.version.sdk", version_string);
+#else
+  int len = __system_property_get("ro.build.version.sdk", version_string);
+#endif
+  if (len <= 0) {
+    LOG("Failed to get Android version!\n");
+    return len;
+  }
+
+  int version = (int)strtol(version_string, NULL, 10);
+  LOG("Android version %d", version);
+  return version;
+}
+#endif
+
+/*static*/ int
+opensl_init(cubeb ** context, char const * context_name)
+{
+  cubeb * ctx;
+
+#if defined(__ANDROID__)
+  int android_version = get_android_version();
+  if (android_version > 0 && android_version <= ANDROID_VERSION_GINGERBREAD_MR1) {
+    // Don't even attempt to run on Gingerbread and lower
+    return CUBEB_ERROR;
+  }
+#endif
+
+  *context = NULL;
+
+  ctx = calloc(1, sizeof(*ctx));
+  assert(ctx);
+
+  ctx->ops = &opensl_ops;
+
+  ctx->lib = dlopen("libOpenSLES.so", RTLD_LAZY);
+  if (!ctx->lib) {
+    free(ctx);
+    return CUBEB_ERROR;
+  }
+
+  typedef SLresult (*slCreateEngine_t)(SLObjectItf *,
+                                       SLuint32,
+                                       const SLEngineOption *,
+                                       SLuint32,
+                                       const SLInterfaceID *,
+                                       const SLboolean *);
+  slCreateEngine_t f_slCreateEngine =
+    (slCreateEngine_t)dlsym(ctx->lib, "slCreateEngine");
+  SLInterfaceID SL_IID_ENGINE = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_ENGINE");
+  SLInterfaceID SL_IID_OUTPUTMIX = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_OUTPUTMIX");
+  ctx->SL_IID_VOLUME = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_VOLUME");
+  ctx->SL_IID_BUFFERQUEUE = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_BUFFERQUEUE");
+#if defined(__ANDROID__)
+  ctx->SL_IID_ANDROIDCONFIGURATION = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_ANDROIDCONFIGURATION");
+  ctx->SL_IID_ANDROIDSIMPLEBUFFERQUEUE = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_ANDROIDSIMPLEBUFFERQUEUE");
+#endif
+  ctx->SL_IID_PLAY = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_PLAY");
+  ctx->SL_IID_RECORD = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_RECORD");
+
+  if (!f_slCreateEngine ||
+      !SL_IID_ENGINE ||
+      !SL_IID_OUTPUTMIX ||
+      !ctx->SL_IID_BUFFERQUEUE ||
+#if defined(__ANDROID__)
+      !ctx->SL_IID_ANDROIDCONFIGURATION ||
+      !ctx->SL_IID_ANDROIDSIMPLEBUFFERQUEUE ||
+#endif
+      !ctx->SL_IID_PLAY ||
+      !ctx->SL_IID_RECORD) {
+    opensl_destroy(ctx);
+    return CUBEB_ERROR;
+  }
+
+  const SLEngineOption opt[] = {{SL_ENGINEOPTION_THREADSAFE, SL_BOOLEAN_TRUE}};
+
+  SLresult res;
+  res = cubeb_get_sles_engine(&ctx->engObj, 1, opt, 0, NULL, NULL);
+
+  if (res != SL_RESULT_SUCCESS) {
+    opensl_destroy(ctx);
+    return CUBEB_ERROR;
+  }
+
+  res = cubeb_realize_sles_engine(ctx->engObj);
+  if (res != SL_RESULT_SUCCESS) {
+    opensl_destroy(ctx);
+    return CUBEB_ERROR;
+  }
+
+  res = (*ctx->engObj)->GetInterface(ctx->engObj, SL_IID_ENGINE, &ctx->eng);
+  if (res != SL_RESULT_SUCCESS) {
+    opensl_destroy(ctx);
+    return CUBEB_ERROR;
+  }
+
+  const SLInterfaceID idsom[] = {SL_IID_OUTPUTMIX};
+  const SLboolean reqom[] = {SL_BOOLEAN_TRUE};
+  res = (*ctx->eng)->CreateOutputMix(ctx->eng, &ctx->outmixObj, 1, idsom, reqom);
+  if (res != SL_RESULT_SUCCESS) {
+    opensl_destroy(ctx);
+    return CUBEB_ERROR;
+  }
+
+  res = (*ctx->outmixObj)->Realize(ctx->outmixObj, SL_BOOLEAN_FALSE);
+  if (res != SL_RESULT_SUCCESS) {
+    opensl_destroy(ctx);
+    return CUBEB_ERROR;
+  }
+
+  ctx->p_output_latency_function = cubeb_output_latency_load_method(android_version);
+  if (!cubeb_output_latency_method_is_loaded(ctx->p_output_latency_function)) {
+    LOG("Warning: output latency is not available, cubeb_stream_get_position() is not supported");
+  }
+
+  *context = ctx;
+
+  LOG("Cubeb init (%p) success", ctx);
+  return CUBEB_OK;
+}
+
+static char const *
+opensl_get_backend_id(cubeb * ctx)
+{
+  return "opensl";
+}
+
+static int
+opensl_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
+{
+  assert(ctx && max_channels);
+  /* The android mixer handles up to two channels, see
+     http://androidxref.com/4.2.2_r1/xref/frameworks/av/services/audioflinger/AudioFlinger.h#67 */
+  *max_channels = 2;
+
+  return CUBEB_OK;
+}
+
+static void
+opensl_destroy(cubeb * ctx)
+{
+  if (ctx->outmixObj)
+    (*ctx->outmixObj)->Destroy(ctx->outmixObj);
+  if (ctx->engObj)
+    cubeb_destroy_sles_engine(&ctx->engObj);
+  dlclose(ctx->lib);
+  if (ctx->p_output_latency_function)
+    cubeb_output_latency_unload_method(ctx->p_output_latency_function);
+  free(ctx);
+}
+
+static void opensl_stream_destroy(cubeb_stream * stm);
+
+#if defined(__ANDROID__) && (__ANDROID_API__ >= ANDROID_VERSION_LOLLIPOP)
+static int
+opensl_set_format_ext(SLAndroidDataFormat_PCM_EX * format, cubeb_stream_params * params)
+{
+  assert(format);
+  assert(params);
+
+  format->formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
+  format->numChannels = params->channels;
+  // sampleRate is in milliHertz
+  format->sampleRate = params->rate * 1000;
+  format->channelMask = params->channels == 1 ?
+                       SL_SPEAKER_FRONT_CENTER :
+                       SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
+
+  switch (params->format) {
+    case CUBEB_SAMPLE_S16LE:
+      format->bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
+      format->containerSize = SL_PCMSAMPLEFORMAT_FIXED_16;
+      format->representation = SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT;
+      format->endianness = SL_BYTEORDER_LITTLEENDIAN;
+      break;
+    case CUBEB_SAMPLE_S16BE:
+      format->bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
+      format->containerSize = SL_PCMSAMPLEFORMAT_FIXED_16;
+      format->representation = SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT;
+      format->endianness = SL_BYTEORDER_BIGENDIAN;
+      break;
+    case CUBEB_SAMPLE_FLOAT32LE:
+      format->bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_32;
+      format->containerSize = SL_PCMSAMPLEFORMAT_FIXED_32;
+      format->representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
+      format->endianness = SL_BYTEORDER_LITTLEENDIAN;
+      break;
+    case CUBEB_SAMPLE_FLOAT32BE:
+      format->bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_32;
+      format->containerSize = SL_PCMSAMPLEFORMAT_FIXED_32;
+      format->representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
+      format->endianness = SL_BYTEORDER_BIGENDIAN;
+      break;
+    default:
+      return CUBEB_ERROR_INVALID_FORMAT;
+  }
+  return CUBEB_OK;
+}
+#endif
+
+static int
+opensl_set_format(SLDataFormat_PCM * format, cubeb_stream_params * params)
+{
+  assert(format);
+  assert(params);
+
+  format->formatType = SL_DATAFORMAT_PCM;
+  format->numChannels = params->channels;
+  // samplesPerSec is in milliHertz
+  format->samplesPerSec = params->rate * 1000;
+  format->bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
+  format->containerSize = SL_PCMSAMPLEFORMAT_FIXED_16;
+  format->channelMask = params->channels == 1 ?
+                       SL_SPEAKER_FRONT_CENTER :
+                       SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
+
+  switch (params->format) {
+    case CUBEB_SAMPLE_S16LE:
+      format->endianness = SL_BYTEORDER_LITTLEENDIAN;
+          break;
+    case CUBEB_SAMPLE_S16BE:
+      format->endianness = SL_BYTEORDER_BIGENDIAN;
+          break;
+    default:
+      return CUBEB_ERROR_INVALID_FORMAT;
+  }
+  return CUBEB_OK;
+}
+
+static int
+opensl_configure_capture(cubeb_stream * stm, cubeb_stream_params * params)
+{
+  assert(stm);
+  assert(params);
+
+  SLDataLocator_AndroidSimpleBufferQueue lDataLocatorOut;
+  lDataLocatorOut.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
+  lDataLocatorOut.numBuffers = NBUFS;
+
+  SLDataFormat_PCM lDataFormat;
+  int r = opensl_set_format(&lDataFormat, params);
+  if (r != CUBEB_OK) {
+    return CUBEB_ERROR_INVALID_FORMAT;
+  }
+
+  /* For now set device rate to params rate. */
+  stm->input_device_rate = params->rate;
+
+  SLDataSink lDataSink;
+  lDataSink.pLocator = &lDataLocatorOut;
+  lDataSink.pFormat = &lDataFormat;
+
+  SLDataLocator_IODevice lDataLocatorIn;
+  lDataLocatorIn.locatorType = SL_DATALOCATOR_IODEVICE;
+  lDataLocatorIn.deviceType = SL_IODEVICE_AUDIOINPUT;
+  lDataLocatorIn.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT;
+  lDataLocatorIn.device = NULL;
+
+  SLDataSource lDataSource;
+  lDataSource.pLocator = &lDataLocatorIn;
+  lDataSource.pFormat = NULL;
+
+  const SLInterfaceID lSoundRecorderIIDs[] = { stm->context->SL_IID_RECORD,
+                                               stm->context->SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
+                                               stm->context->SL_IID_ANDROIDCONFIGURATION };
+
+  const SLboolean lSoundRecorderReqs[] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
+  // create the audio recorder abstract object
+  SLresult res = (*stm->context->eng)->CreateAudioRecorder(stm->context->eng,
+                                                           &stm->recorderObj,
+                                                           &lDataSource,
+                                                           &lDataSink,
+                                                           NELEMS(lSoundRecorderIIDs),
+                                                           lSoundRecorderIIDs,
+                                                           lSoundRecorderReqs);
+  // Sample rate not supported. Try again with default sample rate!
+  if (res == SL_RESULT_CONTENT_UNSUPPORTED) {
+    if (stm->output_enabled && stm->output_configured_rate != 0) {
+      // Set the same with the player. Since there is no
+      // api for input device this is a safe choice.
+      stm->input_device_rate = stm->output_configured_rate;
+    } else  {
+      // The output preferred rate is used for an input only scenario.
+      // The default rate expected to be supported from all android devices.
+      stm->input_device_rate = DEFAULT_SAMPLE_RATE;
+    }
+    lDataFormat.samplesPerSec = stm->input_device_rate * 1000;
+    res = (*stm->context->eng)->CreateAudioRecorder(stm->context->eng,
+                                                    &stm->recorderObj,
+                                                    &lDataSource,
+                                                    &lDataSink,
+                                                    NELEMS(lSoundRecorderIIDs),
+                                                    lSoundRecorderIIDs,
+                                                    lSoundRecorderReqs);
+
+    if (res != SL_RESULT_SUCCESS) {
+      LOG("Failed to create recorder. Error code: %lu", res);
+      return CUBEB_ERROR;
+    }
+  }
+
+
+  if (get_android_version() > ANDROID_VERSION_JELLY_BEAN) {
+    SLAndroidConfigurationItf recorderConfig;
+    res = (*stm->recorderObj)
+              ->GetInterface(stm->recorderObj,
+                             stm->context->SL_IID_ANDROIDCONFIGURATION,
+                             &recorderConfig);
+
+    if (res != SL_RESULT_SUCCESS) {
+      LOG("Failed to get the android configuration interface for recorder. Error "
+          "code: %lu",
+          res);
+      return CUBEB_ERROR;
+    }
+
+    // Voice recognition is the lowest latency, according to the docs. Camcorder
+    // uses a microphone that is in the same direction as the camera.
+    SLint32 streamType = stm->voice ? SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION
+                                    : SL_ANDROID_RECORDING_PRESET_CAMCORDER;
+
+    res = (*recorderConfig)
+              ->SetConfiguration(recorderConfig, SL_ANDROID_KEY_RECORDING_PRESET,
+                                 &streamType, sizeof(SLint32));
+
+    if (res != SL_RESULT_SUCCESS) {
+      LOG("Failed to set the android configuration to VOICE for the recorder. "
+          "Error code: %lu", res);
+      return CUBEB_ERROR;
+    }
+  }
+  // realize the audio recorder
+  res = (*stm->recorderObj)->Realize(stm->recorderObj, SL_BOOLEAN_FALSE);
+  if (res != SL_RESULT_SUCCESS) {
+    LOG("Failed to realize recorder. Error code: %lu", res);
+    return CUBEB_ERROR;
+  }
+  // get the record interface
+  res = (*stm->recorderObj)->GetInterface(stm->recorderObj,
+                                          stm->context->SL_IID_RECORD,
+                                          &stm->recorderItf);
+  if (res != SL_RESULT_SUCCESS) {
+    LOG("Failed to get recorder interface. Error code: %lu", res);
+    return CUBEB_ERROR;
+  }
+
+  res = (*stm->recorderItf)->RegisterCallback(stm->recorderItf, recorder_marker_callback, stm);
+  if (res != SL_RESULT_SUCCESS) {
+    LOG("Failed to register recorder marker callback. Error code: %lu", res);
+    return CUBEB_ERROR;
+  }
+
+  (*stm->recorderItf)->SetMarkerPosition(stm->recorderItf, (SLmillisecond)0);
+
+  res = (*stm->recorderItf)->SetCallbackEventsMask(stm->recorderItf, (SLuint32)SL_RECORDEVENT_HEADATMARKER);
+  if (res != SL_RESULT_SUCCESS) {
+    LOG("Failed to set headatmarker event mask. Error code: %lu", res);
+    return CUBEB_ERROR;
+  }
+  // get the simple android buffer queue interface
+  res = (*stm->recorderObj)->GetInterface(stm->recorderObj,
+                                          stm->context->SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
+                                          &stm->recorderBufferQueueItf);
+  if (res != SL_RESULT_SUCCESS) {
+    LOG("Failed to get recorder (android) buffer queue interface. Error code: %lu", res);
+    return CUBEB_ERROR;
+  }
+
+  // register callback on record (input) buffer queue
+  slAndroidSimpleBufferQueueCallback rec_callback = recorder_callback;
+  if (stm->output_enabled) {
+    // Register full duplex callback instead.
+    rec_callback = recorder_fullduplex_callback;
+  }
+  res = (*stm->recorderBufferQueueItf)->RegisterCallback(stm->recorderBufferQueueItf,
+                                                         rec_callback,
+                                                         stm);
+  if (res != SL_RESULT_SUCCESS) {
+    LOG("Failed to register recorder buffer queue callback. Error code: %lu", res);
+    return CUBEB_ERROR;
+  }
+
+  // Calculate length of input buffer according to requested latency
+  stm->input_frame_size = params->channels * sizeof(int16_t);
+  stm->input_buffer_length = (stm->input_frame_size * stm->buffer_size_frames);
+
+  // Calculate the capacity of input array
+  stm->input_array_capacity = NBUFS;
+  if (stm->output_enabled) {
+    // Full duplex, update capacity to hold 1 sec of data
+    stm->input_array_capacity = 1 * stm->input_device_rate / stm->input_buffer_length;
+  }
+  // Allocate input array
+  stm->input_buffer_array = (void**)calloc(1, sizeof(void*)*stm->input_array_capacity);
+  // Buffering has not started yet.
+  stm->input_buffer_index = -1;
+  // Prepare input buffers
+  for(uint32_t i = 0; i < stm->input_array_capacity; ++i) {
+    stm->input_buffer_array[i] = calloc(1, stm->input_buffer_length);
+  }
+
+  // On full duplex allocate input queue and silent buffer
+  if (stm->output_enabled) {
+    stm->input_queue = array_queue_create(stm->input_array_capacity);
+    assert(stm->input_queue);
+    stm->input_silent_buffer = calloc(1, stm->input_buffer_length);
+    assert(stm->input_silent_buffer);
+  }
+
+  // Enqueue buffer to start rolling once recorder started
+  r = opensl_enqueue_recorder(stm, NULL);
+  if (r != CUBEB_OK) {
+    return r;
+  }
+
+  LOG("Cubeb stream init recorder success");
+
+  return CUBEB_OK;
+}
+
+static int
+opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
+  assert(stm);
+  assert(params);
+
+  stm->user_output_rate = params->rate;
+  if(params->format == CUBEB_SAMPLE_S16NE || params->format == CUBEB_SAMPLE_S16BE) {
+    stm->framesize = params->channels * sizeof(int16_t);
+  } else if(params->format == CUBEB_SAMPLE_FLOAT32NE || params->format == CUBEB_SAMPLE_FLOAT32BE) {
+    stm->framesize = params->channels * sizeof(float);
+  }
+  stm->lastPosition = -1;
+  stm->lastPositionTimeStamp = 0;
+  stm->lastCompensativePosition = -1;
+
+  void* format = NULL;
+  SLuint32* format_sample_rate = NULL;
+
+#if defined(__ANDROID__) && (__ANDROID_API__ >= ANDROID_VERSION_LOLLIPOP)
+  SLAndroidDataFormat_PCM_EX pcm_ext_format;
+  if (get_android_version() >= ANDROID_VERSION_LOLLIPOP) {
+    if (opensl_set_format_ext(&pcm_ext_format, params) != CUBEB_OK) {
+      return CUBEB_ERROR_INVALID_FORMAT;
+    }
+    format = &pcm_ext_format;
+    format_sample_rate = &pcm_ext_format.sampleRate;
+  }
+#endif
+
+  SLDataFormat_PCM pcm_format;
+  if(!format) {
+    if(opensl_set_format(&pcm_format, params) != CUBEB_OK) {
+      return CUBEB_ERROR_INVALID_FORMAT;
+    }
+    format = &pcm_format;
+    format_sample_rate = &pcm_format.samplesPerSec;
+  }
+
+  SLDataLocator_BufferQueue loc_bufq;
+  loc_bufq.locatorType = SL_DATALOCATOR_BUFFERQUEUE;
+  loc_bufq.numBuffers = NBUFS;
+  SLDataSource source;
+  source.pLocator = &loc_bufq;
+  source.pFormat = format;
+
+  SLDataLocator_OutputMix loc_outmix;
+  loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
+  loc_outmix.outputMix = stm->context->outmixObj;
+  SLDataSink sink;
+  sink.pLocator = &loc_outmix;
+  sink.pFormat = NULL;
+
+#if defined(__ANDROID__)
+  const SLInterfaceID ids[] = {stm->context->SL_IID_BUFFERQUEUE,
+                               stm->context->SL_IID_VOLUME,
+                               stm->context->SL_IID_ANDROIDCONFIGURATION};
+  const SLboolean req[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
+#else
+  const SLInterfaceID ids[] = {ctx->SL_IID_BUFFERQUEUE, ctx->SL_IID_VOLUME};
+  const SLboolean req[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
+#endif
+  assert(NELEMS(ids) == NELEMS(req));
+
+  uint32_t preferred_sampling_rate = stm->user_output_rate;
+  SLresult res = SL_RESULT_CONTENT_UNSUPPORTED;
+  if (preferred_sampling_rate) {
+    res = (*stm->context->eng)->CreateAudioPlayer(stm->context->eng,
+                                                  &stm->playerObj,
+                                                  &source,
+                                                  &sink,
+                                                  NELEMS(ids),
+                                                  ids,
+                                                  req);
+  }
+
+  // Sample rate not supported? Try again with primary sample rate!
+  if (res == SL_RESULT_CONTENT_UNSUPPORTED &&
+      preferred_sampling_rate != DEFAULT_SAMPLE_RATE) {
+    preferred_sampling_rate = DEFAULT_SAMPLE_RATE;
+    *format_sample_rate = preferred_sampling_rate * 1000;
+    res = (*stm->context->eng)->CreateAudioPlayer(stm->context->eng,
+                                                  &stm->playerObj,
+                                                  &source,
+                                                  &sink,
+                                                  NELEMS(ids),
+                                                  ids,
+                                                  req);
+  }
+
+  if (res != SL_RESULT_SUCCESS) {
+    LOG("Failed to create audio player. Error code: %lu", res);
+    return CUBEB_ERROR;
+  }
+
+  stm->output_configured_rate = preferred_sampling_rate;
+  stm->bytespersec = stm->output_configured_rate * stm->framesize;
+  stm->queuebuf_len = stm->framesize * stm->buffer_size_frames;
+
+  // Calculate the capacity of input array
+  stm->queuebuf_capacity = NBUFS;
+  if (stm->output_enabled) {
+    // Full duplex, update capacity to hold 1 sec of data
+    stm->queuebuf_capacity = 1 * stm->output_configured_rate / stm->queuebuf_len;
+  }
+  // Allocate input array
+  stm->queuebuf = (void**)calloc(1, sizeof(void*) * stm->queuebuf_capacity);
+  for (uint32_t i = 0; i < stm->queuebuf_capacity; ++i) {
+    stm->queuebuf[i] = calloc(1, stm->queuebuf_len);
+    assert(stm->queuebuf[i]);
+  }
+
+  SLAndroidConfigurationItf playerConfig = NULL;
+
+  if (get_android_version() >= ANDROID_VERSION_N_MR1) {
+    res = (*stm->playerObj)
+              ->GetInterface(stm->playerObj,
+                             stm->context->SL_IID_ANDROIDCONFIGURATION,
+                             &playerConfig);
+    if (res != SL_RESULT_SUCCESS) {
+      LOG("Failed to get Android configuration interface. Error code: %lu", res);
+      return CUBEB_ERROR;
+    }
+
+    SLint32 streamType = SL_ANDROID_STREAM_MEDIA;
+    if (stm->voice) {
+      streamType = SL_ANDROID_STREAM_VOICE;
+    }
+    res = (*playerConfig)->SetConfiguration(playerConfig,
+                                            SL_ANDROID_KEY_STREAM_TYPE,
+                                            &streamType,
+                                            sizeof(streamType));
+    if (res != SL_RESULT_SUCCESS) {
+      LOG("Failed to set Android configuration to %d Error code: %lu",
+          streamType, res);
+    }
+
+    SLuint32 performanceMode = SL_ANDROID_PERFORMANCE_LATENCY;
+    if (stm->buffer_size_frames > POWERSAVE_LATENCY_FRAMES_THRESHOLD) {
+      performanceMode = SL_ANDROID_PERFORMANCE_POWER_SAVING;
+    }
+
+    res = (*playerConfig)->SetConfiguration(playerConfig,
+                                            SL_ANDROID_KEY_PERFORMANCE_MODE,
+                                            &performanceMode,
+                                            sizeof(performanceMode));
+    if (res != SL_RESULT_SUCCESS) {
+      LOG("Failed to set Android performance mode to %d Error code: %lu. This is"
+          " not fatal", performanceMode, res);
+    }
+  }
+
+  res = (*stm->playerObj)->Realize(stm->playerObj, SL_BOOLEAN_FALSE);
+  if (res != SL_RESULT_SUCCESS) {
+    LOG("Failed to realize player object. Error code: %lu", res);
+    return CUBEB_ERROR;
+  }
+
+  // There are two ways of getting the audio output latency:
+  // - a configuration value, only available on some devices (notably devices
+  // running FireOS)
+  // - A Java method, that we call using JNI.
+  //
+  // The first method is prefered, if available, because it can account for more
+  // latency causes, and is more precise.
+
+  // Latency has to be queried after the realization of the interface, when
+  // using SL_IID_ANDROIDCONFIGURATION.
+  SLuint32 audioLatency = 0;
+  SLuint32 paramSize = sizeof(SLuint32);
+  // The reported latency is in milliseconds.
+  if (playerConfig) {
+    res = (*playerConfig)->GetConfiguration(playerConfig,
+                                            (const SLchar *)"androidGetAudioLatency",
+                                            &paramSize,
+                                            &audioLatency);
+    if (res == SL_RESULT_SUCCESS) {
+      LOG("Got playback latency using android configuration extension");
+      stm->output_latency_ms = audioLatency;
+    }
+  }
+  // `playerConfig` is available, but the above failed, or `playerConfig` is not
+  // available. In both cases, we need to acquire the output latency by an other
+  // mean.
+  if ((playerConfig && res != SL_RESULT_SUCCESS) ||
+      !playerConfig) {
+    if (cubeb_output_latency_method_is_loaded(stm->context->p_output_latency_function)) {
+      LOG("Got playback latency using JNI");
+      stm->output_latency_ms = cubeb_get_output_latency(stm->context->p_output_latency_function);
+    } else {
+      LOG("No alternate latency querying method loaded, A/V sync will be off.");
+      stm->output_latency_ms = 0;
+    }
+  }
+
+  LOG("Audio output latency: %dms", stm->output_latency_ms);
+
+  res = (*stm->playerObj)->GetInterface(stm->playerObj,
+                                        stm->context->SL_IID_PLAY,
+                                        &stm->play);
+  if (res != SL_RESULT_SUCCESS) {
+    LOG("Failed to get play interface. Error code: %lu", res);
+    return CUBEB_ERROR;
+  }
+
+  res = (*stm->playerObj)->GetInterface(stm->playerObj,
+                                        stm->context->SL_IID_BUFFERQUEUE,
+                                        &stm->bufq);
+  if (res != SL_RESULT_SUCCESS) {
+    LOG("Failed to get bufferqueue interface. Error code: %lu", res);
+    return CUBEB_ERROR;
+  }
+
+  res = (*stm->playerObj)->GetInterface(stm->playerObj,
+                                        stm->context->SL_IID_VOLUME,
+                                        &stm->volume);
+  if (res != SL_RESULT_SUCCESS) {
+    LOG("Failed to get volume interface. Error code: %lu", res);
+    return CUBEB_ERROR;
+  }
+
+  res = (*stm->play)->RegisterCallback(stm->play, play_callback, stm);
+  if (res != SL_RESULT_SUCCESS) {
+    LOG("Failed to register play callback. Error code: %lu", res);
+    return CUBEB_ERROR;
+  }
+
+  // Work around wilhelm/AudioTrack badness, bug 1221228
+  (*stm->play)->SetMarkerPosition(stm->play, (SLmillisecond)0);
+
+  res = (*stm->play)->SetCallbackEventsMask(stm->play, (SLuint32)SL_PLAYEVENT_HEADATMARKER);
+  if (res != SL_RESULT_SUCCESS) {
+    LOG("Failed to set headatmarker event mask. Error code: %lu", res);
+    return CUBEB_ERROR;
+  }
+
+  slBufferQueueCallback player_callback = bufferqueue_callback;
+  if (stm->input_enabled) {
+    player_callback = player_fullduplex_callback;
+  }
+  res = (*stm->bufq)->RegisterCallback(stm->bufq, player_callback, stm);
+  if (res != SL_RESULT_SUCCESS) {
+    LOG("Failed to register bufferqueue callback. Error code: %lu", res);
+    return CUBEB_ERROR;
+  }
+
+  {
+    // Enqueue a silent frame so once the player becomes playing, the frame
+    // will be consumed and kick off the buffer queue callback.
+    // Note the duration of a single frame is less than 1ms. We don't bother
+    // adjusting the playback position.
+    uint8_t *buf = stm->queuebuf[stm->queuebuf_idx++];
+    memset(buf, 0, stm->framesize);
+    res = (*stm->bufq)->Enqueue(stm->bufq, buf, stm->framesize);
+    assert(res == SL_RESULT_SUCCESS);
+  }
+
+  LOG("Cubeb stream init playback success");
+  return CUBEB_OK;
+}
+
+static int
+opensl_validate_stream_param(cubeb_stream_params * stream_params)
+{
+  if ((stream_params &&
+       (stream_params->channels < 1 || stream_params->channels > 32))) {
+    return CUBEB_ERROR_INVALID_FORMAT;
+  }
+  if ((stream_params &&
+       (stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK))) {
+    LOG("Loopback is not supported");
+    return CUBEB_ERROR_NOT_SUPPORTED;
+  }
+  return CUBEB_OK;
+}
+
+int has_pref_set(cubeb_stream_params* input_params,
+                 cubeb_stream_params* output_params,
+                 cubeb_stream_prefs pref)
+{
+  return (input_params && input_params->prefs & pref) ||
+         (output_params && output_params->prefs & pref);
+}
+
+static int
+opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
+                   cubeb_devid input_device,
+                   cubeb_stream_params * input_stream_params,
+                   cubeb_devid output_device,
+                   cubeb_stream_params * output_stream_params,
+                   unsigned int latency_frames,
+                   cubeb_data_callback data_callback, cubeb_state_callback state_callback,
+                   void * user_ptr)
+{
+  cubeb_stream * stm;
+
+  assert(ctx);
+  if (input_device || output_device) {
+    LOG("Device selection is not supported in Android. The default will be used");
+  }
+
+  *stream = NULL;
+
+  int r = opensl_validate_stream_param(output_stream_params);
+  if(r != CUBEB_OK) {
+    LOG("Output stream params not valid");
+    return r;
+  }
+  r = opensl_validate_stream_param(input_stream_params);
+  if(r != CUBEB_OK) {
+    LOG("Input stream params not valid");
+    return r;
+  }
+
+  stm = calloc(1, sizeof(*stm));
+  assert(stm);
+
+  stm->context = ctx;
+  stm->data_callback = data_callback;
+  stm->state_callback = state_callback;
+  stm->user_ptr = user_ptr;
+  stm->buffer_size_frames = latency_frames ? latency_frames : DEFAULT_NUM_OF_FRAMES;
+  stm->input_enabled = (input_stream_params) ? 1 : 0;
+  stm->output_enabled = (output_stream_params) ? 1 : 0;
+  stm->shutdown = 1;
+  stm->voice = has_pref_set(input_stream_params, output_stream_params, CUBEB_STREAM_PREF_VOICE);
+
+  LOG("cubeb stream prefs: voice: %s", stm->voice ? "true" : "false");
+
+
+#ifdef DEBUG
+  pthread_mutexattr_t attr;
+  pthread_mutexattr_init(&attr);
+  pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
+  r = pthread_mutex_init(&stm->mutex, &attr);
+#else
+  r = pthread_mutex_init(&stm->mutex, NULL);
+#endif
+  assert(r == 0);
+
+  if (output_stream_params) {
+    LOG("Playback params: Rate %d, channels %d, format %d, latency in frames %d.",
+        output_stream_params->rate, output_stream_params->channels,
+        output_stream_params->format, stm->buffer_size_frames);
+    r = opensl_configure_playback(stm, output_stream_params);
+    if (r != CUBEB_OK) {
+      opensl_stream_destroy(stm);
+      return r;
+    }
+  }
+
+  if (input_stream_params) {
+    LOG("Capture params: Rate %d, channels %d, format %d, latency in frames %d.",
+        input_stream_params->rate, input_stream_params->channels,
+        input_stream_params->format, stm->buffer_size_frames);
+    r = opensl_configure_capture(stm, input_stream_params);
+    if (r != CUBEB_OK) {
+      opensl_stream_destroy(stm);
+      return r;
+    }
+  }
+
+  /* Configure resampler*/
+  uint32_t target_sample_rate;
+  if (input_stream_params) {
+    target_sample_rate = input_stream_params->rate;
+  } else {
+    assert(output_stream_params);
+    target_sample_rate = output_stream_params->rate;
+  }
+
+  // Use the actual configured rates for input
+  // and output.
+  cubeb_stream_params input_params;
+  if (input_stream_params) {
+    input_params = *input_stream_params;
+    input_params.rate = stm->input_device_rate;
+  }
+  cubeb_stream_params output_params;
+  if (output_stream_params) {
+    output_params = *output_stream_params;
+    output_params.rate = stm->output_configured_rate;
+  }
+
+  stm->resampler = cubeb_resampler_create(stm,
+                                          input_stream_params ? &input_params : NULL,
+                                          output_stream_params ? &output_params : NULL,
+                                          target_sample_rate,
+                                          data_callback,
+                                          user_ptr,
+                                          CUBEB_RESAMPLER_QUALITY_DEFAULT);
+  if (!stm->resampler) {
+    LOG("Failed to create resampler");
+    opensl_stream_destroy(stm);
+    return CUBEB_ERROR;
+  }
+
+  *stream = stm;
+  LOG("Cubeb stream (%p) init success", stm);
+  return CUBEB_OK;
+}
+
+static int
+opensl_start_player(cubeb_stream * stm)
+{
+  assert(stm->playerObj);
+  SLuint32 playerState;
+  (*stm->playerObj)->GetState(stm->playerObj, &playerState);
+  if (playerState == SL_OBJECT_STATE_REALIZED) {
+    SLresult res = (*stm->play)->SetPlayState(stm->play, SL_PLAYSTATE_PLAYING);
+    if(res != SL_RESULT_SUCCESS) {
+      LOG("Failed to start player. Error code: %lu", res);
+      return CUBEB_ERROR;
+    }
+  }
+  return CUBEB_OK;
+}
+
+static int
+opensl_start_recorder(cubeb_stream * stm)
+{
+  assert(stm->recorderObj);
+  SLuint32 recorderState;
+  (*stm->recorderObj)->GetState(stm->recorderObj, &recorderState);
+  if (recorderState == SL_OBJECT_STATE_REALIZED) {
+    SLresult res = (*stm->recorderItf)->SetRecordState(stm->recorderItf, SL_RECORDSTATE_RECORDING);
+    if(res != SL_RESULT_SUCCESS) {
+      LOG("Failed to start recorder. Error code: %lu", res);
+      return CUBEB_ERROR;
+    }
+  }
+  return CUBEB_OK;
+}
+
+static int
+opensl_stream_start(cubeb_stream * stm)
+{
+  assert(stm);
+
+  int r = pthread_mutex_lock(&stm->mutex);
+  assert(r == 0);
+  opensl_set_shutdown(stm, 0);
+  opensl_set_draining(stm, 0);
+  r = pthread_mutex_unlock(&stm->mutex);
+  assert(r == 0);
+
+  if (stm->playerObj) {
+    r = opensl_start_player(stm);
+    if (r != CUBEB_OK) {
+      return r;
+    }
+  }
+
+  if (stm->recorderObj) {
+    int r = opensl_start_recorder(stm);
+    if (r != CUBEB_OK) {
+      return r;
+    }
+  }
+
+  stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
+  LOG("Cubeb stream (%p) started", stm);
+  return CUBEB_OK;
+}
+
+static int
+opensl_stop_player(cubeb_stream * stm)
+{
+  assert(stm->playerObj);
+  assert(stm->shutdown || stm->draining);
+
+  SLresult res = (*stm->play)->SetPlayState(stm->play, SL_PLAYSTATE_PAUSED);
+  if (res != SL_RESULT_SUCCESS) {
+    LOG("Failed to stop player. Error code: %lu", res);
+    return CUBEB_ERROR;
+  }
+
+  return CUBEB_OK;
+}
+
+static int
+opensl_stop_recorder(cubeb_stream * stm)
+{
+  assert(stm->recorderObj);
+  assert(stm->shutdown || stm->draining);
+
+  SLresult res = (*stm->recorderItf)->SetRecordState(stm->recorderItf, SL_RECORDSTATE_PAUSED);
+  if (res != SL_RESULT_SUCCESS) {
+    LOG("Failed to stop recorder. Error code: %lu", res);
+    return CUBEB_ERROR;
+  }
+
+  return CUBEB_OK;
+}
+
+static int
+opensl_stream_stop(cubeb_stream * stm)
+{
+  assert(stm);
+
+  int r = pthread_mutex_lock(&stm->mutex);
+  assert(r == 0);
+  opensl_set_shutdown(stm, 1);
+  r = pthread_mutex_unlock(&stm->mutex);
+  assert(r == 0);
+
+  if (stm->playerObj) {
+    r = opensl_stop_player(stm);
+    if (r != CUBEB_OK) {
+      return r;
+    }
+  }
+
+  if (stm->recorderObj) {
+    int r = opensl_stop_recorder(stm);
+    if (r != CUBEB_OK) {
+      return r;
+    }
+  }
+
+  stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
+  LOG("Cubeb stream (%p) stopped", stm);
+  return CUBEB_OK;
+}
+
+static int
+opensl_destroy_recorder(cubeb_stream * stm)
+{
+  assert(stm);
+  assert(stm->recorderObj);
+
+  if (stm->recorderBufferQueueItf) {
+    SLresult res = (*stm->recorderBufferQueueItf)->Clear(stm->recorderBufferQueueItf);
+    if (res != SL_RESULT_SUCCESS) {
+      LOG("Failed to clear recorder buffer queue. Error code: %lu", res);
+      return CUBEB_ERROR;
+    }
+    stm->recorderBufferQueueItf = NULL;
+    for (uint32_t i = 0; i < stm->input_array_capacity; ++i) {
+      free(stm->input_buffer_array[i]);
+    }
+  }
+
+  (*stm->recorderObj)->Destroy(stm->recorderObj);
+  stm->recorderObj = NULL;
+  stm->recorderItf = NULL;
+
+  if (stm->input_queue) {
+    array_queue_destroy(stm->input_queue);
+  }
+  free(stm->input_silent_buffer);
+
+  return CUBEB_OK;
+}
+
+static void
+opensl_stream_destroy(cubeb_stream * stm)
+{
+  assert(stm->draining || stm->shutdown);
+
+  if (stm->playerObj) {
+    (*stm->playerObj)->Destroy(stm->playerObj);
+    stm->playerObj = NULL;
+    stm->play = NULL;
+    stm->bufq = NULL;
+    for (uint32_t i = 0; i < stm->queuebuf_capacity; ++i) {
+      free(stm->queuebuf[i]);
+    }
+  }
+
+  if (stm->recorderObj) {
+    int r = opensl_destroy_recorder(stm);
+    assert(r == CUBEB_OK);
+  }
+
+  if (stm->resampler) {
+    cubeb_resampler_destroy(stm->resampler);
+  }
+
+  pthread_mutex_destroy(&stm->mutex);
+
+  LOG("Cubeb stream (%p) destroyed", stm);
+  free(stm);
+}
+
+static int
+opensl_stream_get_position(cubeb_stream * stm, uint64_t * position)
+{
+  SLmillisecond msec;
+  uint32_t compensation_msec = 0;
+  SLresult res;
+
+  res = (*stm->play)->GetPosition(stm->play, &msec);
+  if (res != SL_RESULT_SUCCESS)
+    return CUBEB_ERROR;
+
+  struct timespec t;
+  clock_gettime(CLOCK_MONOTONIC, &t);
+  if(stm->lastPosition == msec) {
+    compensation_msec =
+      (t.tv_sec*1000000000LL + t.tv_nsec - stm->lastPositionTimeStamp) / 1000000;
+  } else {
+    stm->lastPositionTimeStamp = t.tv_sec*1000000000LL + t.tv_nsec;
+    stm->lastPosition = msec;
+  }
+
+  uint64_t samplerate = stm->user_output_rate;
+  uint32_t output_latency = stm->output_latency_ms;
+
+  pthread_mutex_lock(&stm->mutex);
+  int64_t maximum_position = stm->written * (int64_t)stm->user_output_rate / stm->output_configured_rate;
+  pthread_mutex_unlock(&stm->mutex);
+  assert(maximum_position >= 0);
+
+  if (msec > output_latency) {
+    int64_t unadjusted_position;
+    if (stm->lastCompensativePosition > msec + compensation_msec) {
+      // Over compensation, use lastCompensativePosition.
+      unadjusted_position =
+        samplerate * (stm->lastCompensativePosition - output_latency) / 1000;
+    } else {
+      unadjusted_position =
+        samplerate * (msec - output_latency + compensation_msec) / 1000;
+      stm->lastCompensativePosition = msec + compensation_msec;
+    }
+    *position = unadjusted_position < maximum_position ?
+      unadjusted_position : maximum_position;
+  } else {
+    *position = 0;
+  }
+  return CUBEB_OK;
+}
+
+static int
+opensl_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
+{
+  assert(stm);
+  assert(latency);
+
+  uint32_t stream_latency_frames =
+    stm->user_output_rate * (stm->output_latency_ms / 1000);
+
+  return stream_latency_frames + cubeb_resampler_latency(stm->resampler);
+}
+
+int
+opensl_stream_set_volume(cubeb_stream * stm, float volume)
+{
+  SLresult res;
+  SLmillibel max_level, millibels;
+  float unclamped_millibels;
+
+  res = (*stm->volume)->GetMaxVolumeLevel(stm->volume, &max_level);
+
+  if (res != SL_RESULT_SUCCESS) {
+    return CUBEB_ERROR;
+  }
+
+  /* millibels are 100*dB, so the conversion from the volume's linear amplitude
+   * is 100 * 20 * log(volume). However we clamp the resulting value before
+   * passing it to lroundf() in order to prevent it from silently returning an
+   * erroneous value when the unclamped value exceeds the size of a long. */
+  unclamped_millibels = 100.0f * 20.0f * log10f(fmaxf(volume, 0.0f));
+  unclamped_millibels = fmaxf(unclamped_millibels, SL_MILLIBEL_MIN);
+  unclamped_millibels = fminf(unclamped_millibels, max_level);
+
+  millibels = lroundf(unclamped_millibels);
+
+  res = (*stm->volume)->SetVolumeLevel(stm->volume, millibels);
+
+  if (res != SL_RESULT_SUCCESS) {
+    return CUBEB_ERROR;
+  }
+  return CUBEB_OK;
+}
+
+static struct cubeb_ops const opensl_ops = {
+  .init = opensl_init,
+  .get_backend_id = opensl_get_backend_id,
+  .get_max_channel_count = opensl_get_max_channel_count,
+  .get_min_latency = NULL,
+  .get_preferred_sample_rate = NULL,
+  .enumerate_devices = NULL,
+  .device_collection_destroy = NULL,
+  .destroy = opensl_destroy,
+  .stream_init = opensl_stream_init,
+  .stream_destroy = opensl_stream_destroy,
+  .stream_start = opensl_stream_start,
+  .stream_stop = opensl_stream_stop,
+  .stream_reset_default_device = NULL,
+  .stream_get_position = opensl_stream_get_position,
+  .stream_get_latency = opensl_stream_get_latency,
+  .stream_set_volume = opensl_stream_set_volume,
+  .stream_get_current_device = NULL,
+  .stream_device_destroy = NULL,
+  .stream_register_device_changed_callback = NULL,
+  .register_device_collection_changed = NULL
+};
diff --git a/dep/cubeb/src/cubeb_osx_run_loop.cpp b/dep/cubeb/src/cubeb_osx_run_loop.cpp
new file mode 100644
index 000000000..de4e43970
--- /dev/null
+++ b/dep/cubeb/src/cubeb_osx_run_loop.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright © 2016 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+
+#include <cubeb/cubeb.h>
+#include "cubeb_osx_run_loop.h"
+#include "cubeb_log.h"
+#include <AudioUnit/AudioUnit.h>
+#include <CoreAudio/AudioHardware.h>
+#include <CoreAudio/HostTime.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+void cubeb_set_coreaudio_notification_runloop()
+{
+  /* This is needed so that AudioUnit listeners get called on this thread, and
+   * not the main thread. If we don't do that, they are not called, or a crash
+   * occur, depending on the OSX version. */
+  AudioObjectPropertyAddress runloop_address = {
+    kAudioHardwarePropertyRunLoop,
+    kAudioObjectPropertyScopeGlobal,
+    kAudioObjectPropertyElementMaster
+  };
+
+  CFRunLoopRef run_loop = nullptr;
+
+  OSStatus r;
+  r = AudioObjectSetPropertyData(kAudioObjectSystemObject,
+                                 &runloop_address,
+                                 0, NULL, sizeof(CFRunLoopRef), &run_loop);
+  if (r != noErr) {
+    LOG("Could not make global CoreAudio notifications use their own thread.");
+  }
+}
diff --git a/dep/cubeb/src/cubeb_osx_run_loop.h b/dep/cubeb/src/cubeb_osx_run_loop.h
new file mode 100644
index 000000000..78cd68d09
--- /dev/null
+++ b/dep/cubeb/src/cubeb_osx_run_loop.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright © 2014 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+
+/* On OSX 10.6 and after, the notification callbacks from the audio hardware are
+ * called on the main thread. Setting the kAudioHardwarePropertyRunLoop property
+ * to null tells the OSX to use a separate thread for that.
+ *
+ * This has to be called only once per process, so it is in a separate header
+ * for easy integration in other code bases.  */
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+void cubeb_set_coreaudio_notification_runloop();
+
+#if defined(__cplusplus)
+}
+#endif
diff --git a/dep/cubeb/src/cubeb_pulse.c b/dep/cubeb/src/cubeb_pulse.c
new file mode 100644
index 000000000..94ead3a9c
--- /dev/null
+++ b/dep/cubeb/src/cubeb_pulse.c
@@ -0,0 +1,1605 @@
+/*
+ * Copyright © 2011 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+#undef NDEBUG
+#include <assert.h>
+#include <dlfcn.h>
+#include <pulse/pulseaudio.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "cubeb-internal.h"
+#include "cubeb/cubeb.h"
+#include "cubeb_mixer.h"
+#include "cubeb_strings.h"
+
+#ifdef DISABLE_LIBPULSE_DLOPEN
+#define WRAP(x) x
+#else
+#define WRAP(x) cubeb_##x
+#define LIBPULSE_API_VISIT(X)                   \
+  X(pa_channel_map_can_balance)                 \
+  X(pa_channel_map_init)                        \
+  X(pa_context_connect)                         \
+  X(pa_context_disconnect)                      \
+  X(pa_context_drain)                           \
+  X(pa_context_get_server_info)                 \
+  X(pa_context_get_sink_info_by_name)           \
+  X(pa_context_get_sink_info_list)              \
+  X(pa_context_get_sink_input_info)             \
+  X(pa_context_get_source_info_list)            \
+  X(pa_context_get_state)                       \
+  X(pa_context_new)                             \
+  X(pa_context_rttime_new)                      \
+  X(pa_context_set_sink_input_volume)           \
+  X(pa_context_set_state_callback)              \
+  X(pa_context_unref)                           \
+  X(pa_cvolume_set)                             \
+  X(pa_cvolume_set_balance)                     \
+  X(pa_frame_size)                              \
+  X(pa_operation_get_state)                     \
+  X(pa_operation_unref)                         \
+  X(pa_proplist_gets)                           \
+  X(pa_rtclock_now)                             \
+  X(pa_stream_begin_write)                      \
+  X(pa_stream_cancel_write)                     \
+  X(pa_stream_connect_playback)                 \
+  X(pa_stream_cork)                             \
+  X(pa_stream_disconnect)                       \
+  X(pa_stream_get_channel_map)                  \
+  X(pa_stream_get_index)                        \
+  X(pa_stream_get_latency)                      \
+  X(pa_stream_get_sample_spec)                  \
+  X(pa_stream_get_state)                        \
+  X(pa_stream_get_time)                         \
+  X(pa_stream_new)                              \
+  X(pa_stream_set_state_callback)               \
+  X(pa_stream_set_write_callback)               \
+  X(pa_stream_unref)                            \
+  X(pa_stream_update_timing_info)               \
+  X(pa_stream_write)                            \
+  X(pa_sw_volume_from_linear)                   \
+  X(pa_threaded_mainloop_free)                  \
+  X(pa_threaded_mainloop_get_api)               \
+  X(pa_threaded_mainloop_in_thread)             \
+  X(pa_threaded_mainloop_lock)                  \
+  X(pa_threaded_mainloop_new)                   \
+  X(pa_threaded_mainloop_signal)                \
+  X(pa_threaded_mainloop_start)                 \
+  X(pa_threaded_mainloop_stop)                  \
+  X(pa_threaded_mainloop_unlock)                \
+  X(pa_threaded_mainloop_wait)                  \
+  X(pa_usec_to_bytes)                           \
+  X(pa_stream_set_read_callback)                \
+  X(pa_stream_connect_record)                   \
+  X(pa_stream_readable_size)                    \
+  X(pa_stream_writable_size)                    \
+  X(pa_stream_peek)                             \
+  X(pa_stream_drop)                             \
+  X(pa_stream_get_buffer_attr)                  \
+  X(pa_stream_get_device_name)                  \
+  X(pa_context_set_subscribe_callback)          \
+  X(pa_context_subscribe)                       \
+  X(pa_mainloop_api_once)                       \
+  X(pa_get_library_version)                     \
+  X(pa_channel_map_init_auto)                   \
+
+#define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x;
+LIBPULSE_API_VISIT(MAKE_TYPEDEF);
+#undef MAKE_TYPEDEF
+#endif
+
+#if PA_CHECK_VERSION(2, 0, 0)
+static int has_pulse_v2 = 0;
+#endif
+
+static struct cubeb_ops const pulse_ops;
+
+struct cubeb_default_sink_info {
+  pa_channel_map channel_map;
+  uint32_t sample_spec_rate;
+  pa_sink_flags_t flags;
+};
+
+struct cubeb {
+  struct cubeb_ops const * ops;
+  void * libpulse;
+  pa_threaded_mainloop * mainloop;
+  pa_context * context;
+  struct cubeb_default_sink_info * default_sink_info;
+  char * context_name;
+  int error;
+  cubeb_device_collection_changed_callback output_collection_changed_callback;
+  void * output_collection_changed_user_ptr;
+  cubeb_device_collection_changed_callback input_collection_changed_callback;
+  void * input_collection_changed_user_ptr;
+  cubeb_strings * device_ids;
+};
+
+struct cubeb_stream {
+  /* Note: Must match cubeb_stream layout in cubeb.c. */
+  cubeb * context;
+  void * user_ptr;
+  /**/
+  pa_stream * output_stream;
+  pa_stream * input_stream;
+  cubeb_data_callback data_callback;
+  cubeb_state_callback state_callback;
+  pa_time_event * drain_timer;
+  pa_sample_spec output_sample_spec;
+  pa_sample_spec input_sample_spec;
+  int shutdown;
+  float volume;
+  cubeb_state state;
+};
+
+static const float PULSE_NO_GAIN = -1.0;
+
+enum cork_state {
+  UNCORK = 0,
+  CORK = 1 << 0,
+  NOTIFY = 1 << 1
+};
+
+static int
+intern_device_id(cubeb * ctx, char const ** id)
+{
+  char const * interned;
+
+  assert(ctx);
+  assert(id);
+
+  interned = cubeb_strings_intern(ctx->device_ids, *id);
+  if (!interned) {
+    return CUBEB_ERROR;
+  }
+
+  *id = interned;
+
+  return CUBEB_OK;
+}
+
+static void
+sink_info_callback(pa_context * context, const pa_sink_info * info, int eol, void * u)
+{
+  (void)context;
+  cubeb * ctx = u;
+  if (!eol) {
+    free(ctx->default_sink_info);
+    ctx->default_sink_info = malloc(sizeof(struct cubeb_default_sink_info));
+    memcpy(&ctx->default_sink_info->channel_map, &info->channel_map, sizeof(pa_channel_map));
+    ctx->default_sink_info->sample_spec_rate = info->sample_spec.rate;
+    ctx->default_sink_info->flags = info->flags;
+  }
+  WRAP(pa_threaded_mainloop_signal)(ctx->mainloop, 0);
+}
+
+static void
+server_info_callback(pa_context * context, const pa_server_info * info, void * u)
+{
+  pa_operation * o;
+  o = WRAP(pa_context_get_sink_info_by_name)(context, info->default_sink_name, sink_info_callback, u);
+  if (o) {
+    WRAP(pa_operation_unref)(o);
+  }
+}
+
+static void
+context_state_callback(pa_context * c, void * u)
+{
+  cubeb * ctx = u;
+  if (!PA_CONTEXT_IS_GOOD(WRAP(pa_context_get_state)(c))) {
+    ctx->error = 1;
+  }
+  WRAP(pa_threaded_mainloop_signal)(ctx->mainloop, 0);
+}
+
+static void
+context_notify_callback(pa_context * c, void * u)
+{
+  (void)c;
+  cubeb * ctx = u;
+  WRAP(pa_threaded_mainloop_signal)(ctx->mainloop, 0);
+}
+
+static void
+stream_success_callback(pa_stream * s, int success, void * u)
+{
+  (void)s;
+  (void)success;
+  cubeb_stream * stm = u;
+  WRAP(pa_threaded_mainloop_signal)(stm->context->mainloop, 0);
+}
+
+static void
+stream_state_change_callback(cubeb_stream * stm, cubeb_state s)
+{
+  stm->state = s;
+  stm->state_callback(stm, stm->user_ptr, s);
+}
+
+static void
+stream_drain_callback(pa_mainloop_api * a, pa_time_event * e, struct timeval const * tv, void * u)
+{
+  (void)a;
+  (void)tv;
+  cubeb_stream * stm = u;
+  assert(stm->drain_timer == e);
+  stream_state_change_callback(stm, CUBEB_STATE_DRAINED);
+  /* there's no pa_rttime_free, so use this instead. */
+  a->time_free(stm->drain_timer);
+  stm->drain_timer = NULL;
+  WRAP(pa_threaded_mainloop_signal)(stm->context->mainloop, 0);
+}
+
+static void
+stream_state_callback(pa_stream * s, void * u)
+{
+  cubeb_stream * stm = u;
+  if (!PA_STREAM_IS_GOOD(WRAP(pa_stream_get_state)(s))) {
+    stream_state_change_callback(stm, CUBEB_STATE_ERROR);
+  }
+  WRAP(pa_threaded_mainloop_signal)(stm->context->mainloop, 0);
+}
+
+static void
+trigger_user_callback(pa_stream * s, void const * input_data, size_t nbytes, cubeb_stream * stm)
+{
+  void * buffer;
+  size_t size;
+  int r;
+  long got;
+  size_t towrite, read_offset;
+  size_t frame_size;
+
+  frame_size = WRAP(pa_frame_size)(&stm->output_sample_spec);
+  assert(nbytes % frame_size == 0);
+
+  towrite = nbytes;
+  read_offset = 0;
+  while (towrite) {
+    size = towrite;
+    r = WRAP(pa_stream_begin_write)(s, &buffer, &size);
+    // Note: this has failed running under rr on occassion - needs investigation.
+    assert(r == 0);
+    assert(size > 0);
+    assert(size % frame_size == 0);
+
+    LOGV("Trigger user callback with output buffer size=%zd, read_offset=%zd", size, read_offset);
+    got = stm->data_callback(stm, stm->user_ptr, (uint8_t const *)input_data + read_offset, buffer, size / frame_size);
+    if (got < 0) {
+      WRAP(pa_stream_cancel_write)(s);
+      stm->shutdown = 1;
+      return;
+    }
+    // If more iterations move offset of read buffer
+    if (input_data) {
+      size_t in_frame_size = WRAP(pa_frame_size)(&stm->input_sample_spec);
+      read_offset += (size / frame_size) * in_frame_size;
+    }
+
+    if (stm->volume != PULSE_NO_GAIN) {
+      uint32_t samples =  size * stm->output_sample_spec.channels / frame_size ;
+
+      if (stm->output_sample_spec.format == PA_SAMPLE_S16BE ||
+          stm->output_sample_spec.format == PA_SAMPLE_S16LE) {
+        short * b = buffer;
+        for (uint32_t i = 0; i < samples; i++) {
+          b[i] *= stm->volume;
+        }
+      } else {
+        float * b = buffer;
+        for (uint32_t i = 0; i < samples; i++) {
+          b[i] *= stm->volume;
+        }
+      }
+    }
+
+    r = WRAP(pa_stream_write)(s, buffer, got * frame_size, NULL, 0, PA_SEEK_RELATIVE);
+    assert(r == 0);
+
+    if ((size_t) got < size / frame_size) {
+      pa_usec_t latency = 0;
+      r = WRAP(pa_stream_get_latency)(s, &latency, NULL);
+      if (r == -PA_ERR_NODATA) {
+        /* this needs a better guess. */
+        latency = 100 * PA_USEC_PER_MSEC;
+      }
+      assert(r == 0 || r == -PA_ERR_NODATA);
+      /* pa_stream_drain is useless, see PA bug# 866. this is a workaround. */
+      /* arbitrary safety margin: double the current latency. */
+      assert(!stm->drain_timer);
+      stm->drain_timer = WRAP(pa_context_rttime_new)(stm->context->context, WRAP(pa_rtclock_now)() + 2 * latency, stream_drain_callback, stm);
+      stm->shutdown = 1;
+      return;
+    }
+
+    towrite -= size;
+  }
+
+  assert(towrite == 0);
+}
+
+static int
+read_from_input(pa_stream * s, void const ** buffer, size_t * size)
+{
+  size_t readable_size = WRAP(pa_stream_readable_size)(s);
+  if (readable_size > 0) {
+    if (WRAP(pa_stream_peek)(s, buffer, size) < 0) {
+      return -1;
+    }
+  }
+  return readable_size;
+}
+
+static void
+stream_write_callback(pa_stream * s, size_t nbytes, void * u)
+{
+  LOGV("Output callback to be written buffer size %zd", nbytes);
+  cubeb_stream * stm = u;
+  if (stm->shutdown ||
+      stm->state != CUBEB_STATE_STARTED) {
+    return;
+  }
+
+  if (!stm->input_stream){
+    // Output/playback only operation.
+    // Write directly to output
+    assert(!stm->input_stream && stm->output_stream);
+    trigger_user_callback(s, NULL, nbytes, stm);
+  }
+}
+
+static void
+stream_read_callback(pa_stream * s, size_t nbytes, void * u)
+{
+  LOGV("Input callback buffer size %zd", nbytes);
+  cubeb_stream * stm = u;
+  if (stm->shutdown) {
+    return;
+  }
+
+  void const * read_data = NULL;
+  size_t read_size;
+  while (read_from_input(s, &read_data, &read_size) > 0) {
+    /* read_data can be NULL in case of a hole. */
+    if (read_data) {
+      size_t in_frame_size = WRAP(pa_frame_size)(&stm->input_sample_spec);
+      size_t read_frames = read_size / in_frame_size;
+
+      if (stm->output_stream) {
+        // input/capture + output/playback operation
+        size_t out_frame_size = WRAP(pa_frame_size)(&stm->output_sample_spec);
+        size_t write_size = read_frames * out_frame_size;
+        // Offer full duplex data for writing
+        trigger_user_callback(stm->output_stream, read_data, write_size, stm);
+      } else {
+        // input/capture only operation. Call callback directly
+        long got = stm->data_callback(stm, stm->user_ptr, read_data, NULL, read_frames);
+        if (got < 0 || (size_t) got != read_frames) {
+          WRAP(pa_stream_cancel_write)(s);
+          stm->shutdown = 1;
+          break;
+        }
+      }
+    }
+    if (read_size > 0) {
+      WRAP(pa_stream_drop)(s);
+    }
+
+    if (stm->shutdown) {
+      return;
+    }
+  }
+}
+
+static int
+wait_until_context_ready(cubeb * ctx)
+{
+  for (;;) {
+    pa_context_state_t state = WRAP(pa_context_get_state)(ctx->context);
+    if (!PA_CONTEXT_IS_GOOD(state))
+      return -1;
+    if (state == PA_CONTEXT_READY)
+      break;
+    WRAP(pa_threaded_mainloop_wait)(ctx->mainloop);
+  }
+  return 0;
+}
+
+static int
+wait_until_io_stream_ready(pa_stream * stream, pa_threaded_mainloop * mainloop)
+{
+  if (!stream || !mainloop){
+    return -1;
+  }
+  for (;;) {
+    pa_stream_state_t state = WRAP(pa_stream_get_state)(stream);
+    if (!PA_STREAM_IS_GOOD(state))
+      return -1;
+    if (state == PA_STREAM_READY)
+      break;
+    WRAP(pa_threaded_mainloop_wait)(mainloop);
+  }
+  return 0;
+}
+
+static int
+wait_until_stream_ready(cubeb_stream * stm)
+{
+  if (stm->output_stream &&
+      wait_until_io_stream_ready(stm->output_stream, stm->context->mainloop) == -1) {
+    return -1;
+  }
+  if(stm->input_stream &&
+     wait_until_io_stream_ready(stm->input_stream, stm->context->mainloop) == -1) {
+    return -1;
+  }
+  return 0;
+}
+
+static int
+operation_wait(cubeb * ctx, pa_stream * stream, pa_operation * o)
+{
+  while (WRAP(pa_operation_get_state)(o) == PA_OPERATION_RUNNING) {
+    WRAP(pa_threaded_mainloop_wait)(ctx->mainloop);
+    if (!PA_CONTEXT_IS_GOOD(WRAP(pa_context_get_state)(ctx->context))) {
+      return -1;
+    }
+    if (stream && !PA_STREAM_IS_GOOD(WRAP(pa_stream_get_state)(stream))) {
+      return -1;
+    }
+  }
+  return 0;
+}
+
+static void
+cork_io_stream(cubeb_stream * stm, pa_stream * io_stream, enum cork_state state)
+{
+  pa_operation * o;
+  if (!io_stream) {
+    return;
+  }
+  o = WRAP(pa_stream_cork)(io_stream, state & CORK, stream_success_callback, stm);
+  if (o) {
+    operation_wait(stm->context, io_stream, o);
+    WRAP(pa_operation_unref)(o);
+  }
+}
+
+static void
+stream_cork(cubeb_stream * stm, enum cork_state state)
+{
+  WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
+  cork_io_stream(stm, stm->output_stream, state);
+  cork_io_stream(stm, stm->input_stream, state);
+  WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
+
+  if (state & NOTIFY) {
+    stream_state_change_callback(stm, state & CORK ? CUBEB_STATE_STOPPED
+                                                   : CUBEB_STATE_STARTED);
+  }
+}
+
+static int
+stream_update_timing_info(cubeb_stream * stm)
+{
+  int r = -1;
+  pa_operation * o = NULL;
+  if (stm->output_stream) {
+    o = WRAP(pa_stream_update_timing_info)(stm->output_stream, stream_success_callback, stm);
+    if (o) {
+      r = operation_wait(stm->context, stm->output_stream, o);
+      WRAP(pa_operation_unref)(o);
+    }
+    if (r != 0) {
+      return r;
+    }
+  }
+
+  if (stm->input_stream) {
+    o = WRAP(pa_stream_update_timing_info)(stm->input_stream, stream_success_callback, stm);
+    if (o) {
+      r = operation_wait(stm->context, stm->input_stream, o);
+      WRAP(pa_operation_unref)(o);
+    }
+  }
+
+  return r;
+}
+
+static pa_channel_position_t
+cubeb_channel_to_pa_channel(cubeb_channel channel)
+{
+  switch (channel) {
+    case CHANNEL_FRONT_LEFT:
+      return PA_CHANNEL_POSITION_FRONT_LEFT;
+    case CHANNEL_FRONT_RIGHT:
+      return PA_CHANNEL_POSITION_FRONT_RIGHT;
+    case CHANNEL_FRONT_CENTER:
+      return PA_CHANNEL_POSITION_FRONT_CENTER;
+    case CHANNEL_LOW_FREQUENCY:
+      return PA_CHANNEL_POSITION_LFE;
+    case CHANNEL_BACK_LEFT:
+      return PA_CHANNEL_POSITION_REAR_LEFT;
+    case CHANNEL_BACK_RIGHT:
+      return PA_CHANNEL_POSITION_REAR_RIGHT;
+    case CHANNEL_FRONT_LEFT_OF_CENTER:
+      return PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
+    case CHANNEL_FRONT_RIGHT_OF_CENTER:
+      return PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
+    case CHANNEL_BACK_CENTER:
+      return PA_CHANNEL_POSITION_REAR_CENTER;
+    case CHANNEL_SIDE_LEFT:
+      return PA_CHANNEL_POSITION_SIDE_LEFT;
+    case CHANNEL_SIDE_RIGHT:
+      return PA_CHANNEL_POSITION_SIDE_RIGHT;
+    case CHANNEL_TOP_CENTER:
+      return PA_CHANNEL_POSITION_TOP_CENTER;
+    case CHANNEL_TOP_FRONT_LEFT:
+      return PA_CHANNEL_POSITION_TOP_FRONT_LEFT;
+    case CHANNEL_TOP_FRONT_CENTER:
+      return PA_CHANNEL_POSITION_TOP_FRONT_CENTER;
+    case CHANNEL_TOP_FRONT_RIGHT:
+      return PA_CHANNEL_POSITION_TOP_FRONT_RIGHT;
+    case CHANNEL_TOP_BACK_LEFT:
+      return PA_CHANNEL_POSITION_TOP_REAR_LEFT;
+    case CHANNEL_TOP_BACK_CENTER:
+      return PA_CHANNEL_POSITION_TOP_REAR_CENTER;
+    case CHANNEL_TOP_BACK_RIGHT:
+      return PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
+    default:
+      return PA_CHANNEL_POSITION_INVALID;
+  }
+}
+
+static void
+layout_to_channel_map(cubeb_channel_layout layout, pa_channel_map * cm)
+{
+  assert(cm && layout != CUBEB_LAYOUT_UNDEFINED);
+
+  WRAP(pa_channel_map_init)(cm);
+
+  uint32_t channels = 0;
+  cubeb_channel_layout channelMap = layout;
+  for (uint32_t i = 0 ; channelMap != 0; ++i) {
+    uint32_t channel = (channelMap & 1) << i;
+    if (channel != 0) {
+      cm->map[channels] = cubeb_channel_to_pa_channel(channel);
+      channels++;
+    }
+    channelMap = channelMap >> 1;
+  }
+  unsigned int channels_from_layout = cubeb_channel_layout_nb_channels(layout);
+  assert(channels_from_layout <= UINT8_MAX);
+  cm->channels = (uint8_t) channels_from_layout;
+
+  // Special case single channel center mapping as mono.
+  if (cm->channels == 1 && cm->map[0] == PA_CHANNEL_POSITION_FRONT_CENTER) {
+    cm->map[0] = PA_CHANNEL_POSITION_MONO;
+  }
+}
+
+static void pulse_context_destroy(cubeb * ctx);
+static void pulse_destroy(cubeb * ctx);
+
+static int
+pulse_context_init(cubeb * ctx)
+{
+  int r;
+
+  if (ctx->context) {
+    assert(ctx->error == 1);
+    pulse_context_destroy(ctx);
+  }
+
+  ctx->context = WRAP(pa_context_new)(WRAP(pa_threaded_mainloop_get_api)(ctx->mainloop),
+                                      ctx->context_name);
+  if (!ctx->context) {
+    return -1;
+  }
+  WRAP(pa_context_set_state_callback)(ctx->context, context_state_callback, ctx);
+
+  WRAP(pa_threaded_mainloop_lock)(ctx->mainloop);
+  r = WRAP(pa_context_connect)(ctx->context, NULL, 0, NULL);
+
+  if (r < 0 || wait_until_context_ready(ctx) != 0) {
+    WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop);
+    pulse_context_destroy(ctx);
+    ctx->context = NULL;
+    return -1;
+  }
+
+  WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop);
+
+  ctx->error = 0;
+
+  return 0;
+}
+
+static int pulse_subscribe_notifications(cubeb * context,
+                                         pa_subscription_mask_t mask);
+
+/*static*/ int
+pulse_init(cubeb ** context, char const * context_name)
+{
+  void * libpulse = NULL;
+  cubeb * ctx;
+  pa_operation * o;
+
+  *context = NULL;
+
+#ifndef DISABLE_LIBPULSE_DLOPEN
+  libpulse = dlopen("libpulse.so.0", RTLD_LAZY);
+  if (!libpulse) {
+    libpulse = dlopen("libpulse.so", RTLD_LAZY);
+    if (!libpulse) {
+      return CUBEB_ERROR;
+    }
+  }
+
+#define LOAD(x) {                               \
+    cubeb_##x = dlsym(libpulse, #x);            \
+    if (!cubeb_##x) {                           \
+      dlclose(libpulse);                        \
+      return CUBEB_ERROR;                       \
+    }                                           \
+  }
+
+  LIBPULSE_API_VISIT(LOAD);
+#undef LOAD
+#endif
+
+#if PA_CHECK_VERSION(2, 0, 0)
+  const char* version = WRAP(pa_get_library_version)();
+  has_pulse_v2 = strtol(version, NULL, 10) >= 2;
+#endif
+
+  ctx = calloc(1, sizeof(*ctx));
+  assert(ctx);
+
+  ctx->ops = &pulse_ops;
+  ctx->libpulse = libpulse;
+  if (cubeb_strings_init(&ctx->device_ids) != CUBEB_OK) {
+    pulse_destroy(ctx);
+    return CUBEB_ERROR;
+  }
+
+  ctx->mainloop = WRAP(pa_threaded_mainloop_new)();
+  ctx->default_sink_info = NULL;
+
+  WRAP(pa_threaded_mainloop_start)(ctx->mainloop);
+
+  ctx->context_name = context_name ? strdup(context_name) : NULL;
+  if (pulse_context_init(ctx) != 0) {
+    pulse_destroy(ctx);
+    return CUBEB_ERROR;
+  }
+
+  /* server_info_callback performs a second async query, which is
+     responsible for initializing default_sink_info and signalling the
+     mainloop to end the wait. */
+  WRAP(pa_threaded_mainloop_lock)(ctx->mainloop);
+  o = WRAP(pa_context_get_server_info)(ctx->context, server_info_callback, ctx);
+  if (o) {
+    operation_wait(ctx, NULL, o);
+    WRAP(pa_operation_unref)(o);
+  }
+  WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop);
+
+  /* Update `default_sink_info` when the default device changes. */
+  pulse_subscribe_notifications(ctx, PA_SUBSCRIPTION_MASK_SERVER);
+
+  *context = ctx;
+
+  return CUBEB_OK;
+}
+
+static char const *
+pulse_get_backend_id(cubeb * ctx)
+{
+  (void)ctx;
+  return "pulse";
+}
+
+static int
+pulse_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
+{
+  (void)ctx;
+  assert(ctx && max_channels);
+
+  if (!ctx->default_sink_info)
+    return CUBEB_ERROR;
+
+  *max_channels = ctx->default_sink_info->channel_map.channels;
+
+  return CUBEB_OK;
+}
+
+static int
+pulse_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
+{
+  assert(ctx && rate);
+  (void)ctx;
+
+  if (!ctx->default_sink_info)
+    return CUBEB_ERROR;
+
+  *rate = ctx->default_sink_info->sample_spec_rate;
+
+  return CUBEB_OK;
+}
+
+static int
+pulse_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
+{
+  (void)ctx;
+  // According to PulseAudio developers, this is a safe minimum.
+  *latency_frames = 25 * params.rate / 1000;
+
+  return CUBEB_OK;
+}
+
+static void
+pulse_context_destroy(cubeb * ctx)
+{
+  pa_operation * o;
+
+  WRAP(pa_threaded_mainloop_lock)(ctx->mainloop);
+  o = WRAP(pa_context_drain)(ctx->context, context_notify_callback, ctx);
+  if (o) {
+    operation_wait(ctx, NULL, o);
+    WRAP(pa_operation_unref)(o);
+  }
+  WRAP(pa_context_set_state_callback)(ctx->context, NULL, NULL);
+  WRAP(pa_context_disconnect)(ctx->context);
+  WRAP(pa_context_unref)(ctx->context);
+  WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop);
+}
+
+static void
+pulse_destroy(cubeb * ctx)
+{
+  free(ctx->context_name);
+  if (ctx->context) {
+    pulse_context_destroy(ctx);
+  }
+
+  if (ctx->mainloop) {
+    WRAP(pa_threaded_mainloop_stop)(ctx->mainloop);
+    WRAP(pa_threaded_mainloop_free)(ctx->mainloop);
+  }
+
+  if (ctx->device_ids) {
+    cubeb_strings_destroy(ctx->device_ids);
+  }
+
+  if (ctx->libpulse) {
+    dlclose(ctx->libpulse);
+  }
+  free(ctx->default_sink_info);
+  free(ctx);
+}
+
+static void pulse_stream_destroy(cubeb_stream * stm);
+
+static pa_sample_format_t
+to_pulse_format(cubeb_sample_format format)
+{
+  switch (format) {
+  case CUBEB_SAMPLE_S16LE:
+    return PA_SAMPLE_S16LE;
+  case CUBEB_SAMPLE_S16BE:
+    return PA_SAMPLE_S16BE;
+  case CUBEB_SAMPLE_FLOAT32LE:
+    return PA_SAMPLE_FLOAT32LE;
+  case CUBEB_SAMPLE_FLOAT32BE:
+    return PA_SAMPLE_FLOAT32BE;
+  default:
+    return PA_SAMPLE_INVALID;
+  }
+}
+
+static cubeb_channel_layout
+pulse_default_layout_for_channels(uint32_t ch)
+{
+  assert (ch > 0 && ch <= 8);
+  switch (ch) {
+    case 1: return CUBEB_LAYOUT_MONO;
+    case 2: return CUBEB_LAYOUT_STEREO;
+    case 3: return CUBEB_LAYOUT_3F;
+    case 4: return CUBEB_LAYOUT_QUAD;
+    case 5: return CUBEB_LAYOUT_3F2;
+    case 6: return CUBEB_LAYOUT_3F_LFE |
+                   CHANNEL_SIDE_LEFT | CHANNEL_SIDE_RIGHT;
+    case 7: return CUBEB_LAYOUT_3F3R_LFE;
+    case 8: return CUBEB_LAYOUT_3F4_LFE;
+  }
+  // Never get here!
+  return CUBEB_LAYOUT_UNDEFINED;
+}
+
+static int
+create_pa_stream(cubeb_stream * stm,
+                 pa_stream ** pa_stm,
+                 cubeb_stream_params * stream_params,
+                 char const * stream_name)
+{
+  assert(stm && stream_params);
+  assert(&stm->input_stream == pa_stm || (&stm->output_stream == pa_stm &&
+         (stream_params->layout == CUBEB_LAYOUT_UNDEFINED ||
+         (stream_params->layout != CUBEB_LAYOUT_UNDEFINED &&
+         cubeb_channel_layout_nb_channels(stream_params->layout) == stream_params->channels))));
+  if (stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
+    return CUBEB_ERROR_NOT_SUPPORTED;
+  }
+  *pa_stm = NULL;
+  pa_sample_spec ss;
+  ss.format = to_pulse_format(stream_params->format);
+  if (ss.format == PA_SAMPLE_INVALID)
+    return CUBEB_ERROR_INVALID_FORMAT;
+  ss.rate = stream_params->rate;
+  if (stream_params->channels > UINT8_MAX)
+    return CUBEB_ERROR_INVALID_FORMAT;
+  ss.channels = (uint8_t) stream_params->channels;
+
+  if (stream_params->layout == CUBEB_LAYOUT_UNDEFINED) {
+    pa_channel_map cm;
+    if (stream_params->channels <= 8 &&
+       !WRAP(pa_channel_map_init_auto)(&cm, stream_params->channels, PA_CHANNEL_MAP_DEFAULT)) {
+      LOG("Layout undefined and PulseAudio's default layout has not been configured, guess one.");
+      layout_to_channel_map(pulse_default_layout_for_channels(stream_params->channels), &cm);
+      *pa_stm = WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, &cm);
+    } else {
+      LOG("Layout undefined, PulseAudio will use its default.");
+      *pa_stm = WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, NULL);
+    }
+  } else {
+    pa_channel_map cm;
+    layout_to_channel_map(stream_params->layout, &cm);
+    *pa_stm = WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, &cm);
+  }
+  return (*pa_stm == NULL) ? CUBEB_ERROR : CUBEB_OK;
+}
+
+static pa_buffer_attr
+set_buffering_attribute(unsigned int latency_frames, pa_sample_spec * sample_spec)
+{
+  pa_buffer_attr battr;
+  battr.maxlength = -1;
+  battr.prebuf    = -1;
+  battr.tlength   = latency_frames * WRAP(pa_frame_size)(sample_spec);
+  battr.minreq    = battr.tlength / 4;
+  battr.fragsize  = battr.minreq;
+
+  LOG("Requested buffer attributes maxlength %u, tlength %u, prebuf %u, minreq %u, fragsize %u",
+      battr.maxlength, battr.tlength, battr.prebuf, battr.minreq, battr.fragsize);
+
+  return battr;
+}
+
+static int
+pulse_stream_init(cubeb * context,
+                  cubeb_stream ** stream,
+                  char const * stream_name,
+                  cubeb_devid input_device,
+                  cubeb_stream_params * input_stream_params,
+                  cubeb_devid output_device,
+                  cubeb_stream_params * output_stream_params,
+                  unsigned int latency_frames,
+                  cubeb_data_callback data_callback,
+                  cubeb_state_callback state_callback,
+                  void * user_ptr)
+{
+  cubeb_stream * stm;
+  pa_buffer_attr battr;
+  int r;
+
+  assert(context);
+
+  // If the connection failed for some reason, try to reconnect
+  if (context->error == 1 && pulse_context_init(context) != 0) {
+    return CUBEB_ERROR;
+  }
+
+  *stream = NULL;
+
+  stm = calloc(1, sizeof(*stm));
+  assert(stm);
+
+  stm->context = context;
+  stm->data_callback = data_callback;
+  stm->state_callback = state_callback;
+  stm->user_ptr = user_ptr;
+  stm->volume = PULSE_NO_GAIN;
+  stm->state = -1;
+  assert(stm->shutdown == 0);
+
+  WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
+  if (output_stream_params) {
+    r = create_pa_stream(stm, &stm->output_stream, output_stream_params, stream_name);
+    if (r != CUBEB_OK) {
+      WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
+      pulse_stream_destroy(stm);
+      return r;
+    }
+
+    stm->output_sample_spec = *(WRAP(pa_stream_get_sample_spec)(stm->output_stream));
+
+    WRAP(pa_stream_set_state_callback)(stm->output_stream, stream_state_callback, stm);
+    WRAP(pa_stream_set_write_callback)(stm->output_stream, stream_write_callback, stm);
+
+    battr = set_buffering_attribute(latency_frames, &stm->output_sample_spec);
+    WRAP(pa_stream_connect_playback)(stm->output_stream,
+                                     (char const *) output_device,
+                                     &battr,
+                                     PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING |
+                                     PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY,
+                                     NULL, NULL);
+  }
+
+  // Set up input stream
+  if (input_stream_params) {
+    r = create_pa_stream(stm, &stm->input_stream, input_stream_params, stream_name);
+    if (r != CUBEB_OK) {
+      WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
+      pulse_stream_destroy(stm);
+      return r;
+    }
+
+    stm->input_sample_spec = *(WRAP(pa_stream_get_sample_spec)(stm->input_stream));
+
+    WRAP(pa_stream_set_state_callback)(stm->input_stream, stream_state_callback, stm);
+    WRAP(pa_stream_set_read_callback)(stm->input_stream, stream_read_callback, stm);
+
+    battr = set_buffering_attribute(latency_frames, &stm->input_sample_spec);
+    WRAP(pa_stream_connect_record)(stm->input_stream,
+                                   (char const *) input_device,
+                                   &battr,
+                                   PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING |
+                                   PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY);
+  }
+
+  r = wait_until_stream_ready(stm);
+  if (r == 0) {
+    /* force a timing update now, otherwise timing info does not become valid
+       until some point after initialization has completed. */
+    r = stream_update_timing_info(stm);
+  }
+
+  WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
+
+  if (r != 0) {
+    pulse_stream_destroy(stm);
+    return CUBEB_ERROR;
+  }
+
+  if (g_cubeb_log_level) {
+    if (output_stream_params){
+      const pa_buffer_attr * output_att;
+      output_att = WRAP(pa_stream_get_buffer_attr)(stm->output_stream);
+      LOG("Output buffer attributes maxlength %u, tlength %u, prebuf %u, minreq %u, fragsize %u",output_att->maxlength, output_att->tlength,
+          output_att->prebuf, output_att->minreq, output_att->fragsize);
+    }
+
+    if (input_stream_params){
+      const pa_buffer_attr * input_att;
+      input_att = WRAP(pa_stream_get_buffer_attr)(stm->input_stream);
+      LOG("Input buffer attributes maxlength %u, tlength %u, prebuf %u, minreq %u, fragsize %u",input_att->maxlength, input_att->tlength,
+          input_att->prebuf, input_att->minreq, input_att->fragsize);
+    }
+  }
+
+  *stream = stm;
+  LOG("Cubeb stream (%p) init successful.", *stream);
+
+  return CUBEB_OK;
+}
+
+static void
+pulse_stream_destroy(cubeb_stream * stm)
+{
+  stream_cork(stm, CORK);
+
+  WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
+  if (stm->output_stream) {
+
+    if (stm->drain_timer) {
+      /* there's no pa_rttime_free, so use this instead. */
+      WRAP(pa_threaded_mainloop_get_api)(stm->context->mainloop)->time_free(stm->drain_timer);
+    }
+
+    WRAP(pa_stream_set_state_callback)(stm->output_stream, NULL, NULL);
+    WRAP(pa_stream_set_write_callback)(stm->output_stream, NULL, NULL);
+    WRAP(pa_stream_disconnect)(stm->output_stream);
+    WRAP(pa_stream_unref)(stm->output_stream);
+  }
+
+  if (stm->input_stream) {
+    WRAP(pa_stream_set_state_callback)(stm->input_stream, NULL, NULL);
+    WRAP(pa_stream_set_read_callback)(stm->input_stream, NULL, NULL);
+    WRAP(pa_stream_disconnect)(stm->input_stream);
+    WRAP(pa_stream_unref)(stm->input_stream);
+  }
+  WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
+
+  LOG("Cubeb stream (%p) destroyed successfully.", stm);
+  free(stm);
+}
+
+static void
+pulse_defer_event_cb(pa_mainloop_api * a, void * userdata)
+{
+  (void)a;
+  cubeb_stream * stm = userdata;
+  if (stm->shutdown) {
+    return;
+  }
+  size_t writable_size = WRAP(pa_stream_writable_size)(stm->output_stream);
+  trigger_user_callback(stm->output_stream, NULL, writable_size, stm);
+}
+
+static int
+pulse_stream_start(cubeb_stream * stm)
+{
+  stm->shutdown = 0;
+  stream_cork(stm, UNCORK | NOTIFY);
+
+  if (stm->output_stream && !stm->input_stream) {
+    /* On output only case need to manually call user cb once in order to make
+     * things roll. This is done via a defer event in order to execute it
+     * from PA server thread. */
+    WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
+    WRAP(pa_mainloop_api_once)(WRAP(pa_threaded_mainloop_get_api)(stm->context->mainloop),
+                               pulse_defer_event_cb, stm);
+    WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
+  }
+
+  LOG("Cubeb stream (%p) started successfully.", stm);
+  return CUBEB_OK;
+}
+
+static int
+pulse_stream_stop(cubeb_stream * stm)
+{
+  WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
+  stm->shutdown = 1;
+  // If draining is taking place wait to finish
+  while (stm->drain_timer) {
+    WRAP(pa_threaded_mainloop_wait)(stm->context->mainloop);
+  }
+  WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
+
+  stream_cork(stm, CORK | NOTIFY);
+  LOG("Cubeb stream (%p) stopped successfully.", stm);
+  return CUBEB_OK;
+}
+
+static int
+pulse_stream_get_position(cubeb_stream * stm, uint64_t * position)
+{
+  int r, in_thread;
+  pa_usec_t r_usec;
+  uint64_t bytes;
+
+  if (!stm || !stm->output_stream) {
+    return CUBEB_ERROR;
+  }
+
+  in_thread = WRAP(pa_threaded_mainloop_in_thread)(stm->context->mainloop);
+
+  if (!in_thread) {
+    WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
+  }
+  r = WRAP(pa_stream_get_time)(stm->output_stream, &r_usec);
+  if (!in_thread) {
+    WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
+  }
+
+  if (r != 0) {
+    return CUBEB_ERROR;
+  }
+
+  bytes = WRAP(pa_usec_to_bytes)(r_usec, &stm->output_sample_spec);
+  *position = bytes / WRAP(pa_frame_size)(&stm->output_sample_spec);
+
+  return CUBEB_OK;
+}
+
+static int
+pulse_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
+{
+  pa_usec_t r_usec;
+  int negative, r;
+
+  if (!stm || !stm->output_stream) {
+    return CUBEB_ERROR;
+  }
+
+  r = WRAP(pa_stream_get_latency)(stm->output_stream, &r_usec, &negative);
+  assert(!negative);
+  if (r) {
+    return CUBEB_ERROR;
+  }
+
+  *latency = r_usec * stm->output_sample_spec.rate / PA_USEC_PER_SEC;
+  return CUBEB_OK;
+}
+
+static void
+volume_success(pa_context *c, int success, void *userdata)
+{
+  (void)success;
+  (void)c;
+  cubeb_stream * stream = userdata;
+  assert(success);
+  WRAP(pa_threaded_mainloop_signal)(stream->context->mainloop, 0);
+}
+
+static int
+pulse_stream_set_volume(cubeb_stream * stm, float volume)
+{
+  uint32_t index;
+  pa_operation * op;
+  pa_volume_t vol;
+  pa_cvolume cvol;
+  const pa_sample_spec * ss;
+  cubeb * ctx;
+
+  if (!stm->output_stream) {
+    return CUBEB_ERROR;
+  }
+
+  WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
+
+  /* if the pulse daemon is configured to use flat volumes,
+   * apply our own gain instead of changing the input volume on the sink. */
+  ctx = stm->context;
+  if (ctx->default_sink_info &&
+      (ctx->default_sink_info->flags & PA_SINK_FLAT_VOLUME)) {
+    stm->volume = volume;
+  } else {
+    ss = WRAP(pa_stream_get_sample_spec)(stm->output_stream);
+
+    vol = WRAP(pa_sw_volume_from_linear)(volume);
+    WRAP(pa_cvolume_set)(&cvol, ss->channels, vol);
+
+    index = WRAP(pa_stream_get_index)(stm->output_stream);
+
+    op = WRAP(pa_context_set_sink_input_volume)(ctx->context,
+                                                index, &cvol, volume_success,
+                                                stm);
+    if (op) {
+      operation_wait(ctx, stm->output_stream, op);
+      WRAP(pa_operation_unref)(op);
+    }
+  }
+
+  WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop);
+
+  return CUBEB_OK;
+}
+
+typedef struct {
+  char * default_sink_name;
+  char * default_source_name;
+
+  cubeb_device_info * devinfo;
+  uint32_t max;
+  uint32_t count;
+  cubeb * context;
+} pulse_dev_list_data;
+
+static cubeb_device_fmt
+pulse_format_to_cubeb_format(pa_sample_format_t format)
+{
+  switch (format) {
+  case PA_SAMPLE_S16LE:
+    return CUBEB_DEVICE_FMT_S16LE;
+  case PA_SAMPLE_S16BE:
+    return CUBEB_DEVICE_FMT_S16BE;
+  case PA_SAMPLE_FLOAT32LE:
+    return CUBEB_DEVICE_FMT_F32LE;
+  case PA_SAMPLE_FLOAT32BE:
+    return CUBEB_DEVICE_FMT_F32BE;
+  default:
+    return CUBEB_DEVICE_FMT_F32NE;
+  }
+}
+
+static void
+pulse_ensure_dev_list_data_list_size (pulse_dev_list_data * list_data)
+{
+  if (list_data->count == list_data->max) {
+    list_data->max += 8;
+    list_data->devinfo = realloc(list_data->devinfo,
+        sizeof(cubeb_device_info) * list_data->max);
+  }
+}
+
+static cubeb_device_state
+pulse_get_state_from_sink_port(pa_sink_port_info * info)
+{
+  if (info != NULL) {
+#if PA_CHECK_VERSION(2, 0, 0)
+    if (has_pulse_v2 && info->available == PA_PORT_AVAILABLE_NO)
+      return CUBEB_DEVICE_STATE_UNPLUGGED;
+    else /*if (info->available == PA_PORT_AVAILABLE_YES) + UNKNOWN */
+#endif
+      return CUBEB_DEVICE_STATE_ENABLED;
+  }
+
+  return CUBEB_DEVICE_STATE_ENABLED;
+}
+
+static void
+pulse_sink_info_cb(pa_context * context, const pa_sink_info * info,
+                   int eol, void * user_data)
+{
+  pulse_dev_list_data * list_data = user_data;
+  cubeb_device_info * devinfo;
+  char const * prop = NULL;
+  char const * device_id = NULL;
+
+  (void)context;
+
+  if (eol) {
+    WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0);
+    return;
+  }
+
+  if (info == NULL)
+    return;
+
+  device_id = info->name;
+  if (intern_device_id(list_data->context, &device_id) != CUBEB_OK) {
+    assert(NULL);
+    return;
+  }
+
+  pulse_ensure_dev_list_data_list_size(list_data);
+  devinfo = &list_data->devinfo[list_data->count];
+  memset(devinfo, 0, sizeof(cubeb_device_info));
+
+  devinfo->device_id = device_id;
+  devinfo->devid = (cubeb_devid) devinfo->device_id;
+  devinfo->friendly_name = strdup(info->description);
+  prop = WRAP(pa_proplist_gets)(info->proplist, "sysfs.path");
+  if (prop)
+    devinfo->group_id = strdup(prop);
+  prop = WRAP(pa_proplist_gets)(info->proplist, "device.vendor.name");
+  if (prop)
+    devinfo->vendor_name = strdup(prop);
+
+  devinfo->type = CUBEB_DEVICE_TYPE_OUTPUT;
+  devinfo->state = pulse_get_state_from_sink_port(info->active_port);
+  devinfo->preferred = (strcmp(info->name, list_data->default_sink_name) == 0) ?
+    CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE;
+
+  devinfo->format = CUBEB_DEVICE_FMT_ALL;
+  devinfo->default_format = pulse_format_to_cubeb_format(info->sample_spec.format);
+  devinfo->max_channels = info->channel_map.channels;
+  devinfo->min_rate = 1;
+  devinfo->max_rate = PA_RATE_MAX;
+  devinfo->default_rate = info->sample_spec.rate;
+
+  devinfo->latency_lo = 0;
+  devinfo->latency_hi = 0;
+
+  list_data->count += 1;
+}
+
+static cubeb_device_state
+pulse_get_state_from_source_port(pa_source_port_info * info)
+{
+  if (info != NULL) {
+#if PA_CHECK_VERSION(2, 0, 0)
+    if (has_pulse_v2 && info->available == PA_PORT_AVAILABLE_NO)
+      return CUBEB_DEVICE_STATE_UNPLUGGED;
+    else /*if (info->available == PA_PORT_AVAILABLE_YES) + UNKNOWN */
+#endif
+      return CUBEB_DEVICE_STATE_ENABLED;
+  }
+
+  return CUBEB_DEVICE_STATE_ENABLED;
+}
+
+static void
+pulse_source_info_cb(pa_context * context, const pa_source_info * info,
+    int eol, void * user_data)
+{
+  pulse_dev_list_data * list_data = user_data;
+  cubeb_device_info * devinfo;
+  char const * prop = NULL;
+  char const * device_id = NULL;
+
+  (void)context;
+
+  if (eol) {
+    WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0);
+    return;
+  }
+
+  device_id = info->name;
+  if (intern_device_id(list_data->context, &device_id) != CUBEB_OK) {
+    assert(NULL);
+    return;
+  }
+
+  pulse_ensure_dev_list_data_list_size(list_data);
+  devinfo = &list_data->devinfo[list_data->count];
+  memset(devinfo, 0, sizeof(cubeb_device_info));
+
+  devinfo->device_id = device_id;
+  devinfo->devid = (cubeb_devid) devinfo->device_id;
+  devinfo->friendly_name = strdup(info->description);
+  prop = WRAP(pa_proplist_gets)(info->proplist, "sysfs.path");
+  if (prop)
+    devinfo->group_id = strdup(prop);
+  prop = WRAP(pa_proplist_gets)(info->proplist, "device.vendor.name");
+  if (prop)
+    devinfo->vendor_name = strdup(prop);
+
+  devinfo->type = CUBEB_DEVICE_TYPE_INPUT;
+  devinfo->state = pulse_get_state_from_source_port(info->active_port);
+  devinfo->preferred = (strcmp(info->name, list_data->default_source_name) == 0) ?
+    CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE;
+
+  devinfo->format = CUBEB_DEVICE_FMT_ALL;
+  devinfo->default_format = pulse_format_to_cubeb_format(info->sample_spec.format);
+  devinfo->max_channels = info->channel_map.channels;
+  devinfo->min_rate = 1;
+  devinfo->max_rate = PA_RATE_MAX;
+  devinfo->default_rate = info->sample_spec.rate;
+
+  devinfo->latency_lo = 0;
+  devinfo->latency_hi = 0;
+
+  list_data->count += 1;
+}
+
+static void
+pulse_server_info_cb(pa_context * c, const pa_server_info * i, void * userdata)
+{
+  pulse_dev_list_data * list_data = userdata;
+
+  (void)c;
+
+  free(list_data->default_sink_name);
+  free(list_data->default_source_name);
+  list_data->default_sink_name =
+    i->default_sink_name ? strdup(i->default_sink_name) : NULL;
+  list_data->default_source_name =
+    i->default_source_name ? strdup(i->default_source_name) : NULL;
+
+  WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0);
+}
+
+static int
+pulse_enumerate_devices(cubeb * context, cubeb_device_type type,
+                        cubeb_device_collection * collection)
+{
+  pulse_dev_list_data user_data = { NULL, NULL, NULL, 0, 0, context };
+  pa_operation * o;
+
+  WRAP(pa_threaded_mainloop_lock)(context->mainloop);
+
+  o = WRAP(pa_context_get_server_info)(context->context,
+      pulse_server_info_cb, &user_data);
+  if (o) {
+    operation_wait(context, NULL, o);
+    WRAP(pa_operation_unref)(o);
+  }
+
+  if (type & CUBEB_DEVICE_TYPE_OUTPUT) {
+    o = WRAP(pa_context_get_sink_info_list)(context->context,
+        pulse_sink_info_cb, &user_data);
+    if (o) {
+      operation_wait(context, NULL, o);
+      WRAP(pa_operation_unref)(o);
+    }
+  }
+
+  if (type & CUBEB_DEVICE_TYPE_INPUT) {
+    o = WRAP(pa_context_get_source_info_list)(context->context,
+        pulse_source_info_cb, &user_data);
+    if (o) {
+      operation_wait(context, NULL, o);
+      WRAP(pa_operation_unref)(o);
+    }
+  }
+
+  WRAP(pa_threaded_mainloop_unlock)(context->mainloop);
+
+  collection->device = user_data.devinfo;
+  collection->count = user_data.count;
+
+  free(user_data.default_sink_name);
+  free(user_data.default_source_name);
+  return CUBEB_OK;
+}
+
+static int
+pulse_device_collection_destroy(cubeb * ctx, cubeb_device_collection * collection)
+{
+  size_t n;
+
+  for (n = 0; n < collection->count; n++) {
+    free((void *) collection->device[n].friendly_name);
+    free((void *) collection->device[n].vendor_name);
+    free((void *) collection->device[n].group_id);
+  }
+
+  free(collection->device);
+  return CUBEB_OK;
+}
+
+static int
+pulse_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const device)
+{
+#if PA_CHECK_VERSION(0, 9, 8)
+  *device = calloc(1, sizeof(cubeb_device));
+  if (*device == NULL)
+    return CUBEB_ERROR;
+
+  if (stm->input_stream) {
+    const char * name = WRAP(pa_stream_get_device_name)(stm->input_stream);
+    (*device)->input_name = (name == NULL) ? NULL : strdup(name);
+  }
+
+  if (stm->output_stream) {
+    const char * name = WRAP(pa_stream_get_device_name)(stm->output_stream);
+    (*device)->output_name = (name == NULL) ? NULL : strdup(name);
+  }
+
+  return CUBEB_OK;
+#else
+  return CUBEB_ERROR_NOT_SUPPORTED;
+#endif
+}
+
+static int
+pulse_stream_device_destroy(cubeb_stream * stream,
+                            cubeb_device * device)
+{
+  (void)stream;
+  free(device->input_name);
+  free(device->output_name);
+  free(device);
+  return CUBEB_OK;
+}
+
+static void
+pulse_subscribe_callback(pa_context * ctx,
+                         pa_subscription_event_type_t t,
+                         uint32_t index, void * userdata)
+{
+  (void)ctx;
+  cubeb * context = userdata;
+
+  switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
+  case PA_SUBSCRIPTION_EVENT_SERVER:
+    if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) {
+      LOG("Server changed %d", index);
+      WRAP(pa_context_get_server_info)(context->context, server_info_callback, context);
+    }
+    break;
+  case PA_SUBSCRIPTION_EVENT_SOURCE:
+  case PA_SUBSCRIPTION_EVENT_SINK:
+
+    if (g_cubeb_log_level) {
+      if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE &&
+          (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+        LOG("Removing source index %d", index);
+      } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE &&
+          (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
+        LOG("Adding source index %d", index);
+      }
+      if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK &&
+          (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+        LOG("Removing sink index %d", index);
+      } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK &&
+          (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
+        LOG("Adding sink index %d", index);
+      }
+    }
+
+    if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE ||
+        (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
+      if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) {
+        context->input_collection_changed_callback(context, context->input_collection_changed_user_ptr);
+      }
+      if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
+        context->output_collection_changed_callback(context, context->output_collection_changed_user_ptr);
+      }
+    }
+    break;
+  }
+}
+
+static void
+subscribe_success(pa_context *c, int success, void *userdata)
+{
+  (void)c;
+  cubeb * context = userdata;
+  assert(success);
+  WRAP(pa_threaded_mainloop_signal)(context->mainloop, 0);
+}
+
+static int
+pulse_subscribe_notifications(cubeb * context, pa_subscription_mask_t mask) {
+  WRAP(pa_threaded_mainloop_lock)(context->mainloop);
+
+  WRAP(pa_context_set_subscribe_callback)(context->context, pulse_subscribe_callback, context);
+
+  pa_operation * o;
+  o = WRAP(pa_context_subscribe)(context->context, mask, subscribe_success, context);
+  if (o == NULL) {
+    WRAP(pa_threaded_mainloop_unlock)(context->mainloop);
+    LOG("Context subscribe failed");
+    return CUBEB_ERROR;
+  }
+  operation_wait(context, NULL, o);
+  WRAP(pa_operation_unref)(o);
+
+  WRAP(pa_threaded_mainloop_unlock)(context->mainloop);
+
+  return CUBEB_OK;
+}
+
+static int
+pulse_register_device_collection_changed(cubeb * context,
+                                         cubeb_device_type devtype,
+                                         cubeb_device_collection_changed_callback collection_changed_callback,
+                                         void * user_ptr)
+{
+  if (devtype & CUBEB_DEVICE_TYPE_INPUT) {
+    context->input_collection_changed_callback = collection_changed_callback;
+    context->input_collection_changed_user_ptr = user_ptr;
+  }
+  if (devtype & CUBEB_DEVICE_TYPE_OUTPUT) {
+    context->output_collection_changed_callback = collection_changed_callback;
+    context->output_collection_changed_user_ptr = user_ptr;
+  }
+
+  pa_subscription_mask_t mask = PA_SUBSCRIPTION_MASK_NULL;
+  if (context->input_collection_changed_callback) {
+    /* Input added or removed */
+    mask |= PA_SUBSCRIPTION_MASK_SOURCE;
+  }
+  if (context->output_collection_changed_callback) {
+    /* Output added or removed */
+    mask |= PA_SUBSCRIPTION_MASK_SINK;
+  }
+  /* Default device changed, this is always registered in order to update the
+   * `default_sink_info` when the default device changes. */
+  mask |= PA_SUBSCRIPTION_MASK_SERVER;
+
+  return pulse_subscribe_notifications(context, mask);
+}
+
+static struct cubeb_ops const pulse_ops = {
+  .init = pulse_init,
+  .get_backend_id = pulse_get_backend_id,
+  .get_max_channel_count = pulse_get_max_channel_count,
+  .get_min_latency = pulse_get_min_latency,
+  .get_preferred_sample_rate = pulse_get_preferred_sample_rate,
+  .enumerate_devices = pulse_enumerate_devices,
+  .device_collection_destroy = pulse_device_collection_destroy,
+  .destroy = pulse_destroy,
+  .stream_init = pulse_stream_init,
+  .stream_destroy = pulse_stream_destroy,
+  .stream_start = pulse_stream_start,
+  .stream_stop = pulse_stream_stop,
+  .stream_reset_default_device = NULL,
+  .stream_get_position = pulse_stream_get_position,
+  .stream_get_latency = pulse_stream_get_latency,
+  .stream_set_volume = pulse_stream_set_volume,
+  .stream_get_current_device = pulse_stream_get_current_device,
+  .stream_device_destroy = pulse_stream_device_destroy,
+  .stream_register_device_changed_callback = NULL,
+  .register_device_collection_changed = pulse_register_device_collection_changed
+};
diff --git a/dep/cubeb/src/cubeb_resampler.cpp b/dep/cubeb/src/cubeb_resampler.cpp
new file mode 100644
index 000000000..3fc09d294
--- /dev/null
+++ b/dep/cubeb/src/cubeb_resampler.cpp
@@ -0,0 +1,372 @@
+/*
+ * Copyright © 2014 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif // NOMINMAX
+
+#include <algorithm>
+#include <cmath>
+#include <cassert>
+#include <cstring>
+#include <cstddef>
+#include <cstdio>
+#include "cubeb_resampler.h"
+#include "cubeb-speex-resampler.h"
+#include "cubeb_resampler_internal.h"
+#include "cubeb_utils.h"
+
+int
+to_speex_quality(cubeb_resampler_quality q)
+{
+  switch(q) {
+  case CUBEB_RESAMPLER_QUALITY_VOIP:
+    return SPEEX_RESAMPLER_QUALITY_VOIP;
+  case CUBEB_RESAMPLER_QUALITY_DEFAULT:
+    return SPEEX_RESAMPLER_QUALITY_DEFAULT;
+  case CUBEB_RESAMPLER_QUALITY_DESKTOP:
+    return SPEEX_RESAMPLER_QUALITY_DESKTOP;
+  default:
+    assert(false);
+    return 0XFFFFFFFF;
+  }
+}
+
+uint32_t min_buffered_audio_frame(uint32_t sample_rate)
+{
+  return sample_rate / 20;
+}
+
+template<typename T>
+passthrough_resampler<T>::passthrough_resampler(cubeb_stream * s,
+                                                cubeb_data_callback cb,
+                                                void * ptr,
+                                                uint32_t input_channels,
+                                                uint32_t sample_rate)
+  : processor(input_channels)
+  , stream(s)
+  , data_callback(cb)
+  , user_ptr(ptr)
+  , sample_rate(sample_rate)
+{
+}
+
+template<typename T>
+long passthrough_resampler<T>::fill(void * input_buffer, long * input_frames_count,
+                                    void * output_buffer, long output_frames)
+{
+  if (input_buffer) {
+    assert(input_frames_count);
+  }
+  assert((input_buffer && output_buffer &&
+         *input_frames_count + static_cast<int>(samples_to_frames(internal_input_buffer.length())) >= output_frames) ||
+         (output_buffer && !input_buffer && (!input_frames_count || *input_frames_count == 0)) ||
+         (input_buffer && !output_buffer && output_frames == 0));
+
+  // When we have no pending input data and exactly as much input
+  // as output data, we don't need to copy it into the internal buffer
+  // and can directly forward it to the callback.
+  void * in_buf = input_buffer;
+  unsigned long pop_input_count = 0u;
+  if (input_buffer && !output_buffer) {
+      output_frames = *input_frames_count;
+  } else if(input_buffer) {
+    if (internal_input_buffer.length() != 0) {
+      // In this case we have pending input data left and have
+      // to first append the input so we can pass it as one pointer
+      // to the callback
+      internal_input_buffer.push(static_cast<T*>(input_buffer),
+                                 frames_to_samples(*input_frames_count));
+      in_buf = internal_input_buffer.data();
+      pop_input_count = frames_to_samples(output_frames);
+    } else if(*input_frames_count > output_frames) {
+      // In this case we have more input that we need output and
+      // fill the overflowing input into internal_input_buffer
+      // Since we have no other pending data, we can nonetheless
+      // pass the current input data directly to the callback
+      assert(pop_input_count == 0);
+      unsigned long samples_off = frames_to_samples(output_frames);
+      internal_input_buffer.push(static_cast<T*>(input_buffer) + samples_off,
+                                 frames_to_samples(*input_frames_count - output_frames));
+    }
+  }
+
+  long rv = data_callback(stream, user_ptr, in_buf, output_buffer, output_frames);
+
+  if (input_buffer) {
+    if (pop_input_count) {
+      internal_input_buffer.pop(nullptr, pop_input_count);
+    }
+
+    *input_frames_count = output_frames;
+    drop_audio_if_needed();
+  }
+
+  return rv;
+}
+
+template<typename T, typename InputProcessor, typename OutputProcessor>
+cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
+  ::cubeb_resampler_speex(InputProcessor * input_processor,
+                          OutputProcessor * output_processor,
+                          cubeb_stream * s,
+                          cubeb_data_callback cb,
+                          void * ptr)
+  : input_processor(input_processor)
+  , output_processor(output_processor)
+  , stream(s)
+  , data_callback(cb)
+  , user_ptr(ptr)
+{
+  if (input_processor && output_processor) {
+    // Add some delay on the processor that has the lowest delay so that the
+    // streams are synchronized.
+    uint32_t in_latency = input_processor->latency();
+    uint32_t out_latency = output_processor->latency();
+    if (in_latency > out_latency) {
+      uint32_t latency_diff = in_latency - out_latency;
+      output_processor->add_latency(latency_diff);
+    } else if (in_latency < out_latency) {
+      uint32_t latency_diff = out_latency - in_latency;
+      input_processor->add_latency(latency_diff);
+    }
+    fill_internal = &cubeb_resampler_speex::fill_internal_duplex;
+  }  else if (input_processor) {
+    fill_internal = &cubeb_resampler_speex::fill_internal_input;
+  }  else if (output_processor) {
+    fill_internal = &cubeb_resampler_speex::fill_internal_output;
+  }
+}
+
+template<typename T, typename InputProcessor, typename OutputProcessor>
+cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
+  ::~cubeb_resampler_speex()
+{ }
+
+template<typename T, typename InputProcessor, typename OutputProcessor>
+long
+cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
+::fill(void * input_buffer, long * input_frames_count,
+       void * output_buffer, long output_frames_needed)
+{
+  /* Input and output buffers, typed */
+  T * in_buffer = reinterpret_cast<T*>(input_buffer);
+  T * out_buffer = reinterpret_cast<T*>(output_buffer);
+  return (this->*fill_internal)(in_buffer, input_frames_count,
+                                out_buffer, output_frames_needed);
+}
+
+template<typename T, typename InputProcessor, typename OutputProcessor>
+long
+cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
+::fill_internal_output(T * input_buffer, long * input_frames_count,
+                       T * output_buffer, long output_frames_needed)
+{
+  assert(!input_buffer && (!input_frames_count || *input_frames_count == 0) &&
+         output_buffer && output_frames_needed);
+
+  if (!draining) {
+    long got = 0;
+    T * out_unprocessed = nullptr;
+    long output_frames_before_processing = 0;
+
+    /* fill directly the input buffer of the output processor to save a copy */
+    output_frames_before_processing =
+      output_processor->input_needed_for_output(output_frames_needed);
+
+    out_unprocessed =
+      output_processor->input_buffer(output_frames_before_processing);
+
+    got = data_callback(stream, user_ptr,
+                        nullptr, out_unprocessed,
+                        output_frames_before_processing);
+
+    if (got < output_frames_before_processing) {
+      draining = true;
+
+      if (got < 0) {
+        return got;
+      }
+    }
+
+    output_processor->written(got);
+  }
+
+  /* Process the output. If not enough frames have been returned from the
+  * callback, drain the processors. */
+  return output_processor->output(output_buffer, output_frames_needed);
+}
+
+template<typename T, typename InputProcessor, typename OutputProcessor>
+long
+cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
+::fill_internal_input(T * input_buffer, long * input_frames_count,
+                      T * output_buffer, long /*output_frames_needed*/)
+{
+  assert(input_buffer && input_frames_count && *input_frames_count &&
+         !output_buffer);
+
+  /* The input data, after eventual resampling. This is passed to the callback. */
+  T * resampled_input = nullptr;
+  uint32_t resampled_frame_count = input_processor->output_for_input(*input_frames_count);
+
+  /* process the input, and present exactly `output_frames_needed` in the
+  * callback. */
+  input_processor->input(input_buffer, *input_frames_count);
+
+  size_t frames_resampled = 0;
+  resampled_input = input_processor->output(resampled_frame_count, &frames_resampled);
+  *input_frames_count = frames_resampled;
+
+  long got = data_callback(stream, user_ptr,
+                           resampled_input, nullptr, resampled_frame_count);
+
+  /* Return the number of initial input frames or part of it.
+  * Since output_frames_needed == 0 in input scenario, the only
+  * available number outside resampler is the initial number of frames. */
+  return (*input_frames_count) * (got / resampled_frame_count);
+}
+
+template<typename T, typename InputProcessor, typename OutputProcessor>
+long
+cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
+::fill_internal_duplex(T * in_buffer, long * input_frames_count,
+                       T * out_buffer, long output_frames_needed)
+{
+  if (draining) {
+    // discard input and drain any signal remaining in the resampler.
+    return output_processor->output(out_buffer, output_frames_needed);
+  }
+
+  /* The input data, after eventual resampling. This is passed to the callback. */
+  T * resampled_input = nullptr;
+  /* The output buffer passed down in the callback, that might be resampled. */
+  T * out_unprocessed = nullptr;
+  long output_frames_before_processing = 0;
+  /* The number of frames returned from the callback. */
+  long got = 0;
+
+  /* We need to determine how much frames to present to the consumer.
+   * - If we have a two way stream, but we're only resampling input, we resample
+   * the input to the number of output frames.
+   * - If we have a two way stream, but we're only resampling the output, we
+   * resize the input buffer of the output resampler to the number of input
+   * frames, and we resample it afterwards.
+   * - If we resample both ways, we resample the input to the number of frames
+   * we would need to pass down to the consumer (before resampling the output),
+   * get the output data, and resample it to the number of frames needed by the
+   * caller. */
+
+  output_frames_before_processing =
+    output_processor->input_needed_for_output(output_frames_needed);
+   /* fill directly the input buffer of the output processor to save a copy */
+  out_unprocessed =
+    output_processor->input_buffer(output_frames_before_processing);
+
+  if (in_buffer) {
+    /* process the input, and present exactly `output_frames_needed` in the
+    * callback. */
+    input_processor->input(in_buffer, *input_frames_count);
+
+    size_t frames_resampled = 0;
+    resampled_input =
+      input_processor->output(output_frames_before_processing, &frames_resampled);
+    *input_frames_count = frames_resampled;
+  } else {
+    resampled_input = nullptr;
+  }
+
+  got = data_callback(stream, user_ptr,
+                      resampled_input, out_unprocessed,
+                      output_frames_before_processing);
+
+  if (got < output_frames_before_processing) {
+    draining = true;
+
+    if (got < 0) {
+      return got;
+    }
+  }
+
+  output_processor->written(got);
+
+  input_processor->drop_audio_if_needed();
+
+  /* Process the output. If not enough frames have been returned from the
+   * callback, drain the processors. */
+  got = output_processor->output(out_buffer, output_frames_needed);
+
+  output_processor->drop_audio_if_needed();
+
+  return got;
+}
+
+/* Resampler C API */
+
+cubeb_resampler *
+cubeb_resampler_create(cubeb_stream * stream,
+                       cubeb_stream_params * input_params,
+                       cubeb_stream_params * output_params,
+                       unsigned int target_rate,
+                       cubeb_data_callback callback,
+                       void * user_ptr,
+                       cubeb_resampler_quality quality)
+{
+  cubeb_sample_format format;
+
+  assert(input_params || output_params);
+
+  if (input_params) {
+    format = input_params->format;
+  } else {
+    format = output_params->format;
+  }
+
+  switch(format) {
+    case CUBEB_SAMPLE_S16NE:
+      return cubeb_resampler_create_internal<short>(stream,
+                                                    input_params,
+                                                    output_params,
+                                                    target_rate,
+                                                    callback,
+                                                    user_ptr,
+                                                    quality);
+    case CUBEB_SAMPLE_FLOAT32NE:
+      return cubeb_resampler_create_internal<float>(stream,
+                                                    input_params,
+                                                    output_params,
+                                                    target_rate,
+                                                    callback,
+                                                    user_ptr,
+                                                    quality);
+    default:
+      assert(false);
+      return nullptr;
+  }
+}
+
+long
+cubeb_resampler_fill(cubeb_resampler * resampler,
+                     void * input_buffer,
+                     long * input_frames_count,
+                     void * output_buffer,
+                     long output_frames_needed)
+{
+  return resampler->fill(input_buffer, input_frames_count,
+                         output_buffer, output_frames_needed);
+}
+
+void
+cubeb_resampler_destroy(cubeb_resampler * resampler)
+{
+  delete resampler;
+}
+
+long
+cubeb_resampler_latency(cubeb_resampler * resampler)
+{
+  return resampler->latency();
+}
diff --git a/dep/cubeb/src/cubeb_resampler.h b/dep/cubeb/src/cubeb_resampler.h
new file mode 100644
index 000000000..f6b551373
--- /dev/null
+++ b/dep/cubeb/src/cubeb_resampler.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright © 2014 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+#ifndef CUBEB_RESAMPLER_H
+#define CUBEB_RESAMPLER_H
+
+#include "cubeb/cubeb.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef struct cubeb_resampler cubeb_resampler;
+
+typedef enum {
+  CUBEB_RESAMPLER_QUALITY_VOIP,
+  CUBEB_RESAMPLER_QUALITY_DEFAULT,
+  CUBEB_RESAMPLER_QUALITY_DESKTOP
+} cubeb_resampler_quality;
+
+/**
+ * Create a resampler to adapt the requested sample rate into something that
+ * is accepted by the audio backend.
+ * @param stream A cubeb_stream instance supplied to the data callback.
+ * @param input_params Used to calculate bytes per frame and buffer size for
+ * resampling of the input side of the stream. NULL if input should not be
+ * resampled.
+ * @param output_params Used to calculate bytes per frame and buffer size for
+ * resampling of the output side of the stream. NULL if output should not be
+ * resampled.
+ * @param target_rate The sampling rate after resampling for the input side of
+ * the stream, and/or the sampling rate prior to resampling of the output side
+ * of the stream.
+ * @param callback A callback to request data for resampling.
+ * @param user_ptr User data supplied to the data callback.
+ * @param quality Quality of the resampler.
+ * @retval A non-null pointer if success.
+ */
+cubeb_resampler * cubeb_resampler_create(cubeb_stream * stream,
+                                         cubeb_stream_params * input_params,
+                                         cubeb_stream_params * output_params,
+                                         unsigned int target_rate,
+                                         cubeb_data_callback callback,
+                                         void * user_ptr,
+                                         cubeb_resampler_quality quality);
+
+/**
+ * Fill the buffer with frames acquired using the data callback. Resampling will
+ * happen if necessary.
+ * @param resampler A cubeb_resampler instance.
+ * @param input_buffer A buffer of input samples
+ * @param input_frame_count The size of the buffer. Returns the number of frames
+ * consumed.
+ * @param output_buffer The buffer to be filled.
+ * @param output_frames_needed Number of frames that should be produced.
+ * @retval Number of frames that are actually produced.
+ * @retval CUBEB_ERROR on error.
+ */
+long cubeb_resampler_fill(cubeb_resampler * resampler,
+                          void * input_buffer,
+                          long * input_frame_count,
+                          void * output_buffer,
+                          long output_frames_needed);
+
+/**
+ * Destroy a cubeb_resampler.
+ * @param resampler A cubeb_resampler instance.
+ */
+void cubeb_resampler_destroy(cubeb_resampler * resampler);
+
+/**
+ * Returns the latency, in frames, of the resampler.
+ * @param resampler A cubeb resampler instance.
+ * @retval The latency, in frames, induced by the resampler.
+ */
+long cubeb_resampler_latency(cubeb_resampler * resampler);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* CUBEB_RESAMPLER_H */
diff --git a/dep/cubeb/src/cubeb_resampler_internal.h b/dep/cubeb/src/cubeb_resampler_internal.h
new file mode 100644
index 000000000..fb69992ff
--- /dev/null
+++ b/dep/cubeb/src/cubeb_resampler_internal.h
@@ -0,0 +1,602 @@
+/*
+ * Copyright © 2016 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+
+#if !defined(CUBEB_RESAMPLER_INTERNAL)
+#define CUBEB_RESAMPLER_INTERNAL
+
+#include <cmath>
+#include <cassert>
+#include <algorithm>
+#include <memory>
+#ifdef CUBEB_GECKO_BUILD
+#include "mozilla/UniquePtr.h"
+// In libc++, symbols such as std::unique_ptr may be defined in std::__1.
+// The _LIBCPP_BEGIN_NAMESPACE_STD and _LIBCPP_END_NAMESPACE_STD macros
+// will expand to the correct namespace.
+#ifdef _LIBCPP_BEGIN_NAMESPACE_STD
+#define MOZ_BEGIN_STD_NAMESPACE _LIBCPP_BEGIN_NAMESPACE_STD
+#define MOZ_END_STD_NAMESPACE _LIBCPP_END_NAMESPACE_STD
+#else
+#define MOZ_BEGIN_STD_NAMESPACE namespace std {
+#define MOZ_END_STD_NAMESPACE }
+#endif
+MOZ_BEGIN_STD_NAMESPACE
+  using mozilla::DefaultDelete;
+  using mozilla::UniquePtr;
+  #define default_delete DefaultDelete
+  #define unique_ptr UniquePtr
+MOZ_END_STD_NAMESPACE
+#endif
+#include "cubeb/cubeb.h"
+#include "cubeb_utils.h"
+#include "cubeb-speex-resampler.h"
+#include "cubeb_resampler.h"
+#include <stdio.h>
+
+/* This header file contains the internal C++ API of the resamplers, for testing. */
+
+// When dropping audio input frames to prevent building
+// an input delay, this function returns the number of frames
+// to keep in the buffer.
+// @parameter sample_rate The sample rate of the stream.
+// @return A number of frames to keep.
+uint32_t min_buffered_audio_frame(uint32_t sample_rate);
+
+int to_speex_quality(cubeb_resampler_quality q);
+
+struct cubeb_resampler {
+  virtual long fill(void * input_buffer, long * input_frames_count,
+                    void * output_buffer, long frames_needed) = 0;
+  virtual long latency() = 0;
+  virtual ~cubeb_resampler() {}
+};
+
+/** Base class for processors. This is just used to share methods for now. */
+class processor {
+public:
+  explicit processor(uint32_t channels)
+    : channels(channels)
+  {}
+protected:
+  size_t frames_to_samples(size_t frames) const
+  {
+    return frames * channels;
+  }
+  size_t samples_to_frames(size_t samples) const
+  {
+    assert(!(samples % channels));
+    return samples / channels;
+  }
+  /** The number of channel of the audio buffers to be resampled. */
+  const uint32_t channels;
+};
+
+template<typename T>
+class passthrough_resampler : public cubeb_resampler
+                            , public processor {
+public:
+  passthrough_resampler(cubeb_stream * s,
+                        cubeb_data_callback cb,
+                        void * ptr,
+                        uint32_t input_channels,
+                        uint32_t sample_rate);
+
+  virtual long fill(void * input_buffer, long * input_frames_count,
+                    void * output_buffer, long output_frames);
+
+  virtual long latency()
+  {
+    return 0;
+  }
+
+  void drop_audio_if_needed()
+  {
+    uint32_t to_keep = min_buffered_audio_frame(sample_rate);
+    uint32_t available = samples_to_frames(internal_input_buffer.length());
+    if (available > to_keep) {
+      internal_input_buffer.pop(nullptr, frames_to_samples(available - to_keep));
+    }
+  }
+
+private:
+  cubeb_stream * const stream;
+  const cubeb_data_callback data_callback;
+  void * const user_ptr;
+  /* This allows to buffer some input to account for the fact that we buffer
+   * some inputs. */
+  auto_array<T> internal_input_buffer;
+  uint32_t sample_rate;
+};
+
+/** Bidirectional resampler, can resample an input and an output stream, or just
+ * an input stream or output stream. In this case a delay is inserted in the
+ * opposite direction to keep the streams synchronized. */
+template<typename T, typename InputProcessing, typename OutputProcessing>
+class cubeb_resampler_speex : public cubeb_resampler {
+public:
+  cubeb_resampler_speex(InputProcessing * input_processor,
+                        OutputProcessing * output_processor,
+                        cubeb_stream * s,
+                        cubeb_data_callback cb,
+                        void * ptr);
+
+  virtual ~cubeb_resampler_speex();
+
+  virtual long fill(void * input_buffer, long * input_frames_count,
+                    void * output_buffer, long output_frames_needed);
+
+  virtual long latency()
+  {
+    if (input_processor && output_processor) {
+      assert(input_processor->latency() == output_processor->latency());
+      return input_processor->latency();
+    } else if (input_processor) {
+      return input_processor->latency();
+    } else {
+      return output_processor->latency();
+    }
+  }
+
+private:
+  typedef long(cubeb_resampler_speex::*processing_callback)(T * input_buffer, long * input_frames_count, T * output_buffer, long output_frames_needed);
+
+  long fill_internal_duplex(T * input_buffer, long * input_frames_count,
+                            T * output_buffer, long output_frames_needed);
+  long fill_internal_input(T * input_buffer, long * input_frames_count,
+                           T * output_buffer, long output_frames_needed);
+  long fill_internal_output(T * input_buffer, long * input_frames_count,
+                            T * output_buffer, long output_frames_needed);
+
+  std::unique_ptr<InputProcessing> input_processor;
+  std::unique_ptr<OutputProcessing> output_processor;
+  processing_callback fill_internal;
+  cubeb_stream * const stream;
+  const cubeb_data_callback data_callback;
+  void * const user_ptr;
+  bool draining = false;
+};
+
+/** Handles one way of a (possibly) duplex resampler, working on interleaved
+ * audio buffers of type T. This class is designed so that the number of frames
+ * coming out of the resampler can be precisely controled. It manages its own
+ * input buffer, and can use the caller's output buffer, or allocate its own. */
+template<typename T>
+class cubeb_resampler_speex_one_way : public processor {
+public:
+  /** The sample type of this resampler, either 16-bit integers or 32-bit
+   * floats. */
+  typedef T sample_type;
+  /** Construct a resampler resampling from #source_rate to #target_rate, that
+   * can be arbitrary, strictly positive number.
+   * @parameter channels The number of channels this resampler will resample.
+   * @parameter source_rate The sample-rate of the audio input.
+   * @parameter target_rate The sample-rate of the audio output.
+   * @parameter quality A number between 0 (fast, low quality) and 10 (slow,
+   * high quality). */
+  cubeb_resampler_speex_one_way(uint32_t channels,
+                                uint32_t source_rate,
+                                uint32_t target_rate,
+                                int quality)
+  : processor(channels)
+  , resampling_ratio(static_cast<float>(source_rate) / target_rate)
+  , source_rate(source_rate)
+  , additional_latency(0)
+  , leftover_samples(0)
+  {
+    int r;
+    speex_resampler = speex_resampler_init(channels, source_rate,
+                                           target_rate, quality, &r);
+    assert(r == RESAMPLER_ERR_SUCCESS && "resampler allocation failure");
+  }
+
+  /** Destructor, deallocate the resampler */
+  virtual ~cubeb_resampler_speex_one_way()
+  {
+    speex_resampler_destroy(speex_resampler);
+  }
+
+  /** Sometimes, it is necessary to add latency on one way of a two-way
+   * resampler so that the stream are synchronized. This must be called only on
+   * a fresh resampler, otherwise, silent samples will be inserted in the
+   * stream.
+   * @param frames the number of frames of latency to add. */
+  void add_latency(size_t frames)
+  {
+    additional_latency += frames;
+    resampling_in_buffer.push_silence(frames_to_samples(frames));
+  }
+
+  /* Fill the resampler with `input_frame_count` frames. */
+  void input(T * input_buffer, size_t input_frame_count)
+  {
+    resampling_in_buffer.push(input_buffer,
+                              frames_to_samples(input_frame_count));
+  }
+
+  /** Outputs exactly `output_frame_count` into `output_buffer`.
+    * `output_buffer` has to be at least `output_frame_count` long. */
+  size_t output(T * output_buffer, size_t output_frame_count)
+  {
+    uint32_t in_len = samples_to_frames(resampling_in_buffer.length());
+    uint32_t out_len = output_frame_count;
+
+    speex_resample(resampling_in_buffer.data(), &in_len,
+                   output_buffer, &out_len);
+
+    /* This shifts back any unresampled samples to the beginning of the input
+       buffer. */
+    resampling_in_buffer.pop(nullptr, frames_to_samples(in_len));
+
+    return out_len;
+  }
+
+  size_t output_for_input(uint32_t input_frames)
+  {
+    return (size_t)floorf((input_frames + samples_to_frames(resampling_in_buffer.length()))
+                         / resampling_ratio);
+  }
+
+  /** Returns a buffer containing exactly `output_frame_count` resampled frames.
+    * The consumer should not hold onto the pointer. */
+  T * output(size_t output_frame_count, size_t * input_frames_used)
+  {
+    if (resampling_out_buffer.capacity() < frames_to_samples(output_frame_count)) {
+      resampling_out_buffer.reserve(frames_to_samples(output_frame_count));
+    }
+
+    uint32_t in_len = samples_to_frames(resampling_in_buffer.length());
+    uint32_t out_len = output_frame_count;
+
+    speex_resample(resampling_in_buffer.data(), &in_len,
+                   resampling_out_buffer.data(), &out_len);
+
+    assert(out_len == output_frame_count);
+
+    /* This shifts back any unresampled samples to the beginning of the input
+       buffer. */
+    resampling_in_buffer.pop(nullptr, frames_to_samples(in_len));
+    *input_frames_used = in_len;
+
+    return resampling_out_buffer.data();
+  }
+
+  /** Get the latency of the resampler, in output frames. */
+  uint32_t latency() const
+  {
+    /* The documentation of the resampler talks about "samples" here, but it
+     * only consider a single channel here so it's the same number of frames. */
+    int latency = 0;
+
+    latency =
+      speex_resampler_get_output_latency(speex_resampler) + additional_latency;
+
+    assert(latency >= 0);
+
+    return latency;
+  }
+
+  /** Returns the number of frames to pass in the input of the resampler to have
+   * exactly `output_frame_count` resampled frames. This can return a number
+   * slightly bigger than what is strictly necessary, but it guaranteed that the
+   * number of output frames will be exactly equal. */
+  uint32_t input_needed_for_output(uint32_t output_frame_count) const
+  {
+    int32_t unresampled_frames_left = samples_to_frames(resampling_in_buffer.length());
+    int32_t resampled_frames_left = samples_to_frames(resampling_out_buffer.length());
+    float input_frames_needed =
+      (output_frame_count - unresampled_frames_left) * resampling_ratio
+        - resampled_frames_left;
+    if (input_frames_needed < 0) {
+      return 0;
+    }
+    return (uint32_t)ceilf(input_frames_needed);
+  }
+
+  /** Returns a pointer to the input buffer, that contains empty space for at
+   * least `frame_count` elements. This is useful so that consumer can directly
+   * write into the input buffer of the resampler. The pointer returned is
+   * adjusted so that leftover data are not overwritten.
+   */
+  T * input_buffer(size_t frame_count)
+  {
+    leftover_samples = resampling_in_buffer.length();
+    resampling_in_buffer.reserve(leftover_samples +
+                                 frames_to_samples(frame_count));
+    return resampling_in_buffer.data() + leftover_samples;
+  }
+
+  /** This method works with `input_buffer`, and allows to inform the processor
+      how much frames have been written in the provided buffer. */
+  void written(size_t written_frames)
+  {
+    resampling_in_buffer.set_length(leftover_samples +
+                                    frames_to_samples(written_frames));
+  }
+
+  void drop_audio_if_needed()
+  {
+    // Keep at most 100ms buffered.
+    uint32_t available = samples_to_frames(resampling_in_buffer.length());
+    uint32_t to_keep = min_buffered_audio_frame(source_rate);
+    if (available > to_keep) {
+      resampling_in_buffer.pop(nullptr, frames_to_samples(available - to_keep));
+    }
+  }
+private:
+  /** Wrapper for the speex resampling functions to have a typed
+    * interface. */
+  void speex_resample(float * input_buffer, uint32_t * input_frame_count,
+                      float * output_buffer, uint32_t * output_frame_count)
+  {
+#ifndef NDEBUG
+    int rv;
+    rv =
+#endif
+      speex_resampler_process_interleaved_float(speex_resampler,
+                                                input_buffer,
+                                                input_frame_count,
+                                                output_buffer,
+                                                output_frame_count);
+    assert(rv == RESAMPLER_ERR_SUCCESS);
+  }
+
+  void speex_resample(short * input_buffer, uint32_t * input_frame_count,
+                      short * output_buffer, uint32_t * output_frame_count)
+  {
+#ifndef NDEBUG
+    int rv;
+    rv =
+#endif
+      speex_resampler_process_interleaved_int(speex_resampler,
+                                              input_buffer,
+                                              input_frame_count,
+                                              output_buffer,
+                                              output_frame_count);
+    assert(rv == RESAMPLER_ERR_SUCCESS);
+  }
+  /** The state for the speex resampler used internaly. */
+  SpeexResamplerState * speex_resampler;
+  /** Source rate / target rate. */
+  const float resampling_ratio;
+  const uint32_t source_rate;
+  /** Storage for the input frames, to be resampled. Also contains
+   * any unresampled frames after resampling. */
+  auto_array<T> resampling_in_buffer;
+  /* Storage for the resampled frames, to be passed back to the caller. */
+  auto_array<T> resampling_out_buffer;
+  /** Additional latency inserted into the pipeline for synchronisation. */
+  uint32_t additional_latency;
+  /** When `input_buffer` is called, this allows tracking the number of samples
+      that were in the buffer. */
+  uint32_t leftover_samples;
+};
+
+/** This class allows delaying an audio stream by `frames` frames. */
+template<typename T>
+class delay_line : public processor {
+public:
+  /** Constructor
+   * @parameter frames the number of frames of delay.
+   * @parameter channels the number of channels of this delay line.
+   * @parameter sample_rate sample-rate of the audio going through this delay line */
+  delay_line(uint32_t frames, uint32_t channels, uint32_t sample_rate)
+    : processor(channels)
+    , length(frames)
+    , leftover_samples(0)
+    , sample_rate(sample_rate)
+  {
+    /* Fill the delay line with some silent frames to add latency. */
+    delay_input_buffer.push_silence(frames * channels);
+  }
+  /* Add some latency to the delay line.
+   * @param frames the number of frames of latency to add. */
+  void add_latency(size_t frames)
+  {
+    length += frames;
+    delay_input_buffer.push_silence(frames_to_samples(frames));
+  }
+  /** Push some frames into the delay line.
+   * @parameter buffer the frames to push.
+   * @parameter frame_count the number of frames in #buffer. */
+  void input(T * buffer, uint32_t frame_count)
+  {
+    delay_input_buffer.push(buffer, frames_to_samples(frame_count));
+  }
+  /** Pop some frames from the internal buffer, into a internal output buffer.
+   * @parameter frames_needed the number of frames to be returned.
+   * @return a buffer containing the delayed frames. The consumer should not
+   * hold onto the pointer. */
+  T * output(uint32_t frames_needed, size_t * input_frames_used)
+  {
+    if (delay_output_buffer.capacity() < frames_to_samples(frames_needed)) {
+      delay_output_buffer.reserve(frames_to_samples(frames_needed));
+    }
+
+    delay_output_buffer.clear();
+    delay_output_buffer.push(delay_input_buffer.data(),
+                             frames_to_samples(frames_needed));
+    delay_input_buffer.pop(nullptr, frames_to_samples(frames_needed));
+    *input_frames_used = frames_needed;
+
+    return delay_output_buffer.data();
+  }
+  /** Get a pointer to the first writable location in the input buffer>
+   * @parameter frames_needed the number of frames the user needs to write into
+   * the buffer.
+   * @returns a pointer to a location in the input buffer where #frames_needed
+   * can be writen. */
+  T * input_buffer(uint32_t frames_needed)
+  {
+    leftover_samples = delay_input_buffer.length();
+    delay_input_buffer.reserve(leftover_samples + frames_to_samples(frames_needed));
+    return delay_input_buffer.data() + leftover_samples;
+  }
+  /** This method works with `input_buffer`, and allows to inform the processor
+      how much frames have been written in the provided buffer. */
+  void written(size_t frames_written)
+  {
+    delay_input_buffer.set_length(leftover_samples +
+                                  frames_to_samples(frames_written));
+  }
+  /** Drains the delay line, emptying the buffer.
+   * @parameter output_buffer the buffer in which the frames are written.
+   * @parameter frames_needed the maximum number of frames to write.
+   * @return the actual number of frames written. */
+  size_t output(T * output_buffer, uint32_t frames_needed)
+  {
+    uint32_t in_len = samples_to_frames(delay_input_buffer.length());
+    uint32_t out_len = frames_needed;
+
+    uint32_t to_pop = std::min(in_len, out_len);
+
+    delay_input_buffer.pop(output_buffer, frames_to_samples(to_pop));
+
+    return to_pop;
+  }
+  /** Returns the number of frames one needs to input into the delay line to get
+   * #frames_needed frames back.
+   * @parameter frames_needed the number of frames one want to write into the
+   * delay_line
+   * @returns the number of frames one will get. */
+  size_t input_needed_for_output(uint32_t frames_needed) const
+  {
+    return frames_needed;
+  }
+  /** Returns the number of frames produces for `input_frames` frames in input */
+  size_t output_for_input(uint32_t input_frames)
+  {
+    return input_frames;
+  }
+  /** The number of frames this delay line delays the stream by.
+   * @returns The number of frames of delay. */
+  size_t latency()
+  {
+    return length;
+  }
+
+  void drop_audio_if_needed()
+  {
+    size_t available = samples_to_frames(delay_input_buffer.length());
+    uint32_t to_keep = min_buffered_audio_frame(sample_rate);
+    if (available > to_keep) {
+      delay_input_buffer.pop(nullptr, frames_to_samples(available - to_keep));
+    }
+  }
+private:
+  /** The length, in frames, of this delay line */
+  uint32_t length;
+  /** When `input_buffer` is called, this allows tracking the number of samples
+      that where in the buffer. */
+  uint32_t leftover_samples;
+  /** The input buffer, where the delay is applied. */
+  auto_array<T> delay_input_buffer;
+  /** The output buffer. This is only ever used if using the ::output with a
+   * single argument. */
+  auto_array<T> delay_output_buffer;
+  uint32_t sample_rate;
+};
+
+/** This sits behind the C API and is more typed. */
+template<typename T>
+cubeb_resampler *
+cubeb_resampler_create_internal(cubeb_stream * stream,
+                                cubeb_stream_params * input_params,
+                                cubeb_stream_params * output_params,
+                                unsigned int target_rate,
+                                cubeb_data_callback callback,
+                                void * user_ptr,
+                                cubeb_resampler_quality quality)
+{
+  std::unique_ptr<cubeb_resampler_speex_one_way<T>> input_resampler = nullptr;
+  std::unique_ptr<cubeb_resampler_speex_one_way<T>> output_resampler = nullptr;
+  std::unique_ptr<delay_line<T>> input_delay = nullptr;
+  std::unique_ptr<delay_line<T>> output_delay = nullptr;
+
+  assert((input_params || output_params) &&
+         "need at least one valid parameter pointer.");
+
+  /* All the streams we have have a sample rate that matches the target
+     sample rate, use a no-op resampler, that simply forwards the buffers to the
+     callback. */
+  if (((input_params && input_params->rate == target_rate) &&
+      (output_params && output_params->rate == target_rate)) ||
+      (input_params && !output_params && (input_params->rate == target_rate)) ||
+      (output_params && !input_params && (output_params->rate == target_rate))) {
+    return new passthrough_resampler<T>(stream, callback,
+                                        user_ptr,
+                                        input_params ? input_params->channels : 0,
+                                        target_rate);
+  }
+
+  /* Determine if we need to resampler one or both directions, and create the
+     resamplers. */
+  if (output_params && (output_params->rate != target_rate)) {
+    output_resampler.reset(
+        new cubeb_resampler_speex_one_way<T>(output_params->channels,
+                                             target_rate,
+                                             output_params->rate,
+                                             to_speex_quality(quality)));
+    if (!output_resampler) {
+      return NULL;
+    }
+  }
+
+  if (input_params && (input_params->rate != target_rate)) {
+    input_resampler.reset(
+        new cubeb_resampler_speex_one_way<T>(input_params->channels,
+                                             input_params->rate,
+                                             target_rate,
+                                             to_speex_quality(quality)));
+    if (!input_resampler) {
+      return NULL;
+    }
+  }
+
+  /* If we resample only one direction but we have a duplex stream, insert a
+   * delay line with a length equal to the resampler latency of the
+   * other direction so that the streams are synchronized. */
+  if (input_resampler && !output_resampler && input_params && output_params) {
+    output_delay.reset(new delay_line<T>(input_resampler->latency(),
+                                         output_params->channels,
+                                         output_params->rate));
+    if (!output_delay) {
+      return NULL;
+    }
+  } else if (output_resampler && !input_resampler && input_params && output_params) {
+    input_delay.reset(new delay_line<T>(output_resampler->latency(),
+                                        input_params->channels,
+                                        output_params->rate));
+    if (!input_delay) {
+      return NULL;
+    }
+  }
+
+  if (input_resampler && output_resampler) {
+    return new cubeb_resampler_speex<T,
+                                     cubeb_resampler_speex_one_way<T>,
+                                     cubeb_resampler_speex_one_way<T>>
+                                       (input_resampler.release(),
+                                        output_resampler.release(),
+                                        stream, callback, user_ptr);
+  } else if (input_resampler) {
+    return new cubeb_resampler_speex<T,
+                                     cubeb_resampler_speex_one_way<T>,
+                                     delay_line<T>>
+                                      (input_resampler.release(),
+                                       output_delay.release(),
+                                       stream, callback, user_ptr);
+  } else {
+    return new cubeb_resampler_speex<T,
+                                     delay_line<T>,
+                                     cubeb_resampler_speex_one_way<T>>
+                                      (input_delay.release(),
+                                       output_resampler.release(),
+                                       stream, callback, user_ptr);
+  }
+}
+
+#endif /* CUBEB_RESAMPLER_INTERNAL */
diff --git a/dep/cubeb/src/cubeb_ring_array.h b/dep/cubeb/src/cubeb_ring_array.h
new file mode 100644
index 000000000..51b3b321a
--- /dev/null
+++ b/dep/cubeb/src/cubeb_ring_array.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright © 2016 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+
+#ifndef CUBEB_RING_ARRAY_H
+#define CUBEB_RING_ARRAY_H
+
+#include "cubeb_utils.h"
+
+/** Ring array of pointers is used to hold buffers. In case that
+    asynchronous producer/consumer callbacks do not arrive in a
+    repeated order the ring array stores the buffers and fetch
+    them in the correct order. */
+
+typedef struct {
+  AudioBuffer * buffer_array;   /**< Array that hold pointers of the allocated space for the buffers. */
+  unsigned int tail;            /**< Index of the last element (first to deliver). */
+  unsigned int count;           /**< Number of elements in the array. */
+  unsigned int capacity;        /**< Total length of the array. */
+} ring_array;
+
+static int
+single_audiobuffer_init(AudioBuffer * buffer,
+                        uint32_t bytesPerFrame,
+                        uint32_t channelsPerFrame,
+                        uint32_t frames)
+{
+  assert(buffer);
+  assert(bytesPerFrame > 0 && channelsPerFrame && frames > 0);
+
+  size_t size = bytesPerFrame * frames;
+  buffer->mData = operator new(size);
+  if (buffer->mData == NULL) {
+    return CUBEB_ERROR;
+  }
+  PodZero(static_cast<char*>(buffer->mData), size);
+
+  buffer->mNumberChannels = channelsPerFrame;
+  buffer->mDataByteSize = size;
+
+  return CUBEB_OK;
+}
+
+/** Initialize the ring array.
+    @param ra The ring_array pointer of allocated structure.
+    @retval 0 on success. */
+int
+ring_array_init(ring_array * ra,
+                uint32_t capacity,
+                uint32_t bytesPerFrame,
+                uint32_t channelsPerFrame,
+                uint32_t framesPerBuffer)
+{
+  assert(ra);
+  if (capacity == 0 || bytesPerFrame == 0 ||
+      channelsPerFrame == 0 || framesPerBuffer == 0) {
+    return CUBEB_ERROR_INVALID_PARAMETER;
+  }
+  ra->capacity = capacity;
+  ra->tail = 0;
+  ra->count = 0;
+
+  ra->buffer_array = new AudioBuffer[ra->capacity];
+  PodZero(ra->buffer_array, ra->capacity);
+  if (ra->buffer_array == NULL) {
+    return CUBEB_ERROR;
+  }
+
+  for (unsigned int i = 0; i < ra->capacity; ++i) {
+    if (single_audiobuffer_init(&ra->buffer_array[i],
+                                bytesPerFrame,
+                                channelsPerFrame,
+                                framesPerBuffer) != CUBEB_OK) {
+      return CUBEB_ERROR;
+    }
+  }
+
+  return CUBEB_OK;
+}
+
+/** Destroy the ring array.
+    @param ra The ring_array pointer.*/
+void
+ring_array_destroy(ring_array * ra)
+{
+  assert(ra);
+  if (ra->buffer_array == NULL){
+    return;
+  }
+  for (unsigned int i = 0; i < ra->capacity; ++i) {
+    if (ra->buffer_array[i].mData) {
+      operator delete(ra->buffer_array[i].mData);
+    }
+  }
+  delete [] ra->buffer_array;
+}
+
+/** Get the allocated buffer to be stored with fresh data.
+    @param ra The ring_array pointer.
+    @retval Pointer of the allocated space to be stored with fresh data or NULL if full. */
+AudioBuffer *
+ring_array_get_free_buffer(ring_array * ra)
+{
+  assert(ra && ra->buffer_array);
+  assert(ra->buffer_array[0].mData != NULL);
+  if (ra->count == ra->capacity) {
+    return NULL;
+  }
+
+  assert(ra->count == 0 || (ra->tail + ra->count) % ra->capacity != ra->tail);
+  AudioBuffer * ret = &ra->buffer_array[(ra->tail + ra->count) % ra->capacity];
+
+  ++ra->count;
+  assert(ra->count <= ra->capacity);
+
+  return ret;
+}
+
+/** Get the next available buffer with data.
+    @param ra The ring_array pointer.
+    @retval Pointer of the next in order data buffer or NULL if empty. */
+AudioBuffer *
+ring_array_get_data_buffer(ring_array * ra)
+{
+  assert(ra && ra->buffer_array);
+  assert(ra->buffer_array[0].mData != NULL);
+
+  if (ra->count == 0) {
+    return NULL;
+  }
+  AudioBuffer * ret = &ra->buffer_array[ra->tail];
+
+  ra->tail = (ra->tail + 1) % ra->capacity;
+  assert(ra->tail < ra->capacity);
+
+  assert(ra->count > 0);
+  --ra->count;
+
+  return ret;
+}
+
+/** When array is empty get the first allocated buffer in the array.
+    @param ra The ring_array pointer.
+    @retval If arrays is empty, pointer of the allocated space else NULL. */
+AudioBuffer *
+ring_array_get_dummy_buffer(ring_array * ra)
+{
+  assert(ra && ra->buffer_array);
+  assert(ra->capacity > 0);
+  if (ra->count > 0) {
+    return NULL;
+  }
+  return &ra->buffer_array[0];
+}
+
+#endif //CUBEB_RING_ARRAY_H
diff --git a/dep/cubeb/src/cubeb_ringbuffer.h b/dep/cubeb/src/cubeb_ringbuffer.h
new file mode 100644
index 000000000..b6696e886
--- /dev/null
+++ b/dep/cubeb/src/cubeb_ringbuffer.h
@@ -0,0 +1,495 @@
+/*
+ * Copyright © 2016 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+
+#ifndef CUBEB_RING_BUFFER_H
+#define CUBEB_RING_BUFFER_H
+
+#include "cubeb_utils.h"
+#include <algorithm>
+#include <atomic>
+#include <cstdint>
+#include <memory>
+#include <thread>
+
+/**
+ * Single producer single consumer lock-free and wait-free ring buffer.
+ *
+ * This data structure allows producing data from one thread, and consuming it on
+ * another thread, safely and without explicit synchronization. If used on two
+ * threads, this data structure uses atomics for thread safety. It is possible
+ * to disable the use of atomics at compile time and only use this data
+ * structure on one thread.
+ *
+ * The role for the producer and the consumer must be constant, i.e., the
+ * producer should always be on one thread and the consumer should always be on
+ * another thread.
+ *
+ * Some words about the inner workings of this class:
+ * - Capacity is fixed. Only one allocation is performed, in the constructor.
+ *   When reading and writing, the return value of the method allows checking if
+ *   the ring buffer is empty or full.
+ * - We always keep the read index at least one element ahead of the write
+ *   index, so we can distinguish between an empty and a full ring buffer: an
+ *   empty ring buffer is when the write index is at the same position as the
+ *   read index. A full buffer is when the write index is exactly one position
+ *   before the read index.
+ * - We synchronize updates to the read index after having read the data, and
+ *   the write index after having written the data. This means that the each
+ *   thread can only touch a portion of the buffer that is not touched by the
+ *   other thread.
+ * - Callers are expected to provide buffers. When writing to the queue,
+ *   elements are copied into the internal storage from the buffer passed in.
+ *   When reading from the queue, the user is expected to provide a buffer.
+ *   Because this is a ring buffer, data might not be contiguous in memory,
+ *   providing an external buffer to copy into is an easy way to have linear
+ *   data for further processing.
+ */
+template <typename T>
+class ring_buffer_base
+{
+public:
+  /**
+   * Constructor for a ring buffer.
+   *
+   * This performs an allocation, but is the only allocation that will happen
+   * for the life time of a `ring_buffer_base`.
+   *
+   * @param capacity The maximum number of element this ring buffer will hold.
+   */
+  ring_buffer_base(int capacity)
+    /* One more element to distinguish from empty and full buffer. */
+    : capacity_(capacity + 1)
+  {
+    assert(storage_capacity() <
+           std::numeric_limits<int>::max() / 2 &&
+           "buffer too large for the type of index used.");
+    assert(capacity_ > 0);
+
+    data_.reset(new T[storage_capacity()]);
+    /* If this queue is using atomics, initializing those members as the last
+     * action in the constructor acts as a full barrier, and allow capacity() to
+     * be thread-safe. */
+    write_index_ = 0;
+    read_index_ = 0;
+  }
+  /**
+   * Push `count` zero or default constructed elements in the array.
+   *
+   * Only safely called on the producer thread.
+   *
+   * @param count The number of elements to enqueue.
+   * @return The number of element enqueued.
+   */
+  int enqueue_default(int count)
+  {
+    return enqueue(nullptr, count);
+  }
+  /**
+   * @brief Put an element in the queue
+   *
+   * Only safely called on the producer thread.
+   *
+   * @param element The element to put in the queue.
+   *
+   * @return 1 if the element was inserted, 0 otherwise.
+   */
+  int enqueue(T& element)
+  {
+    return enqueue(&element, 1);
+  }
+  /**
+   * Push `count` elements in the ring buffer.
+   *
+   * Only safely called on the producer thread.
+   *
+   * @param elements a pointer to a buffer containing at least `count` elements.
+   * If `elements` is nullptr, zero or default constructed elements are enqueued.
+   * @param count The number of elements to read from `elements`
+   * @return The number of elements successfully coped from `elements` and inserted
+   * into the ring buffer.
+   */
+  int enqueue(T * elements, int count)
+  {
+#ifndef NDEBUG
+    assert_correct_thread(producer_id);
+#endif
+
+    int rd_idx = read_index_.load(std::memory_order::memory_order_relaxed);
+    int wr_idx = write_index_.load(std::memory_order::memory_order_relaxed);
+
+    if (full_internal(rd_idx, wr_idx)) {
+      return 0;
+    }
+
+    int to_write =
+      std::min(available_write_internal(rd_idx, wr_idx), count);
+
+    /* First part, from the write index to the end of the array. */
+    int first_part = std::min(storage_capacity() - wr_idx,
+                                          to_write);
+    /* Second part, from the beginning of the array */
+    int second_part = to_write - first_part;
+
+    if (elements) {
+      Copy(data_.get() + wr_idx, elements, first_part);
+      Copy(data_.get(), elements + first_part, second_part);
+    } else {
+      ConstructDefault(data_.get() + wr_idx, first_part);
+      ConstructDefault(data_.get(), second_part);
+    }
+
+    write_index_.store(increment_index(wr_idx, to_write), std::memory_order::memory_order_release);
+
+    return to_write;
+  }
+  /**
+   * Retrieve at most `count` elements from the ring buffer, and copy them to
+   * `elements`, if non-null.
+   *
+   * Only safely called on the consumer side.
+   *
+   * @param elements A pointer to a buffer with space for at least `count`
+   * elements. If `elements` is `nullptr`, `count` element will be discarded.
+   * @param count The maximum number of elements to dequeue.
+   * @return The number of elements written to `elements`.
+   */
+  int dequeue(T * elements, int count)
+  {
+#ifndef NDEBUG
+    assert_correct_thread(consumer_id);
+#endif
+
+    int wr_idx = write_index_.load(std::memory_order::memory_order_acquire);
+    int rd_idx = read_index_.load(std::memory_order::memory_order_relaxed);
+
+    if (empty_internal(rd_idx, wr_idx)) {
+      return 0;
+    }
+
+    int to_read =
+      std::min(available_read_internal(rd_idx, wr_idx), count);
+
+    int first_part = std::min(storage_capacity() - rd_idx, to_read);
+    int second_part = to_read - first_part;
+
+    if (elements) {
+      Copy(elements, data_.get() + rd_idx, first_part);
+      Copy(elements + first_part, data_.get(), second_part);
+    }
+
+    read_index_.store(increment_index(rd_idx, to_read), std::memory_order::memory_order_relaxed);
+
+    return to_read;
+  }
+  /**
+   * Get the number of available element for consuming.
+   *
+   * Only safely called on the consumer thread.
+   *
+   * @return The number of available elements for reading.
+   */
+  int available_read() const
+  {
+#ifndef NDEBUG
+    assert_correct_thread(consumer_id);
+#endif
+    return available_read_internal(read_index_.load(std::memory_order::memory_order_relaxed),
+                                   write_index_.load(std::memory_order::memory_order_relaxed));
+  }
+  /**
+   * Get the number of available elements for consuming.
+   *
+   * Only safely called on the producer thread.
+   *
+   * @return The number of empty slots in the buffer, available for writing.
+   */
+  int available_write() const
+  {
+#ifndef NDEBUG
+    assert_correct_thread(producer_id);
+#endif
+    return available_write_internal(read_index_.load(std::memory_order::memory_order_relaxed),
+                                    write_index_.load(std::memory_order::memory_order_relaxed));
+  }
+  /**
+   * Get the total capacity, for this ring buffer.
+   *
+   * Can be called safely on any thread.
+   *
+   * @return The maximum capacity of this ring buffer.
+   */
+  int capacity() const
+  {
+    return storage_capacity() - 1;
+  }
+  /**
+   * Reset the consumer and producer thread identifier, in case the thread are
+   * being changed. This has to be externally synchronized. This is no-op when
+   * asserts are disabled.
+   */
+  void reset_thread_ids()
+  {
+#ifndef NDEBUG
+    consumer_id = producer_id = std::thread::id();
+#endif
+  }
+private:
+  /** Return true if the ring buffer is empty.
+   *
+   * @param read_index the read index to consider
+   * @param write_index the write index to consider
+   * @return true if the ring buffer is empty, false otherwise.
+   **/
+  bool empty_internal(int read_index,
+                      int write_index) const
+  {
+    return write_index == read_index;
+  }
+  /** Return true if the ring buffer is full.
+   *
+   * This happens if the write index is exactly one element behind the read
+   * index.
+   *
+   * @param read_index the read index to consider
+   * @param write_index the write index to consider
+   * @return true if the ring buffer is full, false otherwise.
+   **/
+  bool full_internal(int read_index,
+                     int write_index) const
+  {
+    return (write_index + 1) % storage_capacity() == read_index;
+  }
+  /**
+   * Return the size of the storage. It is one more than the number of elements
+   * that can be stored in the buffer.
+   *
+   * @return the number of elements that can be stored in the buffer.
+   */
+  int storage_capacity() const
+  {
+    return capacity_;
+  }
+  /**
+   * Returns the number of elements available for reading.
+   *
+   * @return the number of available elements for reading.
+   */
+  int
+  available_read_internal(int read_index,
+                          int write_index) const
+  {
+    if (write_index >= read_index) {
+      return write_index - read_index;
+    } else {
+      return write_index + storage_capacity() - read_index;
+    }
+  }
+  /**
+   * Returns the number of empty elements, available for writing.
+   *
+   * @return the number of elements that can be written into the array.
+   */
+  int
+  available_write_internal(int read_index,
+                           int write_index) const
+  {
+    /* We substract one element here to always keep at least one sample
+     * free in the buffer, to distinguish between full and empty array. */
+    int rv = read_index - write_index - 1;
+    if (write_index >= read_index) {
+      rv += storage_capacity();
+    }
+    return rv;
+  }
+  /**
+   * Increments an index, wrapping it around the storage.
+   *
+   * @param index a reference to the index to increment.
+   * @param increment the number by which `index` is incremented.
+   * @return the new index.
+   */
+  int
+  increment_index(int index, int increment) const
+  {
+    assert(increment >= 0);
+    return (index + increment) % storage_capacity();
+  }
+  /**
+   * @brief This allows checking that enqueue (resp. dequeue) are always called
+   * by the right thread.
+   *
+   * @param id the id of the thread that has called the calling method first.
+   */
+#ifndef NDEBUG
+  static void assert_correct_thread(std::thread::id& id)
+  {
+    if (id == std::thread::id()) {
+      id = std::this_thread::get_id();
+      return;
+    }
+    assert(id == std::this_thread::get_id());
+  }
+#endif
+  /** Index at which the oldest element is at, in samples. */
+  std::atomic<int> read_index_;
+  /** Index at which to write new elements. `write_index` is always at
+   * least one element ahead of `read_index_`. */
+  std::atomic<int> write_index_;
+  /** Maximum number of elements that can be stored in the ring buffer. */
+  const int capacity_;
+  /** Data storage */
+  std::unique_ptr<T[]> data_;
+#ifndef NDEBUG
+  /** The id of the only thread that is allowed to read from the queue. */
+  mutable std::thread::id consumer_id;
+  /** The id of the only thread that is allowed to write from the queue. */
+  mutable std::thread::id producer_id;
+#endif
+};
+
+/**
+ * Adapter for `ring_buffer_base` that exposes an interface in frames.
+ */
+template <typename T>
+class audio_ring_buffer_base
+{
+public:
+  /**
+   * @brief Constructor.
+   *
+   * @param channel_count       Number of channels.
+   * @param capacity_in_frames  The capacity in frames.
+   */
+  audio_ring_buffer_base(int channel_count, int capacity_in_frames)
+    : channel_count(channel_count)
+    , ring_buffer(frames_to_samples(capacity_in_frames))
+  {
+    assert(channel_count > 0);
+  }
+  /**
+   * @brief Enqueue silence.
+   *
+   * Only safely called on the producer thread.
+   *
+   * @param frame_count The number of frames of silence to enqueue.
+   * @return  The number of frames of silence actually written to the queue.
+   */
+  int enqueue_default(int frame_count)
+  {
+    return samples_to_frames(ring_buffer.enqueue(nullptr, frames_to_samples(frame_count)));
+  }
+  /**
+   * @brief Enqueue `frames_count` frames of audio.
+   *
+   * Only safely called from the producer thread.
+   *
+   * @param [in] frames If non-null, the frames to enqueue.
+   *                    Otherwise, silent frames are enqueued.
+   * @param frame_count The number of frames to enqueue.
+   *
+   * @return The number of frames enqueued
+   */
+
+  int enqueue(T * frames, int frame_count)
+  {
+    return samples_to_frames(ring_buffer.enqueue(frames, frames_to_samples(frame_count)));
+  }
+
+  /**
+   * @brief Removes `frame_count` frames from the buffer, and
+   *        write them to `frames` if it is non-null.
+   *
+   * Only safely called on the consumer thread.
+   *
+   * @param frames      If non-null, the frames are copied to `frames`.
+   *                    Otherwise, they are dropped.
+   * @param frame_count The number of frames to remove.
+   *
+   * @return  The number of frames actually dequeud.
+   */
+  int dequeue(T * frames, int frame_count)
+  {
+    return samples_to_frames(ring_buffer.dequeue(frames, frames_to_samples(frame_count)));
+  }
+  /**
+   * Get the number of available frames of audio for consuming.
+   *
+   * Only safely called on the consumer thread.
+   *
+   * @return The number of available frames of audio for reading.
+   */
+  int available_read() const
+  {
+    return samples_to_frames(ring_buffer.available_read());
+  }
+  /**
+   * Get the number of available frames of audio for consuming.
+   *
+   * Only safely called on the producer thread.
+   *
+   * @return The number of empty slots in the buffer, available for writing.
+   */
+  int available_write() const
+  {
+    return samples_to_frames(ring_buffer.available_write());
+  }
+  /**
+   * Get the total capacity, for this ring buffer.
+   *
+   * Can be called safely on any thread.
+   *
+   * @return The maximum capacity of this ring buffer.
+   */
+  int capacity() const
+  {
+    return samples_to_frames(ring_buffer.capacity());
+  }
+private:
+  /**
+   * @brief Frames to samples conversion.
+   *
+   * @param frames The number of frames.
+   *
+   * @return  A number of samples.
+   */
+  int frames_to_samples(int frames) const
+  {
+    return frames * channel_count;
+  }
+  /**
+   * @brief Samples to frames conversion.
+   *
+   * @param samples The number of samples.
+   *
+   * @return  A number of frames.
+   */
+  int samples_to_frames(int samples) const
+  {
+    return samples / channel_count;
+  }
+  /** Number of channels of audio that will stream through this ring buffer. */
+  int channel_count;
+  /** The underlying ring buffer that is used to store the data. */
+  ring_buffer_base<T> ring_buffer;
+};
+
+/**
+ * Lock-free instantiation of the `ring_buffer_base` type. This is safe to use
+ * from two threads, one producer, one consumer (that never change role),
+ * without explicit synchronization.
+ */
+template<typename T>
+using lock_free_queue = ring_buffer_base<T>;
+/**
+ * Lock-free instantiation of the `audio_ring_buffer` type. This is safe to use
+ * from two threads, one producer, one consumer (that never change role),
+ * without explicit synchronization.
+ */
+template<typename T>
+using lock_free_audio_ring_buffer = audio_ring_buffer_base<T>;
+
+#endif // CUBEB_RING_BUFFER_H
diff --git a/dep/cubeb/src/cubeb_sndio.c b/dep/cubeb/src/cubeb_sndio.c
new file mode 100644
index 000000000..34b3513d5
--- /dev/null
+++ b/dep/cubeb/src/cubeb_sndio.c
@@ -0,0 +1,669 @@
+/*
+ * Copyright (c) 2011 Alexandre Ratchov <alex@caoua.org>
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+#include <inttypes.h>
+#include <math.h>
+#include <poll.h>
+#include <pthread.h>
+#include <sndio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <dlfcn.h>
+#include <assert.h>
+#include "cubeb/cubeb.h"
+#include "cubeb-internal.h"
+
+#if defined(CUBEB_SNDIO_DEBUG)
+#define DPR(...) fprintf(stderr, __VA_ARGS__);
+#else
+#define DPR(...) do {} while(0)
+#endif
+
+#ifdef DISABLE_LIBSNDIO_DLOPEN
+#define WRAP(x) x
+#else
+#define WRAP(x) cubeb_##x
+#define LIBSNDIO_API_VISIT(X)                   \
+  X(sio_close)                               \
+  X(sio_eof)                                 \
+  X(sio_getpar)                              \
+  X(sio_initpar)                             \
+  X(sio_nfds)                                \
+  X(sio_onmove)                              \
+  X(sio_open)                                \
+  X(sio_pollfd)                              \
+  X(sio_read)                                \
+  X(sio_revents)                             \
+  X(sio_setpar)                              \
+  X(sio_start)                               \
+  X(sio_stop)                                \
+  X(sio_write)                               \
+
+#define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x;
+LIBSNDIO_API_VISIT(MAKE_TYPEDEF);
+#undef MAKE_TYPEDEF
+#endif
+
+static struct cubeb_ops const sndio_ops;
+
+struct cubeb {
+  struct cubeb_ops const * ops;
+  void * libsndio;
+};
+
+struct cubeb_stream {
+  /* Note: Must match cubeb_stream layout in cubeb.c. */
+  cubeb * context;
+  void * arg;                     /* user arg to {data,state}_cb */
+  /**/
+  pthread_t th;                   /* to run real-time audio i/o */
+  pthread_mutex_t mtx;            /* protects hdl and pos */
+  struct sio_hdl *hdl;            /* link us to sndio */
+  int mode;			  /* bitmap of SIO_{PLAY,REC} */
+  int active;                     /* cubec_start() called */
+  int conv;                       /* need float->s16 conversion */
+  unsigned char *rbuf;            /* rec data consumed from here */
+  unsigned char *pbuf;            /* play data is prepared here */
+  unsigned int nfr;               /* number of frames in ibuf and obuf */
+  unsigned int rbpf;              /* rec bytes per frame */
+  unsigned int pbpf;              /* play bytes per frame */
+  unsigned int rchan;             /* number of rec channels */
+  unsigned int pchan;             /* number of play channels */
+  unsigned int nblks;		  /* number of blocks in the buffer */
+  uint64_t hwpos;                 /* frame number Joe hears right now */
+  uint64_t swpos;                 /* number of frames produced/consumed */
+  cubeb_data_callback data_cb;    /* cb to preapare data */
+  cubeb_state_callback state_cb;  /* cb to notify about state changes */
+  float volume;			  /* current volume */
+};
+
+static void
+s16_setvol(void *ptr, long nsamp, float volume)
+{
+  int16_t *dst = ptr;
+  int32_t mult = volume * 32768;
+  int32_t s;
+
+  while (nsamp-- > 0) {
+    s = *dst;
+    s = (s * mult) >> 15;
+    *(dst++) = s;
+  }
+}
+
+static void
+float_to_s16(void *ptr, long nsamp, float volume)
+{
+  int16_t *dst = ptr;
+  float *src = ptr;
+  float mult = volume * 32768;
+  int s;
+
+  while (nsamp-- > 0) {
+    s = lrintf(*(src++) * mult);
+    if (s < -32768)
+      s = -32768;
+    else if (s > 32767)
+      s = 32767;
+    *(dst++) = s;
+  }
+}
+
+static void
+s16_to_float(void *ptr, long nsamp)
+{
+  int16_t *src = ptr;
+  float *dst = ptr;
+
+  src += nsamp;
+  dst += nsamp;
+  while (nsamp-- > 0)
+    *(--dst) = (1. / 32768) * *(--src);
+}
+
+static const char *
+sndio_get_device()
+{
+#ifndef __OpenBSD__
+  /*
+   * On other platforms default to sndio devices,
+   * so cubebs other backends can be used instead.
+   */
+  const char *dev = getenv("AUDIODEVICE");
+  if (dev == NULL || *dev == '\0')
+	return "snd/0";
+  return dev;
+#else
+  return SIO_DEVANY;
+#endif
+}
+
+static void
+sndio_onmove(void *arg, int delta)
+{
+  cubeb_stream *s = (cubeb_stream *)arg;
+
+  s->hwpos += delta;
+}
+
+static void *
+sndio_mainloop(void *arg)
+{
+  struct pollfd *pfds;
+  cubeb_stream *s = arg;
+  int n, eof = 0, prime, nfds, events, revents, state = CUBEB_STATE_STARTED;
+  size_t pstart = 0, pend = 0, rstart = 0, rend = 0;
+  long nfr;
+
+  nfds = WRAP(sio_nfds)(s->hdl);
+  pfds = calloc(nfds, sizeof (struct pollfd));
+  if (pfds == NULL)
+	  return NULL;
+
+  DPR("sndio_mainloop()\n");
+  s->state_cb(s, s->arg, CUBEB_STATE_STARTED);
+  pthread_mutex_lock(&s->mtx);
+  if (!WRAP(sio_start)(s->hdl)) {
+    pthread_mutex_unlock(&s->mtx);
+    free(pfds);
+    return NULL;
+  }
+  DPR("sndio_mainloop(), started\n");
+
+  if (s->mode & SIO_PLAY) {
+    pstart = pend = s->nfr * s->pbpf;
+    prime = s->nblks;
+    if (s->mode & SIO_REC) {
+      memset(s->rbuf, 0, s->nfr * s->rbpf);
+      rstart = rend = s->nfr * s->rbpf;
+    }
+  } else {
+    prime = 0;
+    rstart = 0;
+    rend = s->nfr * s->rbpf;
+  }
+
+  for (;;) {
+    if (!s->active) {
+      DPR("sndio_mainloop() stopped\n");
+      state = CUBEB_STATE_STOPPED;
+      break;
+    }
+
+    /* do we have a complete block? */
+    if ((!(s->mode & SIO_PLAY) || pstart == pend) &&
+	(!(s->mode & SIO_REC) || rstart == rend)) {
+
+      if (eof) {
+        DPR("sndio_mainloop() drained\n");
+        state = CUBEB_STATE_DRAINED;
+        break;
+      }
+
+      if ((s->mode & SIO_REC) && s->conv)
+        s16_to_float(s->rbuf, s->nfr * s->rchan);
+
+      /* invoke call-back, it returns less that s->nfr if done */
+      pthread_mutex_unlock(&s->mtx);
+      nfr = s->data_cb(s, s->arg, s->rbuf, s->pbuf, s->nfr);
+      pthread_mutex_lock(&s->mtx);
+      if (nfr < 0) {
+        DPR("sndio_mainloop() cb err\n");
+        state = CUBEB_STATE_ERROR;
+        break;
+      }
+      s->swpos += nfr;
+
+      /* was this last call-back invocation (aka end-of-stream) ? */
+      if (nfr < s->nfr) {
+
+        if (!(s->mode & SIO_PLAY) || nfr == 0) {
+          state = CUBEB_STATE_DRAINED;
+          break;
+	}
+
+        /* need to write (aka drain) the partial play block we got */
+        pend = nfr * s->pbpf;
+        eof = 1;
+      }
+
+      if (prime > 0)
+        prime--;
+
+      if (s->mode & SIO_PLAY) {
+        if (s->conv)
+          float_to_s16(s->pbuf, nfr * s->pchan, s->volume);
+        else
+          s16_setvol(s->pbuf, nfr * s->pchan, s->volume);
+      }
+
+      if (s->mode & SIO_REC)
+        rstart = 0;
+      if (s->mode & SIO_PLAY)
+        pstart = 0;
+    }
+
+    events = 0;
+    if ((s->mode & SIO_REC) && rstart < rend && prime == 0)
+      events |= POLLIN;
+    if ((s->mode & SIO_PLAY) && pstart < pend)
+      events |= POLLOUT;
+    nfds = WRAP(sio_pollfd)(s->hdl, pfds, events);
+
+    if (nfds > 0) {
+      pthread_mutex_unlock(&s->mtx);
+      n = poll(pfds, nfds, -1);
+      pthread_mutex_lock(&s->mtx);
+      if (n < 0)
+        continue;
+    }
+
+    revents = WRAP(sio_revents)(s->hdl, pfds);
+
+    if (revents & POLLHUP) {
+      state = CUBEB_STATE_ERROR;
+      break;
+    }
+
+    if (revents & POLLOUT) {
+      n = WRAP(sio_write)(s->hdl, s->pbuf + pstart, pend - pstart);
+      if (n == 0 && WRAP(sio_eof)(s->hdl)) {
+        DPR("sndio_mainloop() werr\n");
+        state = CUBEB_STATE_ERROR;
+        break;
+      }
+      pstart += n;
+    }
+
+    if (revents & POLLIN) {
+      n = WRAP(sio_read)(s->hdl, s->rbuf + rstart, rend - rstart);
+      if (n == 0 && WRAP(sio_eof)(s->hdl)) {
+        DPR("sndio_mainloop() rerr\n");
+        state = CUBEB_STATE_ERROR;
+        break;
+      }
+      rstart += n;
+    }
+
+    /* skip rec block, if not recording (yet) */
+    if (prime > 0 && (s->mode & SIO_REC))
+      rstart = rend;
+  }
+  WRAP(sio_stop)(s->hdl);
+  s->hwpos = s->swpos;
+  pthread_mutex_unlock(&s->mtx);
+  s->state_cb(s, s->arg, state);
+  free(pfds);
+  return NULL;
+}
+
+/*static*/ int
+sndio_init(cubeb **context, char const *context_name)
+{
+  void * libsndio = NULL;
+  struct sio_hdl *hdl;
+
+  assert(context);
+
+#ifndef DISABLE_LIBSNDIO_DLOPEN
+  libsndio = dlopen("libsndio.so.7.0", RTLD_LAZY);
+  if (!libsndio) {
+    libsndio = dlopen("libsndio.so", RTLD_LAZY);
+    if (!libsndio) {
+      DPR("sndio_init(%s) failed dlopen(libsndio.so)\n", context_name);
+      return CUBEB_ERROR;
+    }
+  }
+
+#define LOAD(x) {                               \
+    cubeb_##x = dlsym(libsndio, #x);            \
+    if (!cubeb_##x) {                           \
+      DPR("sndio_init(%s) failed dlsym(%s)\n", context_name, #x); \
+      dlclose(libsndio);                        \
+      return CUBEB_ERROR;                       \
+    }                                           \
+  }
+
+  LIBSNDIO_API_VISIT(LOAD);
+#undef LOAD
+#endif
+
+  /* test if sndio works */
+  hdl = WRAP(sio_open)(sndio_get_device(), SIO_PLAY, 1);
+  if (hdl == NULL) {
+    return CUBEB_ERROR;
+  }
+  WRAP(sio_close)(hdl);
+
+  DPR("sndio_init(%s)\n", context_name);
+  *context = malloc(sizeof(**context));
+  if (*context == NULL)
+	return CUBEB_ERROR;
+  (*context)->libsndio = libsndio;
+  (*context)->ops = &sndio_ops;
+  (void)context_name;
+  return CUBEB_OK;
+}
+
+static char const *
+sndio_get_backend_id(cubeb *context)
+{
+  return "sndio";
+}
+
+static void
+sndio_destroy(cubeb *context)
+{
+  DPR("sndio_destroy()\n");
+  if (context->libsndio)
+    dlclose(context->libsndio);
+  free(context);
+}
+
+static int
+sndio_stream_init(cubeb * context,
+                  cubeb_stream ** stream,
+                  char const * stream_name,
+                  cubeb_devid input_device,
+                  cubeb_stream_params * input_stream_params,
+                  cubeb_devid output_device,
+                  cubeb_stream_params * output_stream_params,
+                  unsigned int latency_frames,
+                  cubeb_data_callback data_callback,
+                  cubeb_state_callback state_callback,
+                  void *user_ptr)
+{
+  cubeb_stream *s;
+  struct sio_par wpar, rpar;
+  cubeb_sample_format format;
+  int rate;
+  size_t bps;
+
+  DPR("sndio_stream_init(%s)\n", stream_name);
+
+  s = malloc(sizeof(cubeb_stream));
+  if (s == NULL)
+    return CUBEB_ERROR;
+  memset(s, 0, sizeof(cubeb_stream));
+  s->mode = 0;
+  if (input_stream_params) {
+    if (input_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
+      DPR("sndio_stream_init(), loopback not supported\n");
+      goto err;
+    }
+    s->mode |= SIO_REC;
+    format = input_stream_params->format;
+    rate = input_stream_params->rate;
+  }
+  if (output_stream_params) {
+    if (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
+      DPR("sndio_stream_init(), loopback not supported\n");
+      goto err;
+    }
+    s->mode |= SIO_PLAY;
+    format = output_stream_params->format;
+    rate = output_stream_params->rate;
+  }
+  if (s->mode == 0) {
+    DPR("sndio_stream_init(), neither playing nor recording\n");
+    goto err;
+  }
+  s->context = context;
+  s->hdl = WRAP(sio_open)(sndio_get_device(), s->mode, 1);
+  if (s->hdl == NULL) {
+    DPR("sndio_stream_init(), sio_open() failed\n");
+    goto err;
+  }
+  WRAP(sio_initpar)(&wpar);
+  wpar.sig = 1;
+  wpar.bits = 16;
+  switch (format) {
+  case CUBEB_SAMPLE_S16LE:
+    wpar.le = 1;
+    break;
+  case CUBEB_SAMPLE_S16BE:
+    wpar.le = 0;
+    break;
+  case CUBEB_SAMPLE_FLOAT32NE:
+    wpar.le = SIO_LE_NATIVE;
+    break;
+  default:
+    DPR("sndio_stream_init() unsupported format\n");
+    goto err;
+  }
+  wpar.rate = rate;
+  if (s->mode & SIO_REC)
+    wpar.rchan = input_stream_params->channels;
+  if (s->mode & SIO_PLAY)
+    wpar.pchan = output_stream_params->channels;
+  wpar.appbufsz = latency_frames;
+  if (!WRAP(sio_setpar)(s->hdl, &wpar) || !WRAP(sio_getpar)(s->hdl, &rpar)) {
+    DPR("sndio_stream_init(), sio_setpar() failed\n");
+    goto err;
+  }
+  if (rpar.bits != wpar.bits || rpar.le != wpar.le ||
+      rpar.sig != wpar.sig || rpar.rate != wpar.rate ||
+      ((s->mode & SIO_REC) && rpar.rchan != wpar.rchan) ||
+      ((s->mode & SIO_PLAY) && rpar.pchan != wpar.pchan)) {
+    DPR("sndio_stream_init() unsupported params\n");
+    goto err;
+  }
+  WRAP(sio_onmove)(s->hdl, sndio_onmove, s);
+  s->active = 0;
+  s->nfr = rpar.round;
+  s->rbpf = rpar.bps * rpar.rchan;
+  s->pbpf = rpar.bps * rpar.pchan;
+  s->rchan = rpar.rchan;
+  s->pchan = rpar.pchan;
+  s->nblks = rpar.bufsz / rpar.round;
+  s->data_cb = data_callback;
+  s->state_cb = state_callback;
+  s->arg = user_ptr;
+  s->mtx = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
+  s->hwpos = s->swpos = 0;
+  if (format == CUBEB_SAMPLE_FLOAT32LE) {
+    s->conv = 1;
+    bps = sizeof(float);
+  } else {
+    s->conv = 0;
+    bps = rpar.bps;
+  }
+  if (s->mode & SIO_PLAY) {
+    s->pbuf = malloc(bps * rpar.pchan * rpar.round);
+    if (s->pbuf == NULL)
+      goto err;
+  }
+  if (s->mode & SIO_REC) {
+    s->rbuf = malloc(bps * rpar.rchan * rpar.round);
+    if (s->rbuf == NULL)
+      goto err;
+  }
+  s->volume = 1.;
+  *stream = s;
+  DPR("sndio_stream_init() end, ok\n");
+  (void)context;
+  (void)stream_name;
+  return CUBEB_OK;
+err:
+  if (s->hdl)
+    WRAP(sio_close)(s->hdl);
+  if (s->pbuf)
+    free(s->pbuf);
+  if (s->rbuf)
+    free(s->pbuf);
+  free(s);
+  return CUBEB_ERROR;
+}
+
+static int
+sndio_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
+{
+  assert(ctx && max_channels);
+
+  *max_channels = 8;
+
+  return CUBEB_OK;
+}
+
+static int
+sndio_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
+{
+  /*
+   * We've no device-independent prefered rate; any rate will work if
+   * sndiod is running. If it isn't, 48kHz is what is most likely to
+   * work as most (but not all) devices support it.
+   */
+  *rate = 48000;
+  return CUBEB_OK;
+}
+
+static int
+sndio_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
+{
+  /*
+   * We've no device-independent minimum latency.
+   */
+  *latency_frames = 2048;
+
+  return CUBEB_OK;
+}
+
+static void
+sndio_stream_destroy(cubeb_stream *s)
+{
+  DPR("sndio_stream_destroy()\n");
+  WRAP(sio_close)(s->hdl);
+  if (s->mode & SIO_PLAY)
+    free(s->pbuf);
+  if (s->mode & SIO_REC)
+    free(s->rbuf);
+  free(s);
+}
+
+static int
+sndio_stream_start(cubeb_stream *s)
+{
+  int err;
+
+  DPR("sndio_stream_start()\n");
+  s->active = 1;
+  err = pthread_create(&s->th, NULL, sndio_mainloop, s);
+  if (err) {
+    s->active = 0;
+    return CUBEB_ERROR;
+  }
+  return CUBEB_OK;
+}
+
+static int
+sndio_stream_stop(cubeb_stream *s)
+{
+  void *dummy;
+
+  DPR("sndio_stream_stop()\n");
+  if (s->active) {
+    s->active = 0;
+    pthread_join(s->th, &dummy);
+  }
+  return CUBEB_OK;
+}
+
+static int
+sndio_stream_get_position(cubeb_stream *s, uint64_t *p)
+{
+  pthread_mutex_lock(&s->mtx);
+  DPR("sndio_stream_get_position() %" PRId64 "\n", s->hwpos);
+  *p = s->hwpos;
+  pthread_mutex_unlock(&s->mtx);
+  return CUBEB_OK;
+}
+
+static int
+sndio_stream_set_volume(cubeb_stream *s, float volume)
+{
+  DPR("sndio_stream_set_volume(%f)\n", volume);
+  pthread_mutex_lock(&s->mtx);
+  if (volume < 0.)
+    volume = 0.;
+  else if (volume > 1.0)
+    volume = 1.;
+  s->volume = volume;
+  pthread_mutex_unlock(&s->mtx);
+  return CUBEB_OK;
+}
+
+int
+sndio_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
+{
+  // http://www.openbsd.org/cgi-bin/man.cgi?query=sio_open
+  // in the "Measuring the latency and buffers usage" paragraph.
+  *latency = stm->swpos - stm->hwpos;
+  return CUBEB_OK;
+}
+
+static int
+sndio_enumerate_devices(cubeb *context, cubeb_device_type type,
+	cubeb_device_collection *collection)
+{
+  static char dev[] = SIO_DEVANY;
+  cubeb_device_info *device;
+
+  device = malloc(sizeof(cubeb_device_info));
+  if (device == NULL)
+    return CUBEB_ERROR;
+
+  device->devid = dev;		/* passed to stream_init() */
+  device->device_id = dev;	/* printable in UI */
+  device->friendly_name = dev;	/* same, but friendly */
+  device->group_id = dev;	/* actual device if full-duplex */
+  device->vendor_name = NULL;   /* may be NULL */
+  device->type = type;		/* Input/Output */
+  device->state = CUBEB_DEVICE_STATE_ENABLED;
+  device->preferred = CUBEB_DEVICE_PREF_ALL;
+  device->format = CUBEB_DEVICE_FMT_S16NE;
+  device->default_format = CUBEB_DEVICE_FMT_S16NE;
+  device->max_channels = 16;
+  device->default_rate = 48000;
+  device->min_rate = 4000;
+  device->max_rate = 192000;
+  device->latency_lo = 480;
+  device->latency_hi = 9600;
+  collection->device = device;
+  collection->count = 1;
+  return CUBEB_OK;
+}
+
+static int
+sndio_device_collection_destroy(cubeb * context,
+	cubeb_device_collection * collection)
+{
+  free(collection->device);
+  return CUBEB_OK;
+}
+
+static struct cubeb_ops const sndio_ops = {
+  .init = sndio_init,
+  .get_backend_id = sndio_get_backend_id,
+  .get_max_channel_count = sndio_get_max_channel_count,
+  .get_min_latency = sndio_get_min_latency,
+  .get_preferred_sample_rate = sndio_get_preferred_sample_rate,
+  .enumerate_devices = sndio_enumerate_devices,
+  .device_collection_destroy = sndio_device_collection_destroy,
+  .destroy = sndio_destroy,
+  .stream_init = sndio_stream_init,
+  .stream_destroy = sndio_stream_destroy,
+  .stream_start = sndio_stream_start,
+  .stream_stop = sndio_stream_stop,
+  .stream_reset_default_device = NULL,
+  .stream_get_position = sndio_stream_get_position,
+  .stream_get_latency = sndio_stream_get_latency,
+  .stream_set_volume = sndio_stream_set_volume,
+  .stream_get_current_device = NULL,
+  .stream_device_destroy = NULL,
+  .stream_register_device_changed_callback = NULL,
+  .register_device_collection_changed = NULL
+};
diff --git a/dep/cubeb/src/cubeb_strings.c b/dep/cubeb/src/cubeb_strings.c
new file mode 100644
index 000000000..79d7d21b3
--- /dev/null
+++ b/dep/cubeb/src/cubeb_strings.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright © 2011 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+
+#include "cubeb_strings.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define CUBEB_STRINGS_INLINE_COUNT 4
+
+struct cubeb_strings {
+  uint32_t size;
+  uint32_t count;
+  char ** data;
+  char * small_store[CUBEB_STRINGS_INLINE_COUNT];
+};
+
+int
+cubeb_strings_init(cubeb_strings ** strings)
+{
+  cubeb_strings* strs = NULL;
+
+  if (!strings) {
+    return CUBEB_ERROR;
+  }
+
+  strs = calloc(1, sizeof(cubeb_strings));
+  assert(strs);
+
+  if (!strs) {
+    return CUBEB_ERROR;
+  }
+
+  strs->size = sizeof(strs->small_store) / sizeof(strs->small_store[0]);
+  strs->count = 0;
+  strs->data = strs->small_store;
+
+  *strings = strs;
+
+  return CUBEB_OK;
+}
+
+void
+cubeb_strings_destroy(cubeb_strings * strings)
+{
+  char ** sp = NULL;
+  char ** se = NULL;
+
+  if (!strings) {
+    return;
+  }
+
+  sp = strings->data;
+  se = sp + strings->count;
+
+  for ( ;  sp != se; sp++) {
+    if (*sp) {
+      free(*sp);
+    }
+  }
+
+  if (strings->data != strings->small_store) {
+    free(strings->data);
+  }
+
+  free(strings);
+}
+
+/** Look for string in string storage.
+    @param strings Opaque pointer to interned string storage.
+    @param s String to look up.
+    @retval Read-only string or NULL if not found. */
+static char const *
+cubeb_strings_lookup(cubeb_strings * strings, char const * s)
+{
+  char ** sp = NULL;
+  char ** se = NULL;
+
+  if (!strings || !s) {
+    return NULL;
+  }
+
+  sp = strings->data;
+  se = sp + strings->count;
+
+  for ( ; sp != se; sp++) {
+    if (*sp && strcmp(*sp, s) == 0) {
+      return *sp;
+    }
+  }
+
+  return NULL;
+}
+
+static char const *
+cubeb_strings_push(cubeb_strings * strings, char const * s)
+{
+  char * is = NULL;
+
+  if (strings->count == strings->size) {
+    char ** new_data;
+    uint32_t value_size = sizeof(char const *);
+    uint32_t new_size = strings->size * 2;
+    if (!new_size || value_size > (uint32_t)-1 / new_size) {
+      // overflow
+      return NULL;
+    }
+
+    if (strings->small_store == strings->data) {
+      // First time heap allocation.
+      new_data = malloc(new_size * value_size);
+      if (new_data) {
+        memcpy(new_data, strings->small_store, sizeof(strings->small_store));
+      }
+    } else {
+      new_data = realloc(strings->data, new_size * value_size);
+    }
+
+    if (!new_data) {
+      // out of memory
+      return NULL;
+    }
+
+    strings->size = new_size;
+    strings->data = new_data;
+  }
+
+  is = strdup(s);
+  strings->data[strings->count++] = is;
+
+  return is;
+}
+
+char const *
+cubeb_strings_intern(cubeb_strings * strings, char const * s)
+{
+  char const * is = NULL;
+
+  if (!strings || !s) {
+    return NULL;
+  }
+
+  is = cubeb_strings_lookup(strings, s);
+  if (is) {
+    return is;
+  }
+
+  return cubeb_strings_push(strings, s);
+}
+
diff --git a/dep/cubeb/src/cubeb_strings.h b/dep/cubeb/src/cubeb_strings.h
new file mode 100644
index 000000000..a918a01c5
--- /dev/null
+++ b/dep/cubeb/src/cubeb_strings.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright © 2011 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+
+#ifndef CUBEB_STRINGS_H
+#define CUBEB_STRINGS_H
+
+#include "cubeb/cubeb.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/** Opaque handle referencing interned string storage. */
+typedef struct cubeb_strings cubeb_strings;
+
+/** Initialize an interned string structure.
+    @param strings An out param where an opaque pointer to the
+    interned string storage will be returned.
+    @retval CUBEB_OK in case of success.
+    @retval CUBEB_ERROR in case of error. */
+CUBEB_EXPORT int cubeb_strings_init(cubeb_strings ** strings);
+
+/** Destroy an interned string structure freeing all associated memory.
+    @param strings An opaque pointer to the interned string storage to
+                   destroy. */
+CUBEB_EXPORT void cubeb_strings_destroy(cubeb_strings * strings);
+
+/** Add string to internal storage.
+    @param strings Opaque pointer to interned string storage.
+    @param s String to add to storage.
+    @retval CUBEB_OK
+    @retval CUBEB_ERROR
+ */
+CUBEB_EXPORT char const * cubeb_strings_intern(cubeb_strings * strings, char const * s);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif // !CUBEB_STRINGS_H
diff --git a/dep/cubeb/src/cubeb_sun.c b/dep/cubeb/src/cubeb_sun.c
new file mode 100644
index 000000000..64ab0b5b1
--- /dev/null
+++ b/dep/cubeb/src/cubeb_sun.c
@@ -0,0 +1,752 @@
+/*
+ * Copyright © 2019 Nia Alarie
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+#include <sys/audioio.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include "cubeb/cubeb.h"
+#include "cubeb-internal.h"
+
+#define BYTES_TO_FRAMES(bytes, channels) \
+  (bytes / (channels * sizeof(int16_t)))
+
+#define FRAMES_TO_BYTES(frames, channels) \
+  (frames * (channels * sizeof(int16_t)))
+
+/* Default to 4 + 1 for the default device. */
+#ifndef SUN_DEVICE_COUNT
+#define SUN_DEVICE_COUNT (5)
+#endif
+
+/* Supported well by most hardware. */
+#ifndef SUN_PREFER_RATE
+#define SUN_PREFER_RATE (48000)
+#endif
+
+/* Standard acceptable minimum. */
+#ifndef SUN_LATENCY_MS
+#define SUN_LATENCY_MS (40)
+#endif
+
+#ifndef SUN_DEFAULT_DEVICE
+#define SUN_DEFAULT_DEVICE "/dev/audio"
+#endif
+
+#ifndef SUN_POLL_TIMEOUT
+#define SUN_POLL_TIMEOUT (1000)
+#endif
+
+#ifndef SUN_BUFFER_FRAMES
+#define SUN_BUFFER_FRAMES (32)
+#endif
+
+/*
+ * Supported on NetBSD regardless of hardware.
+ */
+
+#ifndef SUN_MAX_CHANNELS
+# ifdef __NetBSD__
+#  define SUN_MAX_CHANNELS (12)
+# else
+#  define SUN_MAX_CHANNELS (2)
+# endif
+#endif
+
+#ifndef SUN_MIN_RATE
+#define SUN_MIN_RATE (1000)
+#endif
+
+#ifndef SUN_MAX_RATE
+#define SUN_MAX_RATE (192000)
+#endif
+
+static struct cubeb_ops const sun_ops;
+
+struct cubeb {
+  struct cubeb_ops const * ops;
+};
+
+struct cubeb_stream {
+  struct cubeb * context;
+  void * user_ptr;
+  pthread_t thread;
+  pthread_mutex_t mutex; /* protects running, volume, frames_written */
+  int floating;
+  int running;
+  int play_fd;
+  int record_fd;
+  float volume;
+  struct audio_info p_info; /* info for the play fd */
+  struct audio_info r_info; /* info for the record fd */
+  cubeb_data_callback data_cb;
+  cubeb_state_callback state_cb;
+  int16_t * play_buf;
+  int16_t * record_buf;
+  float * f_play_buf;
+  float * f_record_buf;
+  char input_name[32];
+  char output_name[32];
+  uint64_t frames_written;
+  uint64_t blocks_written;
+};
+
+int
+sun_init(cubeb ** context, char const * context_name)
+{
+  cubeb * c;
+
+  (void)context_name;
+  if ((c = calloc(1, sizeof(cubeb))) == NULL) {
+    return CUBEB_ERROR;
+  }
+  c->ops = &sun_ops;
+  *context = c;
+  return CUBEB_OK;
+}
+
+static void
+sun_destroy(cubeb * context)
+{
+  free(context);
+}
+
+static char const *
+sun_get_backend_id(cubeb * context)
+{
+  return "sun";
+}
+
+static int
+sun_get_preferred_sample_rate(cubeb * context, uint32_t * rate)
+{
+  (void)context;
+
+  *rate = SUN_PREFER_RATE;
+  return CUBEB_OK;
+}
+
+static int
+sun_get_max_channel_count(cubeb * context, uint32_t * max_channels)
+{
+  (void)context;
+
+  *max_channels = SUN_MAX_CHANNELS;
+  return CUBEB_OK;
+}
+
+static int
+sun_get_min_latency(cubeb * context, cubeb_stream_params params,
+                    uint32_t * latency_frames)
+{
+  (void)context;
+
+  *latency_frames = SUN_LATENCY_MS * params.rate / 1000;
+  return CUBEB_OK;
+}
+
+static int
+sun_get_hwinfo(const char * device, struct audio_info * format,
+               int * props, struct audio_device * dev)
+{
+  int fd = -1;
+
+  if ((fd = open(device, O_RDONLY)) == -1) {
+    goto error;
+  }
+#ifdef AUDIO_GETFORMAT
+  if (ioctl(fd, AUDIO_GETFORMAT, format) != 0) {
+    goto error;
+  }
+#endif
+#ifdef AUDIO_GETPROPS
+  if (ioctl(fd, AUDIO_GETPROPS, props) != 0) {
+    goto error;
+  }
+#endif
+  if (ioctl(fd, AUDIO_GETDEV, dev) != 0) {
+    goto error;
+  }
+  close(fd);
+  return CUBEB_OK;
+error:
+  if (fd != -1) {
+    close(fd);
+  }
+  return CUBEB_ERROR;
+}
+
+/*
+ * XXX: PR kern/54264
+ */
+static int
+sun_prinfo_verify_sanity(struct audio_prinfo * prinfo)
+{
+   return prinfo->precision >= 8 && prinfo->precision <= 32 &&
+     prinfo->channels >= 1 && prinfo->channels < SUN_MAX_CHANNELS &&
+     prinfo->sample_rate < SUN_MAX_RATE && prinfo->sample_rate > SUN_MIN_RATE;
+}
+
+static int
+sun_enumerate_devices(cubeb * context, cubeb_device_type type,
+                      cubeb_device_collection * collection)
+{
+  unsigned i;
+  cubeb_device_info device = {0};
+  char dev[16] = SUN_DEFAULT_DEVICE;
+  char dev_friendly[64];
+  struct audio_info hwfmt;
+  struct audio_device hwname;
+  struct audio_prinfo *prinfo = NULL;
+  int hwprops;
+
+  collection->device = calloc(SUN_DEVICE_COUNT, sizeof(cubeb_device_info));
+  if (collection->device == NULL) {
+    return CUBEB_ERROR;
+  }
+  collection->count = 0;
+
+  for (i = 0; i < SUN_DEVICE_COUNT; ++i) {
+    if (i > 0) {
+      (void)snprintf(dev, sizeof(dev), "/dev/audio%u", i - 1);
+    }
+    if (sun_get_hwinfo(dev, &hwfmt, &hwprops, &hwname) != CUBEB_OK) {
+      continue;
+    }
+#ifdef AUDIO_GETPROPS
+    device.type = 0;
+    if ((hwprops & AUDIO_PROP_CAPTURE) != 0 &&
+        sun_prinfo_verify_sanity(&hwfmt.record)) {
+      /* the device supports recording, probably */
+      device.type |= CUBEB_DEVICE_TYPE_INPUT;
+    }
+    if ((hwprops & AUDIO_PROP_PLAYBACK) != 0 &&
+        sun_prinfo_verify_sanity(&hwfmt.play)) {
+      /* the device supports playback, probably */
+      device.type |= CUBEB_DEVICE_TYPE_OUTPUT;
+    }
+    switch (device.type) {
+    case 0:
+      /* device doesn't do input or output, aliens probably involved */
+      continue;
+    case CUBEB_DEVICE_TYPE_INPUT:
+      if ((type & CUBEB_DEVICE_TYPE_INPUT) == 0) {
+        /* this device is input only, not scanning for those, skip it */
+        continue;
+      }
+      break;
+    case CUBEB_DEVICE_TYPE_OUTPUT:
+      if ((type & CUBEB_DEVICE_TYPE_OUTPUT) == 0) {
+        /* this device is output only, not scanning for those, skip it */
+        continue;
+      }
+      break;
+    }
+    if ((type & CUBEB_DEVICE_TYPE_INPUT) != 0) {
+      prinfo = &hwfmt.record;
+    }
+    if ((type & CUBEB_DEVICE_TYPE_OUTPUT) != 0) {
+      prinfo = &hwfmt.play;
+    }
+#endif
+    if (i > 0) {
+      (void)snprintf(dev_friendly, sizeof(dev_friendly), "%s %s %s (%d)",
+                     hwname.name, hwname.version, hwname.config, i - 1);
+    } else {
+      (void)snprintf(dev_friendly, sizeof(dev_friendly), "%s %s %s (default)",
+                     hwname.name, hwname.version, hwname.config);
+    }
+    device.devid = (void *)(uintptr_t)i;
+    device.device_id = strdup(dev);
+    device.friendly_name = strdup(dev_friendly);
+    device.group_id = strdup(dev);
+    device.vendor_name = strdup(hwname.name);
+    device.type = type;
+    device.state = CUBEB_DEVICE_STATE_ENABLED;
+    device.preferred = (i == 0) ? CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE;
+#ifdef AUDIO_GETFORMAT
+    device.max_channels = prinfo->channels;
+    device.default_rate = prinfo->sample_rate;
+#else
+    device.max_channels = 2;
+    device.default_rate = SUN_PREFER_RATE;
+#endif
+    device.default_format = CUBEB_DEVICE_FMT_S16NE;
+    device.format = CUBEB_DEVICE_FMT_S16NE;
+    device.min_rate = SUN_MIN_RATE;
+    device.max_rate = SUN_MAX_RATE;
+    device.latency_lo = SUN_LATENCY_MS * SUN_MIN_RATE / 1000;
+    device.latency_hi = SUN_LATENCY_MS * SUN_MAX_RATE / 1000;
+    collection->device[collection->count++] = device;
+  }
+  return CUBEB_OK;
+}
+
+static int
+sun_device_collection_destroy(cubeb * context,
+                              cubeb_device_collection * collection)
+{
+  unsigned i;
+
+  for (i = 0; i < collection->count; ++i) {
+    free((char *)collection->device[i].device_id);
+    free((char *)collection->device[i].friendly_name);
+    free((char *)collection->device[i].group_id);
+    free((char *)collection->device[i].vendor_name);
+  }
+  free(collection->device);
+  return CUBEB_OK;
+}
+
+static int
+sun_copy_params(int fd, cubeb_stream * stream, cubeb_stream_params * params,
+                struct audio_info * info, struct audio_prinfo * prinfo)
+{
+  prinfo->channels = params->channels;
+  prinfo->sample_rate = params->rate;
+  prinfo->precision = 16;
+#ifdef AUDIO_ENCODING_SLINEAR_LE
+  switch (params->format) {
+  case CUBEB_SAMPLE_S16LE:
+    prinfo->encoding = AUDIO_ENCODING_SLINEAR_LE;
+    break;
+  case CUBEB_SAMPLE_S16BE:
+    prinfo->encoding = AUDIO_ENCODING_SLINEAR_BE;
+    break;
+  case CUBEB_SAMPLE_FLOAT32NE:
+    stream->floating = 1;
+    prinfo->encoding = AUDIO_ENCODING_SLINEAR;
+    break;
+  default:
+    LOG("Unsupported format");
+    return CUBEB_ERROR_INVALID_FORMAT;
+  }
+#else
+  switch (params->format) {
+  case CUBEB_SAMPLE_S16NE:
+    prinfo->encoding = AUDIO_ENCODING_LINEAR;
+    break;
+  case CUBEB_SAMPLE_FLOAT32NE:
+    stream->floating = 1;
+    prinfo->encoding = AUDIO_ENCODING_LINEAR;
+    break;
+  default:
+    LOG("Unsupported format");
+    return CUBEB_ERROR_INVALID_FORMAT;
+  }
+#endif
+  if (ioctl(fd, AUDIO_SETINFO, info) == -1) {
+    return CUBEB_ERROR;
+  }
+  if (ioctl(fd, AUDIO_GETINFO, info) == -1) {
+    return CUBEB_ERROR;
+  }
+  return CUBEB_OK;
+}
+
+static int
+sun_stream_stop(cubeb_stream * s)
+{
+  pthread_mutex_lock(&s->mutex);
+  if (s->running) {
+    s->running = 0;
+    pthread_mutex_unlock(&s->mutex);
+    pthread_join(s->thread, NULL);
+  } else {
+    pthread_mutex_unlock(&s->mutex);
+  }
+  return CUBEB_OK;
+}
+
+static void
+sun_stream_destroy(cubeb_stream * s)
+{
+  pthread_mutex_destroy(&s->mutex);
+  sun_stream_stop(s);
+  if (s->play_fd != -1) {
+    close(s->play_fd);
+  }
+  if (s->record_fd != -1) {
+    close(s->record_fd);
+  }
+  free(s->f_play_buf);
+  free(s->f_record_buf);
+  free(s->play_buf);
+  free(s->record_buf);
+  free(s);
+}
+
+static void
+sun_float_to_linear(float * in, int16_t * out,
+                    unsigned channels, long frames, float vol)
+{
+  unsigned i, sample_count = frames * channels;
+  float multiplier = vol * 0x8000;
+
+  for (i = 0; i < sample_count; ++i) {
+    int32_t sample = lrintf(in[i] * multiplier);
+    if (sample < -0x8000) {
+      out[i] = -0x8000;
+    } else if (sample > 0x7fff) {
+      out[i] = 0x7fff;
+    } else {
+      out[i] = sample;
+    }
+  }
+}
+
+static void
+sun_linear_to_float(int16_t * in, float * out,
+                    unsigned channels, long frames)
+{
+  unsigned i, sample_count = frames * channels;
+
+  for (i = 0; i < sample_count; ++i) {
+    out[i] = (1.0 / 0x8000) * in[i];
+  }
+}
+
+static void
+sun_linear_set_vol(int16_t * buf, unsigned channels, long frames, float vol)
+{
+  unsigned i, sample_count = frames * channels;
+  int32_t multiplier = vol * 0x8000;
+
+  for (i = 0; i < sample_count; ++i) {
+    buf[i] = (buf[i] * multiplier) >> 15;
+  }
+}
+
+static void *
+sun_io_routine(void * arg)
+{
+  cubeb_stream *s = arg;
+  cubeb_state state = CUBEB_STATE_STARTED;
+  size_t to_read = 0;
+  long to_write = 0;
+  size_t write_ofs = 0;
+  size_t read_ofs = 0;
+  int drain = 0;
+
+  s->state_cb(s, s->user_ptr, CUBEB_STATE_STARTED);
+  while (state != CUBEB_STATE_ERROR) {
+    pthread_mutex_lock(&s->mutex);
+    if (!s->running) {
+      pthread_mutex_unlock(&s->mutex);
+      state = CUBEB_STATE_STOPPED;
+      break;
+    }
+    pthread_mutex_unlock(&s->mutex);
+    if (s->floating) {
+      if (s->record_fd != -1) {
+        sun_linear_to_float(s->record_buf, s->f_record_buf,
+                            s->r_info.record.channels, SUN_BUFFER_FRAMES);
+      }
+      to_write = s->data_cb(s, s->user_ptr,
+                            s->f_record_buf, s->f_play_buf, SUN_BUFFER_FRAMES);
+      if (to_write == CUBEB_ERROR) {
+        state = CUBEB_STATE_ERROR;
+        break;
+      }
+      if (s->play_fd != -1) {
+        pthread_mutex_lock(&s->mutex);
+        sun_float_to_linear(s->f_play_buf, s->play_buf,
+                            s->p_info.play.channels, to_write, s->volume);
+        pthread_mutex_unlock(&s->mutex);
+      }
+    } else {
+      to_write = s->data_cb(s, s->user_ptr,
+                            s->record_buf, s->play_buf, SUN_BUFFER_FRAMES);
+      if (to_write == CUBEB_ERROR) {
+        state = CUBEB_STATE_ERROR;
+        break;
+      }
+      if (s->play_fd != -1) {
+        pthread_mutex_lock(&s->mutex);
+        sun_linear_set_vol(s->play_buf, s->p_info.play.channels, to_write, s->volume);
+        pthread_mutex_unlock(&s->mutex);
+      }
+    }
+    if (to_write < SUN_BUFFER_FRAMES) {
+      drain = 1;
+    }
+    to_write = s->play_fd != -1 ? to_write : 0;
+    to_read = s->record_fd != -1 ? SUN_BUFFER_FRAMES : 0;
+    write_ofs = 0;
+    read_ofs = 0;
+    while (to_write > 0 || to_read > 0) {
+      size_t bytes;
+      ssize_t n, frames;
+
+      if (to_write > 0) {
+        bytes = FRAMES_TO_BYTES(to_write, s->p_info.play.channels);
+        if ((n = write(s->play_fd, s->play_buf + write_ofs, bytes)) < 0) {
+          state = CUBEB_STATE_ERROR;
+          break;
+        }
+        frames = BYTES_TO_FRAMES(n, s->p_info.play.channels);
+        pthread_mutex_lock(&s->mutex);
+        s->frames_written += frames;
+        pthread_mutex_unlock(&s->mutex);
+        to_write -= frames;
+        write_ofs += frames;
+      }
+      if (to_read > 0) {
+        bytes = FRAMES_TO_BYTES(to_read, s->r_info.record.channels);
+        if ((n = read(s->record_fd, s->record_buf + read_ofs, bytes)) < 0) {
+          state = CUBEB_STATE_ERROR;
+          break;
+        }
+        frames = BYTES_TO_FRAMES(n, s->r_info.record.channels);
+        to_read -= frames;
+        read_ofs += frames;
+      }
+    }
+    if (drain && state != CUBEB_STATE_ERROR) {
+      state = CUBEB_STATE_DRAINED;
+      break;
+    }
+  }
+  s->state_cb(s, s->user_ptr, state);
+  return NULL;
+}
+
+static int
+sun_stream_init(cubeb * context,
+                cubeb_stream ** stream,
+                char const * stream_name,
+                cubeb_devid input_device,
+                cubeb_stream_params * input_stream_params,
+                cubeb_devid output_device,
+                cubeb_stream_params * output_stream_params,
+                unsigned latency_frames,
+                cubeb_data_callback data_callback,
+                cubeb_state_callback state_callback,
+                void * user_ptr)
+{
+  int ret = CUBEB_OK;
+  cubeb_stream *s = NULL;
+
+  (void)stream_name;
+  (void)latency_frames;
+  if ((s = calloc(1, sizeof(cubeb_stream))) == NULL) {
+    ret = CUBEB_ERROR;
+    goto error;
+  }
+  s->record_fd = -1;
+  s->play_fd = -1;
+  if (input_device != 0) {
+    snprintf(s->input_name, sizeof(s->input_name),
+      "/dev/audio%zu", (uintptr_t)input_device - 1);
+  } else {
+    snprintf(s->input_name, sizeof(s->input_name), "%s", SUN_DEFAULT_DEVICE);
+  }
+  if (output_device != 0) {
+    snprintf(s->output_name, sizeof(s->output_name),
+      "/dev/audio%zu", (uintptr_t)output_device - 1);
+  } else {
+    snprintf(s->output_name, sizeof(s->output_name), "%s", SUN_DEFAULT_DEVICE);
+  }
+  if (input_stream_params != NULL) {
+    if (input_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
+      LOG("Loopback not supported");
+      ret = CUBEB_ERROR_NOT_SUPPORTED;
+      goto error;
+    }
+    if (s->record_fd == -1) {
+      if ((s->record_fd = open(s->input_name, O_RDONLY)) == -1) {
+        LOG("Audio device cannot be opened as read-only");
+        ret = CUBEB_ERROR_DEVICE_UNAVAILABLE;
+        goto error;
+      }
+    }
+    AUDIO_INITINFO(&s->r_info);
+#ifdef AUMODE_RECORD
+    s->r_info.mode = AUMODE_RECORD;
+#endif
+    if ((ret = sun_copy_params(s->record_fd, s, input_stream_params,
+                               &s->r_info, &s->r_info.record)) != CUBEB_OK) {
+      LOG("Setting record params failed");
+      goto error;
+    }
+  }
+  if (output_stream_params != NULL) {
+    if (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
+      LOG("Loopback not supported");
+      ret = CUBEB_ERROR_NOT_SUPPORTED;
+      goto error;
+    }
+    if (s->play_fd == -1) {
+      if ((s->play_fd = open(s->output_name, O_WRONLY)) == -1) {
+        LOG("Audio device cannot be opened as write-only");
+        ret = CUBEB_ERROR_DEVICE_UNAVAILABLE;
+        goto error;
+      }
+    }
+    AUDIO_INITINFO(&s->p_info);
+#ifdef AUMODE_PLAY
+    s->p_info.mode = AUMODE_PLAY;
+#endif
+    if ((ret = sun_copy_params(s->play_fd, s, output_stream_params,
+                               &s->p_info, &s->p_info.play)) != CUBEB_OK) {
+      LOG("Setting play params failed");
+      goto error;
+    }
+  }
+  s->context = context;
+  s->volume = 1.0;
+  s->state_cb = state_callback;
+  s->data_cb = data_callback;
+  s->user_ptr = user_ptr;
+  if (pthread_mutex_init(&s->mutex, NULL) != 0) {
+    LOG("Failed to create mutex");
+    goto error;
+  }
+  if (s->play_fd != -1 && (s->play_buf = calloc(SUN_BUFFER_FRAMES,
+      s->p_info.play.channels * sizeof(int16_t))) == NULL) {
+    ret = CUBEB_ERROR;
+    goto error;
+  }
+  if (s->record_fd != -1 && (s->record_buf = calloc(SUN_BUFFER_FRAMES,
+      s->r_info.record.channels * sizeof(int16_t))) == NULL) {
+    ret = CUBEB_ERROR;
+    goto error;
+  }
+  if (s->floating) {
+    if (s->play_fd != -1 && (s->f_play_buf = calloc(SUN_BUFFER_FRAMES,
+        s->p_info.play.channels * sizeof(float))) == NULL) {
+      ret = CUBEB_ERROR;
+      goto error;
+    }
+    if (s->record_fd != -1 && (s->f_record_buf = calloc(SUN_BUFFER_FRAMES,
+        s->r_info.record.channels * sizeof(float))) == NULL) {
+      ret = CUBEB_ERROR;
+      goto error;
+    }
+  }
+  *stream = s;
+  return CUBEB_OK;
+error:
+  if (s != NULL) {
+    sun_stream_destroy(s);
+  }
+  return ret;
+}
+
+static int
+sun_stream_start(cubeb_stream * s)
+{
+  s->running = 1;
+  if (pthread_create(&s->thread, NULL, sun_io_routine, s) != 0) {
+    LOG("Couldn't create thread");
+    return CUBEB_ERROR;
+  }
+  return CUBEB_OK;
+}
+
+static int
+sun_stream_get_position(cubeb_stream * s, uint64_t * position)
+{
+#ifdef AUDIO_GETOOFFS
+  struct audio_offset offset;
+
+  if (ioctl(s->play_fd, AUDIO_GETOOFFS, &offset) == -1) {
+    return CUBEB_ERROR;
+  }
+  s->blocks_written += offset.deltablks;
+  *position = BYTES_TO_FRAMES(s->blocks_written * s->p_info.blocksize,
+                              s->p_info.play.channels);
+  return CUBEB_OK;
+#else
+  pthread_mutex_lock(&s->mutex);
+  *position = s->frames_written;
+  pthread_mutex_unlock(&s->mutex);
+  return CUBEB_OK;
+#endif
+}
+
+static int
+sun_stream_get_latency(cubeb_stream * stream, uint32_t * latency)
+{
+#ifdef AUDIO_GETBUFINFO
+  struct audio_info info;
+
+  if (ioctl(stream->play_fd, AUDIO_GETBUFINFO, &info) == -1) {
+    return CUBEB_ERROR;
+  }
+
+  *latency = BYTES_TO_FRAMES(info.play.seek + info.blocksize,
+                             info.play.channels);
+  return CUBEB_OK;
+#else
+  cubeb_stream_params params;
+
+  params.rate = stream->p_info.play.sample_rate;
+
+  return sun_get_min_latency(NULL, params, latency);
+#endif
+}
+
+static int
+sun_stream_set_volume(cubeb_stream * stream, float volume)
+{
+  pthread_mutex_lock(&stream->mutex);
+  stream->volume = volume;
+  pthread_mutex_unlock(&stream->mutex);
+  return CUBEB_OK;
+}
+
+static int
+sun_get_current_device(cubeb_stream * stream, cubeb_device ** const device)
+{
+  *device = calloc(1, sizeof(cubeb_device));
+  if (*device == NULL) {
+    return CUBEB_ERROR;
+  }
+  (*device)->input_name = stream->record_fd != -1 ?
+    strdup(stream->input_name) : NULL;
+  (*device)->output_name = stream->play_fd != -1 ?
+    strdup(stream->output_name) : NULL;
+  return CUBEB_OK;
+}
+
+static int
+sun_stream_device_destroy(cubeb_stream * stream, cubeb_device * device)
+{
+  (void)stream;
+  free(device->input_name);
+  free(device->output_name);
+  free(device);
+  return CUBEB_OK;
+}
+
+static struct cubeb_ops const sun_ops = {
+  .init = sun_init,
+  .get_backend_id = sun_get_backend_id,
+  .get_max_channel_count = sun_get_max_channel_count,
+  .get_min_latency = sun_get_min_latency,
+  .get_preferred_sample_rate = sun_get_preferred_sample_rate,
+  .enumerate_devices = sun_enumerate_devices,
+  .device_collection_destroy = sun_device_collection_destroy,
+  .destroy = sun_destroy,
+  .stream_init = sun_stream_init,
+  .stream_destroy = sun_stream_destroy,
+  .stream_start = sun_stream_start,
+  .stream_stop = sun_stream_stop,
+  .stream_reset_default_device = NULL,
+  .stream_get_position = sun_stream_get_position,
+  .stream_get_latency = sun_stream_get_latency,
+  .stream_set_volume = sun_stream_set_volume,
+  .stream_get_current_device = sun_get_current_device,
+  .stream_device_destroy = sun_stream_device_destroy,
+  .stream_register_device_changed_callback = NULL,
+  .register_device_collection_changed = NULL
+};
diff --git a/dep/cubeb/src/cubeb_utils.cpp b/dep/cubeb/src/cubeb_utils.cpp
new file mode 100644
index 000000000..85572a9fe
--- /dev/null
+++ b/dep/cubeb/src/cubeb_utils.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright © 2018 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+
+#include "cubeb_utils.h"
+
+size_t cubeb_sample_size(cubeb_sample_format format)
+{
+  switch (format) {
+    case CUBEB_SAMPLE_S16LE:
+    case CUBEB_SAMPLE_S16BE:
+      return sizeof(int16_t);
+    case CUBEB_SAMPLE_FLOAT32LE:
+    case CUBEB_SAMPLE_FLOAT32BE:
+      return sizeof(float);
+    default:
+      // should never happen as all cases are handled above.
+      assert(false);
+  }
+}
diff --git a/dep/cubeb/src/cubeb_utils.h b/dep/cubeb/src/cubeb_utils.h
new file mode 100644
index 000000000..df6751155
--- /dev/null
+++ b/dep/cubeb/src/cubeb_utils.h
@@ -0,0 +1,343 @@
+/*
+ * Copyright © 2016 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+
+#if !defined(CUBEB_UTILS)
+#define CUBEB_UTILS
+
+#include "cubeb/cubeb.h"
+
+#ifdef __cplusplus
+
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <mutex>
+#include <type_traits>
+#if defined(_WIN32)
+#include "cubeb_utils_win.h"
+#else
+#include "cubeb_utils_unix.h"
+#endif
+
+/** Similar to memcpy, but accounts for the size of an element. */
+template<typename T>
+void PodCopy(T * destination, const T * source, size_t count)
+{
+  static_assert(std::is_trivial<T>::value, "Requires trivial type");
+  assert(destination && source);
+  memcpy(destination, source, count * sizeof(T));
+}
+
+/** Similar to memmove, but accounts for the size of an element. */
+template<typename T>
+void PodMove(T * destination, const T * source, size_t count)
+{
+  static_assert(std::is_trivial<T>::value, "Requires trivial type");
+  assert(destination && source);
+  memmove(destination, source, count * sizeof(T));
+}
+
+/** Similar to a memset to zero, but accounts for the size of an element. */
+template<typename T>
+void PodZero(T * destination, size_t count)
+{
+  static_assert(std::is_trivial<T>::value, "Requires trivial type");
+  assert(destination);
+  memset(destination, 0,  count * sizeof(T));
+}
+
+namespace {
+template<typename T, typename Trait>
+void Copy(T * destination, const T * source, size_t count, Trait)
+{
+  for (size_t i = 0; i < count; i++) {
+    destination[i] = source[i];
+  }
+}
+
+template<typename T>
+void Copy(T * destination, const T * source, size_t count, std::true_type)
+{
+  PodCopy(destination, source, count);
+}
+}
+
+/**
+ * This allows copying a number of elements from a `source` pointer to a
+ * `destination` pointer, using `memcpy` if it is safe to do so, or a loop that
+ * calls the constructors and destructors otherwise.
+ */
+template<typename T>
+void Copy(T * destination, const T * source, size_t count)
+{
+  assert(destination && source);
+  Copy(destination, source, count, typename std::is_trivial<T>::type());
+}
+
+namespace {
+template<typename T, typename Trait>
+void ConstructDefault(T * destination, size_t count, Trait)
+{
+  for (size_t i = 0; i < count; i++) {
+    destination[i] = T();
+  }
+}
+
+template<typename T>
+void ConstructDefault(T * destination,
+                      size_t count, std::true_type)
+{
+  PodZero(destination, count);
+}
+}
+
+/**
+ * This allows zeroing (using memset) or default-constructing a number of
+ * elements calling the constructors and destructors if necessary.
+ */
+template<typename T>
+void ConstructDefault(T * destination, size_t count)
+{
+  assert(destination);
+  ConstructDefault(destination, count,
+                   typename std::is_arithmetic<T>::type());
+}
+
+template<typename T>
+class auto_array
+{
+public:
+  explicit auto_array(uint32_t capacity = 0)
+    : data_(capacity ? new T[capacity] : nullptr)
+    , capacity_(capacity)
+    , length_(0)
+  {}
+
+  ~auto_array()
+  {
+    delete [] data_;
+  }
+
+  /** Get a constant pointer to the underlying data. */
+  T * data() const
+  {
+    return data_;
+  }
+
+  T * end() const
+  {
+    return data_ + length_;
+  }
+
+  const T& at(size_t index) const
+  {
+    assert(index < length_ && "out of range");
+    return data_[index];
+  }
+
+  T& at(size_t index)
+  {
+    assert(index < length_ && "out of range");
+    return data_[index];
+  }
+
+  /** Get how much underlying storage this auto_array has. */
+  size_t capacity() const
+  {
+    return capacity_;
+  }
+
+  /** Get how much elements this auto_array contains. */
+  size_t length() const
+  {
+    return length_;
+  }
+
+  /** Keeps the storage, but removes all the elements from the array. */
+  void clear()
+  {
+    length_ = 0;
+  }
+
+   /** Change the storage of this auto array, copying the elements to the new
+    * storage.
+    * @returns true in case of success
+    * @returns false if the new capacity is not big enough to accomodate for the
+    *                elements in the array.
+    */
+  bool reserve(size_t new_capacity)
+  {
+    if (new_capacity < length_) {
+      return false;
+    }
+    T * new_data = new T[new_capacity];
+    if (data_ && length_) {
+      PodCopy(new_data, data_, length_);
+    }
+    capacity_ = new_capacity;
+    delete [] data_;
+    data_ = new_data;
+
+    return true;
+  }
+
+   /** Append `length` elements to the end of the array, resizing the array if
+    * needed.
+    * @parameter elements the elements to append to the array.
+    * @parameter length the number of elements to append to the array.
+    */
+  void push(const T * elements, size_t length)
+  {
+    if (length_ + length > capacity_) {
+      reserve(length_ + length);
+    }
+    PodCopy(data_ + length_, elements, length);
+    length_ += length;
+  }
+
+  /** Append `length` zero-ed elements to the end of the array, resizing the
+   * array if needed.
+   * @parameter length the number of elements to append to the array.
+   */
+  void push_silence(size_t length)
+  {
+    if (length_ + length > capacity_) {
+      reserve(length + length_);
+    }
+    PodZero(data_ + length_, length);
+    length_ += length;
+  }
+
+  /** Prepend `length` zero-ed elements to the end of the array, resizing the
+   * array if needed.
+   * @parameter length the number of elements to prepend to the array.
+   */
+  void push_front_silence(size_t length)
+  {
+    if (length_ + length > capacity_) {
+      reserve(length + length_);
+    }
+    PodMove(data_ + length, data_, length_);
+    PodZero(data_, length);
+    length_ += length;
+  }
+
+  /** Return the number of free elements in the array. */
+  size_t available() const
+  {
+    return capacity_ - length_;
+  }
+
+  /** Copies `length` elements to `elements` if it is not null, and shift
+    * the remaining elements of the `auto_array` to the beginning.
+    * @parameter elements a buffer to copy the elements to, or nullptr.
+    * @parameter length the number of elements to copy.
+    * @returns true in case of success.
+    * @returns false if the auto_array contains less than `length` elements. */
+  bool pop(T * elements, size_t length)
+  {
+    if (length > length_) {
+      return false;
+    }
+    if (elements) {
+      PodCopy(elements, data_, length);
+    }
+    PodMove(data_, data_ + length, length_ - length);
+
+    length_ -= length;
+
+    return true;
+  }
+
+  void set_length(size_t length)
+  {
+    assert(length <= capacity_);
+    length_ = length;
+  }
+
+private:
+  /** The underlying storage */
+  T * data_;
+  /** The size, in number of elements, of the storage. */
+  size_t capacity_;
+  /** The number of elements the array contains. */
+  size_t length_;
+};
+
+struct auto_array_wrapper {
+  virtual void push(void * elements, size_t length) = 0;
+  virtual size_t length() = 0;
+  virtual void push_silence(size_t length) = 0;
+  virtual bool pop(size_t length) = 0;
+  virtual void * data() = 0;
+  virtual void * end() = 0;
+  virtual void clear() = 0;
+  virtual bool reserve(size_t capacity) = 0;
+  virtual void set_length(size_t length) = 0;
+  virtual ~auto_array_wrapper() {}
+};
+
+template <typename T>
+struct auto_array_wrapper_impl : public auto_array_wrapper {
+  auto_array_wrapper_impl() {}
+
+  explicit auto_array_wrapper_impl(uint32_t size)
+    : ar(size)
+  {}
+
+  void push(void * elements, size_t length) override {
+    ar.push(static_cast<T *>(elements), length);
+  }
+
+  size_t length() override {
+    return ar.length();
+  }
+
+  void push_silence(size_t length) override {
+    ar.push_silence(length);
+  }
+
+  bool pop(size_t length) override {
+    return ar.pop(nullptr, length);
+  }
+
+  void * data() override {
+    return ar.data();
+  }
+
+  void * end() override {
+    return ar.end();
+  }
+
+  void clear() override {
+    ar.clear();
+  }
+
+  bool reserve(size_t capacity) override {
+    return ar.reserve(capacity);
+  }
+
+  void set_length(size_t length) override {
+    ar.set_length(length);
+  }
+
+  ~auto_array_wrapper_impl() {
+    ar.clear();
+  }
+
+private:
+  auto_array<T> ar;
+};
+
+extern "C" {
+  size_t cubeb_sample_size(cubeb_sample_format format);
+}
+
+using auto_lock = std::lock_guard<owned_critical_section>;
+#endif // __cplusplus
+
+#endif /* CUBEB_UTILS */
diff --git a/dep/cubeb/src/cubeb_utils_unix.h b/dep/cubeb/src/cubeb_utils_unix.h
new file mode 100644
index 000000000..4876d015f
--- /dev/null
+++ b/dep/cubeb/src/cubeb_utils_unix.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright © 2016 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+
+#if !defined(CUBEB_UTILS_UNIX)
+#define CUBEB_UTILS_UNIX
+
+#include <pthread.h>
+#include <errno.h>
+#include <stdio.h>
+
+/* This wraps a critical section to track the owner in debug mode. */
+class owned_critical_section
+{
+public:
+  owned_critical_section()
+  {
+    pthread_mutexattr_t attr;
+    pthread_mutexattr_init(&attr);
+#ifndef NDEBUG
+    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
+#else
+    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
+#endif
+
+#ifndef NDEBUG
+    int r =
+#endif
+    pthread_mutex_init(&mutex, &attr);
+#ifndef NDEBUG
+    assert(r == 0);
+#endif
+
+    pthread_mutexattr_destroy(&attr);
+  }
+
+  ~owned_critical_section()
+  {
+#ifndef NDEBUG
+    int r =
+#endif
+    pthread_mutex_destroy(&mutex);
+#ifndef NDEBUG
+    assert(r == 0);
+#endif
+  }
+
+  void lock()
+  {
+#ifndef NDEBUG
+    int r =
+#endif
+    pthread_mutex_lock(&mutex);
+#ifndef NDEBUG
+    assert(r == 0 && "Deadlock");
+#endif
+  }
+
+  void unlock()
+  {
+#ifndef NDEBUG
+    int r =
+#endif
+    pthread_mutex_unlock(&mutex);
+#ifndef NDEBUG
+    assert(r == 0 && "Unlocking unlocked mutex");
+#endif
+  }
+
+  void assert_current_thread_owns()
+  {
+#ifndef NDEBUG
+    int r = pthread_mutex_lock(&mutex);
+    assert(r == EDEADLK);
+#endif
+  }
+
+private:
+  pthread_mutex_t mutex;
+
+  // Disallow copy and assignment because pthread_mutex_t cannot be copied.
+  owned_critical_section(const owned_critical_section&);
+  owned_critical_section& operator=(const owned_critical_section&);
+};
+
+#endif /* CUBEB_UTILS_UNIX */
diff --git a/dep/cubeb/src/cubeb_utils_win.h b/dep/cubeb/src/cubeb_utils_win.h
new file mode 100644
index 000000000..0112ad6d3
--- /dev/null
+++ b/dep/cubeb/src/cubeb_utils_win.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright © 2016 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+
+#if !defined(CUBEB_UTILS_WIN)
+#define CUBEB_UTILS_WIN
+
+#include <windows.h>
+#include "cubeb-internal.h"
+
+/* This wraps a critical section to track the owner in debug mode, adapted from
+   NSPR and http://blogs.msdn.com/b/oldnewthing/archive/2013/07/12/10433554.aspx */
+class owned_critical_section
+{
+public:
+  owned_critical_section()
+#ifndef NDEBUG
+    : owner(0)
+#endif
+  {
+    InitializeCriticalSection(&critical_section);
+  }
+
+  ~owned_critical_section()
+  {
+    DeleteCriticalSection(&critical_section);
+  }
+
+  void lock()
+  {
+    EnterCriticalSection(&critical_section);
+#ifndef NDEBUG
+    XASSERT(owner != GetCurrentThreadId() && "recursive locking");
+    owner = GetCurrentThreadId();
+#endif
+  }
+
+  void unlock()
+  {
+#ifndef NDEBUG
+    /* GetCurrentThreadId cannot return 0: it is not a the valid thread id */
+    owner = 0;
+#endif
+    LeaveCriticalSection(&critical_section);
+  }
+
+  /* This is guaranteed to have the good behaviour if it succeeds. The behaviour
+     is undefined otherwise. */
+  void assert_current_thread_owns()
+  {
+#ifndef NDEBUG
+    /* This implies owner != 0, because GetCurrentThreadId cannot return 0. */
+    XASSERT(owner == GetCurrentThreadId());
+#endif
+  }
+
+private:
+  CRITICAL_SECTION critical_section;
+#ifndef NDEBUG
+  DWORD owner;
+#endif
+
+  // Disallow copy and assignment because CRICICAL_SECTION cannot be copied.
+  owned_critical_section(const owned_critical_section&);
+  owned_critical_section& operator=(const owned_critical_section&);
+};
+
+#endif /* CUBEB_UTILS_WIN */
diff --git a/dep/cubeb/src/cubeb_wasapi.cpp b/dep/cubeb/src/cubeb_wasapi.cpp
new file mode 100644
index 000000000..e85462f12
--- /dev/null
+++ b/dep/cubeb/src/cubeb_wasapi.cpp
@@ -0,0 +1,2965 @@
+/*
+ * Copyright © 2013 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+#define _WIN32_WINNT 0x0600
+#define NOMINMAX
+
+#include <initguid.h>
+#include <windows.h>
+#include <mmdeviceapi.h>
+#include <windef.h>
+#include <audioclient.h>
+#include <devicetopology.h>
+#include <process.h>
+#include <avrt.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <cmath>
+#include <algorithm>
+#include <memory>
+#include <limits>
+#include <atomic>
+#include <vector>
+
+#include "cubeb/cubeb.h"
+#include "cubeb-internal.h"
+#include "cubeb_mixer.h"
+#include "cubeb_resampler.h"
+#include "cubeb_strings.h"
+#include "cubeb_utils.h"
+
+// Windows 10 exposes the IAudioClient3 interface to create low-latency streams.
+// Copy the interface definition from audioclient.h here to make the code simpler
+// and so that we can still access IAudioClient3 via COM if cubeb was compiled
+// against an older SDK.
+#ifndef __IAudioClient3_INTERFACE_DEFINED__
+#define __IAudioClient3_INTERFACE_DEFINED__
+MIDL_INTERFACE("7ED4EE07-8E67-4CD4-8C1A-2B7A5987AD42")
+IAudioClient3 : public IAudioClient
+{
+public:
+    virtual HRESULT STDMETHODCALLTYPE GetSharedModeEnginePeriod(
+        /* [annotation][in] */
+        _In_  const WAVEFORMATEX *pFormat,
+        /* [annotation][out] */
+        _Out_  UINT32 *pDefaultPeriodInFrames,
+        /* [annotation][out] */
+        _Out_  UINT32 *pFundamentalPeriodInFrames,
+        /* [annotation][out] */
+        _Out_  UINT32 *pMinPeriodInFrames,
+        /* [annotation][out] */
+        _Out_  UINT32 *pMaxPeriodInFrames) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE GetCurrentSharedModeEnginePeriod(
+        /* [unique][annotation][out] */
+        _Out_  WAVEFORMATEX **ppFormat,
+        /* [annotation][out] */
+        _Out_  UINT32 *pCurrentPeriodInFrames) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE InitializeSharedAudioStream(
+        /* [annotation][in] */
+        _In_  DWORD StreamFlags,
+        /* [annotation][in] */
+        _In_  UINT32 PeriodInFrames,
+        /* [annotation][in] */
+        _In_  const WAVEFORMATEX *pFormat,
+        /* [annotation][in] */
+        _In_opt_  LPCGUID AudioSessionGuid) = 0;
+};
+#ifdef __CRT_UUID_DECL
+// Required for MinGW
+__CRT_UUID_DECL(IAudioClient3, 0x7ED4EE07, 0x8E67, 0x4CD4, 0x8C, 0x1A, 0x2B, 0x7A, 0x59, 0x87, 0xAD, 0x42)
+#endif
+#endif
+// Copied from audioclient.h in the Windows 10 SDK
+#ifndef AUDCLNT_E_ENGINE_PERIODICITY_LOCKED
+#define AUDCLNT_E_ENGINE_PERIODICITY_LOCKED    AUDCLNT_ERR(0x028)
+#endif
+
+#ifndef PKEY_Device_FriendlyName
+DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName,    0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14);    // DEVPROP_TYPE_STRING
+#endif
+#ifndef PKEY_Device_InstanceId
+DEFINE_PROPERTYKEY(PKEY_Device_InstanceId,      0x78c34fc8, 0x104a, 0x4aca, 0x9e, 0xa4, 0x52, 0x4d, 0x52, 0x99, 0x6e, 0x57, 0x00000100); //    VT_LPWSTR
+#endif
+
+namespace {
+struct com_heap_ptr_deleter {
+  void operator()(void * ptr) const noexcept {
+    CoTaskMemFree(ptr);
+  }
+};
+
+template <typename T>
+using com_heap_ptr = std::unique_ptr<T, com_heap_ptr_deleter>;
+
+template<typename T, size_t N>
+constexpr size_t
+ARRAY_LENGTH(T(&)[N])
+{
+  return N;
+}
+
+template <typename T>
+class no_addref_release : public T {
+  ULONG STDMETHODCALLTYPE AddRef() = 0;
+  ULONG STDMETHODCALLTYPE Release() = 0;
+};
+
+template <typename T>
+class com_ptr {
+public:
+  com_ptr() noexcept = default;
+
+  com_ptr(com_ptr const & other) noexcept = delete;
+  com_ptr & operator=(com_ptr const & other) noexcept = delete;
+  T ** operator&() const noexcept = delete;
+
+  ~com_ptr() noexcept {
+    release();
+  }
+
+  com_ptr(com_ptr && other) noexcept
+    : ptr(other.ptr)
+  {
+    other.ptr = nullptr;
+  }
+
+  com_ptr & operator=(com_ptr && other) noexcept {
+    if (ptr != other.ptr) {
+      release();
+      ptr = other.ptr;
+      other.ptr = nullptr;
+    }
+    return *this;
+  }
+
+  explicit operator bool() const noexcept {
+    return nullptr != ptr;
+  }
+
+  no_addref_release<T> * operator->() const noexcept {
+    return static_cast<no_addref_release<T> *>(ptr);
+  }
+
+  T * get() const noexcept {
+    return ptr;
+  }
+
+  T ** receive() noexcept {
+    XASSERT(ptr == nullptr);
+    return &ptr;
+  }
+
+  void ** receive_vpp() noexcept {
+    return reinterpret_cast<void **>(receive());
+  }
+
+  com_ptr & operator=(std::nullptr_t) noexcept {
+    release();
+    return *this;
+  }
+
+  void reset(T * p = nullptr) noexcept {
+    release();
+    ptr = p;
+  }
+
+private:
+  void release() noexcept {
+    T * temp = ptr;
+
+    if (temp) {
+      ptr = nullptr;
+      temp->Release();
+    }
+  }
+
+  T * ptr = nullptr;
+};
+
+extern cubeb_ops const wasapi_ops;
+
+int wasapi_stream_stop(cubeb_stream * stm);
+int wasapi_stream_start(cubeb_stream * stm);
+void close_wasapi_stream(cubeb_stream * stm);
+int setup_wasapi_stream(cubeb_stream * stm);
+ERole pref_to_role(cubeb_stream_prefs param);
+static char const * wstr_to_utf8(wchar_t const * str);
+static std::unique_ptr<wchar_t const []> utf8_to_wstr(char const * str);
+
+}
+
+class wasapi_collection_notification_client;
+class monitor_device_notifications;
+
+struct cubeb {
+  cubeb_ops const * ops = &wasapi_ops;
+  cubeb_strings * device_ids;
+  /* Device enumerator to get notifications when the
+     device collection change. */
+  com_ptr<IMMDeviceEnumerator> device_collection_enumerator;
+  com_ptr<wasapi_collection_notification_client> collection_notification_client;
+  /* Collection changed for input (capture) devices. */
+  cubeb_device_collection_changed_callback input_collection_changed_callback = nullptr;
+  void * input_collection_changed_user_ptr = nullptr;
+  /* Collection changed for output (render) devices. */
+  cubeb_device_collection_changed_callback output_collection_changed_callback = nullptr;
+  void * output_collection_changed_user_ptr = nullptr;
+};
+
+class wasapi_endpoint_notification_client;
+
+/* We have three possible callbacks we can use with a stream:
+ * - input only
+ * - output only
+ * - synchronized input and output
+ *
+ * Returns true when we should continue to play, false otherwise.
+ */
+typedef bool (*wasapi_refill_callback)(cubeb_stream * stm);
+
+struct cubeb_stream {
+  /* Note: Must match cubeb_stream layout in cubeb.c. */
+  cubeb * context = nullptr;
+  void * user_ptr = nullptr;
+  /**/
+
+  /* Mixer pameters. We need to convert the input stream to this
+     samplerate/channel layout, as WASAPI does not resample nor upmix
+     itself. */
+  cubeb_stream_params input_mix_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE };
+  cubeb_stream_params output_mix_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE };
+  /* Stream parameters. This is what the client requested,
+   * and what will be presented in the callback. */
+  cubeb_stream_params input_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE };
+  cubeb_stream_params output_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE };
+  /* A MMDevice role for this stream: either communication or console here. */
+  ERole role;
+  /* The input and output device, or NULL for default. */
+  std::unique_ptr<const wchar_t[]> input_device;
+  std::unique_ptr<const wchar_t[]> output_device;
+  /* The latency initially requested for this stream, in frames. */
+  unsigned latency = 0;
+  cubeb_state_callback state_callback = nullptr;
+  cubeb_data_callback data_callback = nullptr;
+  wasapi_refill_callback refill_callback = nullptr;
+  /* True when a loopback device is requested with no output device. In this
+     case a dummy output device is opened to drive the loopback, but should not
+     be exposed. */
+  bool has_dummy_output = false;
+  /* Lifetime considerations:
+     - client, render_client, audio_clock and audio_stream_volume are interface
+       pointer to the IAudioClient.
+     - The lifetime for device_enumerator and notification_client, resampler,
+       mix_buffer are the same as the cubeb_stream instance. */
+
+  /* Main handle on the WASAPI stream. */
+  com_ptr<IAudioClient> output_client;
+  /* Interface pointer to use the event-driven interface. */
+  com_ptr<IAudioRenderClient> render_client;
+  /* Interface pointer to use the volume facilities. */
+  com_ptr<IAudioStreamVolume> audio_stream_volume;
+  /* Interface pointer to use the stream audio clock. */
+  com_ptr<IAudioClock> audio_clock;
+  /* Frames written to the stream since it was opened. Reset on device
+     change. Uses mix_params.rate. */
+  UINT64 frames_written = 0;
+  /* Frames written to the (logical) stream since it was first
+     created. Updated on device change. Uses stream_params.rate. */
+  UINT64 total_frames_written = 0;
+  /* Last valid reported stream position.  Used to ensure the position
+     reported by stream_get_position increases monotonically. */
+  UINT64 prev_position = 0;
+  /* Device enumerator to be able to be notified when the default
+     device change. */
+  com_ptr<IMMDeviceEnumerator> device_enumerator;
+  /* Device notification client, to be able to be notified when the default
+     audio device changes and route the audio to the new default audio output
+     device */
+  com_ptr<wasapi_endpoint_notification_client> notification_client;
+  /* Main andle to the WASAPI capture stream. */
+  com_ptr<IAudioClient> input_client;
+  /* Interface to use the event driven capture interface */
+  com_ptr<IAudioCaptureClient> capture_client;
+  /* This event is set by the stream_stop and stream_destroy
+     function, so the render loop can exit properly. */
+  HANDLE shutdown_event = 0;
+  /* Set by OnDefaultDeviceChanged when a stream reconfiguration is required.
+     The reconfiguration is handled by the render loop thread. */
+  HANDLE reconfigure_event = 0;
+  /* This is set by WASAPI when we should refill the stream. */
+  HANDLE refill_event = 0;
+  /* This is set by WASAPI when we should read from the input stream. In
+   * practice, we read from the input stream in the output callback, so
+   * this is not used, but it is necessary to start getting input data. */
+  HANDLE input_available_event = 0;
+  /* Each cubeb_stream has its own thread. */
+  HANDLE thread = 0;
+  /* The lock protects all members that are touched by the render thread or
+     change during a device reset, including: audio_clock, audio_stream_volume,
+     client, frames_written, mix_params, total_frames_written, prev_position. */
+  owned_critical_section stream_reset_lock;
+  /* Maximum number of frames that can be passed down in a callback. */
+  uint32_t input_buffer_frame_count = 0;
+  /* Maximum number of frames that can be requested in a callback. */
+  uint32_t output_buffer_frame_count = 0;
+  /* Resampler instance. Resampling will only happen if necessary. */
+  std::unique_ptr<cubeb_resampler, decltype(&cubeb_resampler_destroy)> resampler = { nullptr, cubeb_resampler_destroy };
+  /* Mixer interfaces */
+  std::unique_ptr<cubeb_mixer, decltype(&cubeb_mixer_destroy)> output_mixer = { nullptr, cubeb_mixer_destroy };
+  std::unique_ptr<cubeb_mixer, decltype(&cubeb_mixer_destroy)> input_mixer = { nullptr, cubeb_mixer_destroy };
+  /* A buffer for up/down mixing multi-channel audio output. */
+  std::vector<BYTE> mix_buffer;
+  /* WASAPI input works in "packets". We re-linearize the audio packets
+   * into this buffer before handing it to the resampler. */
+  std::unique_ptr<auto_array_wrapper> linear_input_buffer;
+  /* Bytes per sample. This multiplied by the number of channels is the number
+   * of bytes per frame. */
+  size_t bytes_per_sample = 0;
+  /* WAVEFORMATEXTENSIBLE sub-format: either PCM or float. */
+  GUID waveformatextensible_sub_format = GUID_NULL;
+  /* Stream volume.  Set via stream_set_volume and used to reset volume on
+     device changes. */
+  float volume = 1.0;
+  /* True if the stream is draining. */
+  bool draining = false;
+  /* True when we've destroyed the stream. This pointer is leaked on stream
+   * destruction if we could not join the thread. */
+  std::atomic<std::atomic<bool>*> emergency_bailout { nullptr };
+  /* Synchronizes render thread start to ensure safe access to emergency_bailout. */
+  HANDLE thread_ready_event = 0;
+};
+
+class monitor_device_notifications {
+public:
+  monitor_device_notifications(cubeb * context)
+  : cubeb_context(context)
+  {
+    create_thread();
+  }
+
+  ~monitor_device_notifications()
+  {
+    SetEvent(shutdown);
+    WaitForSingleObject(thread, INFINITE);
+    CloseHandle(thread);
+
+    CloseHandle(input_changed);
+    CloseHandle(output_changed);
+    CloseHandle(shutdown);
+  }
+
+  void notify(EDataFlow flow)
+  {
+    XASSERT(cubeb_context);
+    if (flow == eCapture && cubeb_context->input_collection_changed_callback) {
+      bool res = SetEvent(input_changed);
+      if (!res) {
+        LOG("Failed to set input changed event");
+      }
+      return;
+    }
+    if (flow == eRender && cubeb_context->output_collection_changed_callback) {
+      bool res = SetEvent(output_changed);
+      if (!res) {
+        LOG("Failed to set output changed event");
+      }
+    }
+  }
+private:
+  static unsigned int __stdcall
+  thread_proc(LPVOID args)
+  {
+    XASSERT(args);
+    static_cast<monitor_device_notifications*>(args)
+      ->notification_thread_loop();
+    return 0;
+  }
+
+  void notification_thread_loop()
+  {
+    struct auto_com {
+      auto_com() {
+        HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
+        XASSERT(SUCCEEDED(hr));
+      }
+      ~auto_com() {
+        CoUninitialize();
+      }
+    } com;
+
+    HANDLE wait_array[3] = {
+      input_changed,
+      output_changed,
+      shutdown,
+    };
+
+    while (true) {
+      Sleep(200);
+
+      DWORD wait_result = WaitForMultipleObjects(ARRAY_LENGTH(wait_array),
+                                                 wait_array,
+                                                 FALSE,
+                                                 INFINITE);
+      if (wait_result == WAIT_OBJECT_0) { // input changed
+        cubeb_context->input_collection_changed_callback(cubeb_context,
+          cubeb_context->input_collection_changed_user_ptr);
+      } else if (wait_result == WAIT_OBJECT_0 + 1) { // output changed
+        cubeb_context->output_collection_changed_callback(cubeb_context,
+          cubeb_context->output_collection_changed_user_ptr);
+      } else if (wait_result == WAIT_OBJECT_0 + 2) { // shutdown
+        break;
+      } else {
+        LOG("Unexpected result %lu", wait_result);
+      }
+    } // loop
+  }
+
+  void create_thread()
+  {
+    output_changed = CreateEvent(nullptr, 0, 0, nullptr);
+    if (!output_changed) {
+      LOG("Failed to create output changed event.");
+      return;
+    }
+
+    input_changed = CreateEvent(nullptr, 0, 0, nullptr);
+    if (!input_changed) {
+      LOG("Failed to create input changed event.");
+      return;
+    }
+
+    shutdown = CreateEvent(nullptr, 0, 0, nullptr);
+    if (!shutdown) {
+      LOG("Failed to create shutdown event.");
+      return;
+    }
+
+    thread = (HANDLE) _beginthreadex(nullptr,
+                                     256 * 1024,
+                                     thread_proc,
+                                     this,
+                                     STACK_SIZE_PARAM_IS_A_RESERVATION,
+                                     nullptr);
+    if (!thread) {
+      LOG("Failed to create thread.");
+      return;
+    }
+  }
+
+  HANDLE thread = INVALID_HANDLE_VALUE;
+  HANDLE output_changed = INVALID_HANDLE_VALUE;
+  HANDLE input_changed = INVALID_HANDLE_VALUE;
+  HANDLE shutdown = INVALID_HANDLE_VALUE;
+
+  cubeb * cubeb_context = nullptr;
+};
+
+class wasapi_collection_notification_client : public IMMNotificationClient
+{
+public:
+  /* The implementation of MSCOM was copied from MSDN. */
+  ULONG STDMETHODCALLTYPE
+  AddRef()
+  {
+    return InterlockedIncrement(&ref_count);
+  }
+
+  ULONG STDMETHODCALLTYPE
+  Release()
+  {
+    ULONG ulRef = InterlockedDecrement(&ref_count);
+    if (0 == ulRef) {
+      delete this;
+    }
+    return ulRef;
+  }
+
+  HRESULT STDMETHODCALLTYPE
+  QueryInterface(REFIID riid, VOID **ppvInterface)
+  {
+    if (__uuidof(IUnknown) == riid) {
+      AddRef();
+      *ppvInterface = (IUnknown*)this;
+    } else if (__uuidof(IMMNotificationClient) == riid) {
+      AddRef();
+      *ppvInterface = (IMMNotificationClient*)this;
+    } else {
+      *ppvInterface = NULL;
+      return E_NOINTERFACE;
+    }
+    return S_OK;
+  }
+
+  wasapi_collection_notification_client(cubeb * context)
+    : ref_count(1)
+    , cubeb_context(context)
+    , monitor_notifications(context)
+  {
+    XASSERT(cubeb_context);
+  }
+
+  virtual ~wasapi_collection_notification_client()
+  { }
+
+  HRESULT STDMETHODCALLTYPE
+  OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR device_id)
+  {
+    LOG("collection: Audio device default changed, id = %S.", device_id);
+    return S_OK;
+  }
+
+  /* The remaining methods are not implemented, they simply log when called (if
+     log is enabled), for debugging. */
+  HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR device_id)
+  {
+    LOG("collection: Audio device added.");
+    return S_OK;
+  };
+
+  HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR device_id)
+  {
+    LOG("collection: Audio device removed.");
+    return S_OK;
+  }
+
+  HRESULT STDMETHODCALLTYPE
+  OnDeviceStateChanged(LPCWSTR device_id, DWORD new_state)
+  {
+    XASSERT(cubeb_context->output_collection_changed_callback ||
+            cubeb_context->input_collection_changed_callback);
+    LOG("collection: Audio device state changed, id = %S, state = %lu.", device_id, new_state);
+    EDataFlow flow;
+    HRESULT hr = GetDataFlow(device_id, &flow);
+    if (FAILED(hr)) {
+      return hr;
+    }
+    monitor_notifications.notify(flow);
+    return S_OK;
+  }
+
+  HRESULT STDMETHODCALLTYPE
+  OnPropertyValueChanged(LPCWSTR device_id, const PROPERTYKEY key)
+  {
+    //Audio device property value changed.
+    return S_OK;
+  }
+
+private:
+  HRESULT GetDataFlow(LPCWSTR device_id, EDataFlow * flow)
+  {
+    com_ptr<IMMDevice> device;
+    com_ptr<IMMEndpoint> endpoint;
+
+    HRESULT hr = cubeb_context->device_collection_enumerator
+                   ->GetDevice(device_id, device.receive());
+    if (FAILED(hr)) {
+      LOG("collection: Could not get device: %lx", hr);
+      return hr;
+    }
+
+    hr = device->QueryInterface(IID_PPV_ARGS(endpoint.receive()));
+    if (FAILED(hr)) {
+      LOG("collection: Could not get endpoint: %lx", hr);
+      return hr;
+    }
+
+    return endpoint->GetDataFlow(flow);
+  }
+
+  /* refcount for this instance, necessary to implement MSCOM semantics. */
+  LONG ref_count;
+
+  cubeb * cubeb_context = nullptr;
+  monitor_device_notifications monitor_notifications;
+};
+
+class wasapi_endpoint_notification_client : public IMMNotificationClient
+{
+public:
+  /* The implementation of MSCOM was copied from MSDN. */
+  ULONG STDMETHODCALLTYPE
+  AddRef()
+  {
+    return InterlockedIncrement(&ref_count);
+  }
+
+  ULONG STDMETHODCALLTYPE
+  Release()
+  {
+    ULONG ulRef = InterlockedDecrement(&ref_count);
+    if (0 == ulRef) {
+      delete this;
+    }
+    return ulRef;
+  }
+
+  HRESULT STDMETHODCALLTYPE
+  QueryInterface(REFIID riid, VOID **ppvInterface)
+  {
+    if (__uuidof(IUnknown) == riid) {
+      AddRef();
+      *ppvInterface = (IUnknown*)this;
+    } else if (__uuidof(IMMNotificationClient) == riid) {
+      AddRef();
+      *ppvInterface = (IMMNotificationClient*)this;
+    } else {
+      *ppvInterface = NULL;
+      return E_NOINTERFACE;
+    }
+    return S_OK;
+  }
+
+  wasapi_endpoint_notification_client(HANDLE event, ERole role)
+    : ref_count(1)
+    , reconfigure_event(event)
+    , role(role)
+  { }
+
+  virtual ~wasapi_endpoint_notification_client()
+  { }
+
+  HRESULT STDMETHODCALLTYPE
+  OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR device_id)
+  {
+    LOG("endpoint: Audio device default changed.");
+
+    /* we only support a single stream type for now. */
+    if (flow != eRender && role != this->role) {
+      return S_OK;
+    }
+
+    BOOL ok = SetEvent(reconfigure_event);
+    if (!ok) {
+      LOG("endpoint: SetEvent on reconfigure_event failed: %lx", GetLastError());
+    }
+
+    return S_OK;
+  }
+
+  /* The remaining methods are not implemented, they simply log when called (if
+     log is enabled), for debugging. */
+  HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR device_id)
+  {
+    LOG("endpoint: Audio device added.");
+    return S_OK;
+  };
+
+  HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR device_id)
+  {
+    LOG("endpoint: Audio device removed.");
+    return S_OK;
+  }
+
+  HRESULT STDMETHODCALLTYPE
+  OnDeviceStateChanged(LPCWSTR device_id, DWORD new_state)
+  {
+    LOG("endpoint: Audio device state changed.");
+    return S_OK;
+  }
+
+  HRESULT STDMETHODCALLTYPE
+  OnPropertyValueChanged(LPCWSTR device_id, const PROPERTYKEY key)
+  {
+    //Audio device property value changed.
+    return S_OK;
+  }
+private:
+  /* refcount for this instance, necessary to implement MSCOM semantics. */
+  LONG ref_count;
+  HANDLE reconfigure_event;
+  ERole role;
+};
+
+namespace {
+
+char const *
+intern_device_id(cubeb * ctx, wchar_t const * id)
+{
+  XASSERT(id);
+
+  char const * tmp = wstr_to_utf8(id);
+  if (!tmp)
+    return nullptr;
+
+  char const * interned = cubeb_strings_intern(ctx->device_ids, tmp);
+
+  free((void *) tmp);
+
+  return interned;
+}
+
+bool has_input(cubeb_stream * stm)
+{
+  return stm->input_stream_params.rate != 0;
+}
+
+bool has_output(cubeb_stream * stm)
+{
+  return stm->output_stream_params.rate != 0;
+}
+
+double stream_to_mix_samplerate_ratio(cubeb_stream_params & stream, cubeb_stream_params & mixer)
+{
+  return double(stream.rate) / mixer.rate;
+}
+
+/* Convert the channel layout into the corresponding KSAUDIO_CHANNEL_CONFIG.
+   See more: https://msdn.microsoft.com/en-us/library/windows/hardware/ff537083(v=vs.85).aspx */
+
+cubeb_channel_layout
+mask_to_channel_layout(WAVEFORMATEX const * fmt)
+{
+  cubeb_channel_layout mask = 0;
+
+  if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
+    WAVEFORMATEXTENSIBLE const * ext = reinterpret_cast<WAVEFORMATEXTENSIBLE const *>(fmt);
+    mask = ext->dwChannelMask;
+  } else if (fmt->wFormatTag == WAVE_FORMAT_PCM ||
+             fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
+    if (fmt->nChannels == 1) {
+      mask = CHANNEL_FRONT_CENTER;
+    } else if (fmt->nChannels == 2) {
+      mask = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT;
+    }
+  }
+  return mask;
+}
+
+uint32_t
+get_rate(cubeb_stream * stm)
+{
+  return has_input(stm) ? stm->input_stream_params.rate
+                        : stm->output_stream_params.rate;
+}
+
+uint32_t
+hns_to_frames(uint32_t rate, REFERENCE_TIME hns)
+{
+  return std::ceil((hns - 1) / 10000000.0 * rate);
+}
+
+uint32_t
+hns_to_frames(cubeb_stream * stm, REFERENCE_TIME hns)
+{
+  return hns_to_frames(get_rate(stm), hns);
+}
+
+REFERENCE_TIME
+frames_to_hns(cubeb_stream * stm, uint32_t frames)
+{
+  return std::ceil(frames * 10000000.0 / get_rate(stm));
+}
+
+/* This returns the size of a frame in the stream, before the eventual upmix
+   occurs. */
+static size_t
+frames_to_bytes_before_mix(cubeb_stream * stm, size_t frames)
+{
+  // This is called only when we has a output client.
+  XASSERT(has_output(stm));
+  return stm->output_stream_params.channels * stm->bytes_per_sample * frames;
+}
+
+/* This function handles the processing of the input and output audio,
+ * converting it to rate and channel layout specified at initialization.
+ * It then calls the data callback, via the resampler. */
+long
+refill(cubeb_stream * stm, void * input_buffer, long input_frames_count,
+       void * output_buffer, long output_frames_needed)
+{
+  XASSERT(!stm->draining);
+  /* If we need to upmix after resampling, resample into the mix buffer to
+     avoid a copy. Avoid exposing output if it is a dummy stream. */
+  void * dest = nullptr;
+  if (has_output(stm) && !stm->has_dummy_output) {
+    if (stm->output_mixer) {
+      dest = stm->mix_buffer.data();
+    } else {
+      dest = output_buffer;
+    }
+  }
+
+  long out_frames = cubeb_resampler_fill(stm->resampler.get(),
+                                         input_buffer,
+                                         &input_frames_count,
+                                         dest,
+                                         output_frames_needed);
+  /* TODO: Report out_frames < 0 as an error via the API. */
+  XASSERT(out_frames >= 0);
+
+  {
+    auto_lock lock(stm->stream_reset_lock);
+    stm->frames_written += out_frames;
+  }
+
+  /* Go in draining mode if we got fewer frames than requested. If the stream
+     has no output we still expect the callback to return number of frames read
+     from input, otherwise we stop. */
+  if ((out_frames < output_frames_needed) ||
+      (!has_output(stm) && out_frames < input_frames_count)) {
+    LOG("start draining.");
+    stm->draining = true;
+  }
+
+  /* If this is not true, there will be glitches.
+     It is alright to have produced less frames if we are draining, though. */
+  XASSERT(out_frames == output_frames_needed || stm->draining || !has_output(stm) || stm->has_dummy_output);
+
+  // We don't bother mixing dummy output as it will be silenced, otherwise mix output if needed
+  if (!stm->has_dummy_output && has_output(stm) && stm->output_mixer) {
+    XASSERT(dest == stm->mix_buffer.data());
+    size_t dest_size =
+      out_frames * stm->output_stream_params.channels * stm->bytes_per_sample;
+    XASSERT(dest_size <= stm->mix_buffer.size());
+    size_t output_buffer_size =
+      out_frames * stm->output_mix_params.channels * stm->bytes_per_sample;
+    int ret = cubeb_mixer_mix(stm->output_mixer.get(),
+                              out_frames,
+                              dest,
+                              dest_size,
+                              output_buffer,
+                              output_buffer_size);
+    if (ret < 0) {
+      LOG("Error remixing content (%d)", ret);
+    }
+  }
+
+  return out_frames;
+}
+
+int wasapi_stream_reset_default_device(cubeb_stream * stm);
+
+/* This helper grabs all the frames available from a capture client, put them in
+ * linear_input_buffer. linear_input_buffer should be cleared before the
+ * callback exits. This helper does not work with exclusive mode streams. */
+bool get_input_buffer(cubeb_stream * stm)
+{
+  XASSERT(has_input(stm));
+
+  HRESULT hr;
+  BYTE * input_packet = NULL;
+  DWORD flags;
+  UINT64 dev_pos;
+  UINT32 next;
+  /* Get input packets until we have captured enough frames, and put them in a
+   * contiguous buffer. */
+  uint32_t offset = 0;
+  // If the input stream is event driven we should only ever expect to read a
+  // single packet each time. However, if we're pulling from the stream we may
+  // need to grab multiple packets worth of frames that have accumulated (so
+  // need a loop).
+  for (hr = stm->capture_client->GetNextPacketSize(&next);
+       next > 0;
+       hr = stm->capture_client->GetNextPacketSize(&next)) {
+    if (hr == AUDCLNT_E_DEVICE_INVALIDATED) {
+      // Application can recover from this error. More info
+      // https://msdn.microsoft.com/en-us/library/windows/desktop/dd316605(v=vs.85).aspx
+      LOG("Device invalidated error, reset default device");
+      wasapi_stream_reset_default_device(stm);
+      return true;
+    }
+
+    if (FAILED(hr)) {
+      LOG("cannot get next packet size: %lx", hr);
+      return false;
+    }
+
+    UINT32 frames;
+    hr = stm->capture_client->GetBuffer(&input_packet,
+                                        &frames,
+                                        &flags,
+                                        &dev_pos,
+                                        NULL);
+    if (FAILED(hr)) {
+      LOG("GetBuffer failed for capture: %lx", hr);
+      return false;
+    }
+    XASSERT(frames == next);
+
+    UINT32 input_stream_samples = frames * stm->input_stream_params.channels;
+    // We do not explicitly handle the AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY
+    // flag. There a two primary (non exhaustive) scenarios we anticipate this
+    // flag being set in:
+    //   - The first GetBuffer after Start has this flag undefined. In this
+    //     case the flag may be set but is meaningless and can be ignored.
+    //   - If a glitch is introduced into the input. This should not happen
+    //     for event based inputs, and should be mitigated by using a dummy
+    //     stream to drive input in the case of input only loopback. Without
+    //     a dummy output, input only loopback would glitch on silence. However,
+    //     the dummy input should push silence to the loopback and prevent
+    //     discontinuities. See https://blogs.msdn.microsoft.com/matthew_van_eerde/2008/12/16/sample-wasapi-loopback-capture-record-what-you-hear/
+    // As the first scenario can be ignored, and we anticipate the second
+    // scenario is mitigated, we ignore the flag.
+    // For more info: https://msdn.microsoft.com/en-us/library/windows/desktop/dd370859(v=vs.85).aspx,
+    // https://msdn.microsoft.com/en-us/library/windows/desktop/dd371458(v=vs.85).aspx
+    if (flags & AUDCLNT_BUFFERFLAGS_SILENT) {
+      LOG("insert silence: ps=%u", frames);
+      stm->linear_input_buffer->push_silence(input_stream_samples);
+    } else {
+      if (stm->input_mixer) {
+        bool ok = stm->linear_input_buffer->reserve(
+          stm->linear_input_buffer->length() + input_stream_samples);
+        XASSERT(ok);
+        size_t input_packet_size =
+          frames * stm->input_mix_params.channels *
+          cubeb_sample_size(stm->input_mix_params.format);
+        size_t linear_input_buffer_size =
+          input_stream_samples *
+          cubeb_sample_size(stm->input_stream_params.format);
+        cubeb_mixer_mix(stm->input_mixer.get(),
+                        frames,
+                        input_packet,
+                        input_packet_size,
+                        stm->linear_input_buffer->end(),
+                        linear_input_buffer_size);
+        stm->linear_input_buffer->set_length(
+          stm->linear_input_buffer->length() + input_stream_samples);
+      } else {
+        stm->linear_input_buffer->push(
+          input_packet, input_stream_samples);
+      }
+    }
+    hr = stm->capture_client->ReleaseBuffer(frames);
+    if (FAILED(hr)) {
+      LOG("FAILED to release intput buffer");
+      return false;
+    }
+    offset += input_stream_samples;
+  }
+
+  XASSERT(stm->linear_input_buffer->length() >= offset);
+
+  return true;
+}
+
+/* Get an output buffer from the render_client. It has to be released before
+ * exiting the callback. */
+bool get_output_buffer(cubeb_stream * stm, void *& buffer, size_t & frame_count)
+{
+  UINT32 padding_out;
+  HRESULT hr;
+
+  XASSERT(has_output(stm));
+
+  hr = stm->output_client->GetCurrentPadding(&padding_out);
+  if (hr == AUDCLNT_E_DEVICE_INVALIDATED) {
+      // Application can recover from this error. More info
+      // https://msdn.microsoft.com/en-us/library/windows/desktop/dd316605(v=vs.85).aspx
+      LOG("Device invalidated error, reset default device");
+      wasapi_stream_reset_default_device(stm);
+      return true;
+  }
+
+  if (FAILED(hr)) {
+    LOG("Failed to get padding: %lx", hr);
+    return false;
+  }
+
+  XASSERT(padding_out <= stm->output_buffer_frame_count);
+
+  if (stm->draining) {
+    if (padding_out == 0) {
+      LOG("Draining finished.");
+      stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
+      return false;
+    }
+    LOG("Draining.");
+    return true;
+  }
+
+  frame_count = stm->output_buffer_frame_count - padding_out;
+  BYTE * output_buffer;
+
+  hr = stm->render_client->GetBuffer(frame_count, &output_buffer);
+  if (FAILED(hr)) {
+    LOG("cannot get render buffer");
+    return false;
+  }
+
+  buffer = output_buffer;
+
+  return true;
+}
+
+/**
+ * This function gets input data from a input device, and pass it along with an
+ * output buffer to the resamplers.  */
+bool
+refill_callback_duplex(cubeb_stream * stm)
+{
+  HRESULT hr;
+  void * output_buffer = nullptr;
+  size_t output_frames = 0;
+  size_t input_frames;
+  bool rv;
+
+  XASSERT(has_input(stm) && has_output(stm));
+
+  rv = get_input_buffer(stm);
+  if (!rv) {
+    return rv;
+  }
+
+  input_frames = stm->linear_input_buffer->length() / stm->input_stream_params.channels;
+  if (!input_frames) {
+    return true;
+  }
+
+  rv = get_output_buffer(stm, output_buffer, output_frames);
+  if (!rv) {
+    hr = stm->render_client->ReleaseBuffer(output_frames, 0);
+    return rv;
+  }
+
+  /* This can only happen when debugging, and having breakpoints set in the
+   * callback in a way that it makes the stream underrun. */
+  if (output_frames == 0) {
+    return true;
+  }
+
+  /* Wait for draining is not important on duplex. */
+  if (stm->draining) {
+    return false;
+  }
+
+  if (stm->has_dummy_output) {
+    ALOGV("Duplex callback (dummy output): input frames: %Iu, output frames: %Iu",
+          input_frames, output_frames);
+
+    // We don't want to expose the dummy output to the callback so don't pass
+    // the output buffer (it will be released later with silence in it)
+    refill(stm,
+           stm->linear_input_buffer->data(),
+           input_frames,
+           nullptr,
+           0);
+  } else {
+    ALOGV("Duplex callback: input frames: %Iu, output frames: %Iu",
+          input_frames, output_frames);
+
+    refill(stm,
+           stm->linear_input_buffer->data(),
+           input_frames,
+           output_buffer,
+           output_frames);
+  }
+
+  stm->linear_input_buffer->clear();
+
+  if (stm->has_dummy_output) {
+    // If output is a dummy output, make sure it's silent
+    hr = stm->render_client->ReleaseBuffer(output_frames, AUDCLNT_BUFFERFLAGS_SILENT);
+  } else {
+    hr = stm->render_client->ReleaseBuffer(output_frames, 0);
+  }
+  if (FAILED(hr)) {
+    LOG("failed to release buffer: %lx", hr);
+    return false;
+  }
+  return true;
+}
+
+bool
+refill_callback_input(cubeb_stream * stm)
+{
+  bool rv;
+  size_t input_frames;
+
+  XASSERT(has_input(stm) && !has_output(stm));
+
+  rv = get_input_buffer(stm);
+  if (!rv) {
+    return rv;
+  }
+
+  input_frames = stm->linear_input_buffer->length() / stm->input_stream_params.channels;
+  if (!input_frames) {
+    return true;
+  }
+
+  ALOGV("Input callback: input frames: %Iu", input_frames);
+
+  long read = refill(stm,
+                     stm->linear_input_buffer->data(),
+                     input_frames,
+                     nullptr,
+                     0);
+
+  XASSERT(read >= 0);
+
+  stm->linear_input_buffer->clear();
+
+  return !stm->draining;
+}
+
+bool
+refill_callback_output(cubeb_stream * stm)
+{
+  bool rv;
+  HRESULT hr;
+  void * output_buffer = nullptr;
+  size_t output_frames = 0;
+
+  XASSERT(!has_input(stm) && has_output(stm));
+
+  rv = get_output_buffer(stm, output_buffer, output_frames);
+  if (!rv) {
+    return rv;
+  }
+
+  if (stm->draining || output_frames == 0) {
+    return true;
+  }
+
+  long got = refill(stm,
+                    nullptr,
+                    0,
+                    output_buffer,
+                    output_frames);
+
+  ALOGV("Output callback: output frames requested: %Iu, got %ld",
+        output_frames, got);
+
+  XASSERT(got >= 0);
+  XASSERT(size_t(got) == output_frames || stm->draining);
+
+  hr = stm->render_client->ReleaseBuffer(got, 0);
+  if (FAILED(hr)) {
+    LOG("failed to release buffer: %lx", hr);
+    return false;
+  }
+
+  return size_t(got) == output_frames || stm->draining;
+}
+
+static unsigned int __stdcall
+wasapi_stream_render_loop(LPVOID stream)
+{
+  cubeb_stream * stm = static_cast<cubeb_stream *>(stream);
+  std::atomic<bool> * emergency_bailout = stm->emergency_bailout;
+
+  // Signal wasapi_stream_start that we've copied emergency_bailout.
+  BOOL ok = SetEvent(stm->thread_ready_event);
+  if (!ok) {
+    LOG("thread_ready SetEvent failed: %lx", GetLastError());
+    return 0;
+  }
+
+  bool is_playing = true;
+  HANDLE wait_array[4] = {
+    stm->shutdown_event,
+    stm->reconfigure_event,
+    stm->refill_event,
+    stm->input_available_event
+  };
+  HANDLE mmcss_handle = NULL;
+  HRESULT hr = 0;
+  DWORD mmcss_task_index = 0;
+  struct auto_com {
+    auto_com() {
+      HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
+      XASSERT(SUCCEEDED(hr));
+    }
+    ~auto_com() {
+      CoUninitialize();
+    }
+  } com;
+
+  /* We could consider using "Pro Audio" here for WebAudio and
+     maybe WebRTC. */
+  mmcss_handle = AvSetMmThreadCharacteristicsA("Audio", &mmcss_task_index);
+  if (!mmcss_handle) {
+    /* This is not fatal, but we might glitch under heavy load. */
+    LOG("Unable to use mmcss to bump the render thread priority: %lx", GetLastError());
+  }
+
+  /* WaitForMultipleObjects timeout can trigger in cases where we don't want to
+     treat it as a timeout, such as across a system sleep/wake cycle.  Trigger
+     the timeout error handling only when the timeout_limit is reached, which is
+     reset on each successful loop. */
+  unsigned timeout_count = 0;
+  const unsigned timeout_limit = 3;
+  while (is_playing) {
+    // We want to check the emergency bailout variable before a
+    // and after the WaitForMultipleObject, because the handles WaitForMultipleObjects
+    // is going to wait on might have been closed already.
+    if (*emergency_bailout) {
+      delete emergency_bailout;
+      return 0;
+    }
+    DWORD waitResult = WaitForMultipleObjects(ARRAY_LENGTH(wait_array),
+                                              wait_array,
+                                              FALSE,
+                                              1000);
+    if (*emergency_bailout) {
+      delete emergency_bailout;
+      return 0;
+    }
+    if (waitResult != WAIT_TIMEOUT) {
+      timeout_count = 0;
+    }
+    switch (waitResult) {
+    case WAIT_OBJECT_0: { /* shutdown */
+      is_playing = false;
+      /* We don't check if the drain is actually finished here, we just want to
+         shutdown. */
+      if (stm->draining) {
+        stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
+      }
+      continue;
+    }
+    case WAIT_OBJECT_0 + 1: { /* reconfigure */
+      XASSERT(stm->output_client || stm->input_client);
+      LOG("Reconfiguring the stream");
+      /* Close the stream */
+      if (stm->output_client) {
+        stm->output_client->Stop();
+        LOG("Output stopped.");
+      }
+      if (stm->input_client) {
+        stm->input_client->Stop();
+        LOG("Input stopped.");
+      }
+      {
+        auto_lock lock(stm->stream_reset_lock);
+        close_wasapi_stream(stm);
+        LOG("Stream closed.");
+        /* Reopen a stream and start it immediately. This will automatically pick the
+           new default device for this role. */
+        int r = setup_wasapi_stream(stm);
+        if (r != CUBEB_OK) {
+          LOG("Error setting up the stream during reconfigure.");
+          /* Don't destroy the stream here, since we expect the caller to do
+             so after the error has propagated via the state callback. */
+          is_playing = false;
+          hr = E_FAIL;
+          continue;
+        }
+        LOG("Stream setup successfuly.");
+      }
+      XASSERT(stm->output_client || stm->input_client);
+      if (stm->output_client) {
+        hr = stm->output_client->Start();
+        if (FAILED(hr)) {
+          LOG("Error starting output after reconfigure, error: %lx", hr);
+          is_playing = false;
+          continue;
+        }
+        LOG("Output started after reconfigure.");
+      }
+      if (stm->input_client) {
+        hr = stm->input_client->Start();
+        if (FAILED(hr)) {
+          LOG("Error starting input after reconfiguring, error: %lx", hr);
+          is_playing = false;
+          continue;
+        }
+        LOG("Input started after reconfigure.");
+      }
+      break;
+    }
+    case WAIT_OBJECT_0 + 2:  /* refill */
+      XASSERT((has_input(stm) && has_output(stm)) ||
+              (!has_input(stm) && has_output(stm)));
+      is_playing = stm->refill_callback(stm);
+      break;
+    case WAIT_OBJECT_0 + 3: /* input available */
+      if (has_input(stm) && has_output(stm)) { continue; }
+      is_playing = stm->refill_callback(stm);
+      break;
+    case WAIT_TIMEOUT:
+      XASSERT(stm->shutdown_event == wait_array[0]);
+      if (++timeout_count >= timeout_limit) {
+        LOG("Render loop reached the timeout limit.");
+        is_playing = false;
+        hr = E_FAIL;
+      }
+      break;
+    default:
+      LOG("case %lu not handled in render loop.", waitResult);
+      abort();
+    }
+  }
+
+  if (FAILED(hr)) {
+    stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
+  }
+
+  if (mmcss_handle) {
+    AvRevertMmThreadCharacteristics(mmcss_handle);
+  }
+
+  return 0;
+}
+
+void wasapi_destroy(cubeb * context);
+
+HRESULT register_notification_client(cubeb_stream * stm)
+{
+  HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
+                                NULL, CLSCTX_INPROC_SERVER,
+                                IID_PPV_ARGS(stm->device_enumerator.receive()));
+  if (FAILED(hr)) {
+    LOG("Could not get device enumerator: %lx", hr);
+    return hr;
+  }
+
+  stm->notification_client.reset(new wasapi_endpoint_notification_client(stm->reconfigure_event, stm->role));
+
+  hr = stm->device_enumerator->RegisterEndpointNotificationCallback(stm->notification_client.get());
+  if (FAILED(hr)) {
+    LOG("Could not register endpoint notification callback: %lx", hr);
+    stm->notification_client = nullptr;
+    stm->device_enumerator = nullptr;
+  }
+
+  return hr;
+}
+
+HRESULT unregister_notification_client(cubeb_stream * stm)
+{
+  XASSERT(stm);
+  HRESULT hr;
+
+  if (!stm->device_enumerator) {
+    return S_OK;
+  }
+
+  hr = stm->device_enumerator->UnregisterEndpointNotificationCallback(stm->notification_client.get());
+  if (FAILED(hr)) {
+    // We can't really do anything here, we'll probably leak the
+    // notification client, but we can at least release the enumerator.
+    stm->device_enumerator = nullptr;
+    return S_OK;
+  }
+
+  stm->notification_client = nullptr;
+  stm->device_enumerator = nullptr;
+
+  return S_OK;
+}
+
+HRESULT get_endpoint(com_ptr<IMMDevice> & device, LPCWSTR devid)
+{
+  com_ptr<IMMDeviceEnumerator> enumerator;
+  HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
+                                NULL, CLSCTX_INPROC_SERVER,
+                                IID_PPV_ARGS(enumerator.receive()));
+  if (FAILED(hr)) {
+    LOG("Could not get device enumerator: %lx", hr);
+    return hr;
+  }
+
+  hr = enumerator->GetDevice(devid, device.receive());
+  if (FAILED(hr)) {
+    LOG("Could not get device: %lx", hr);
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT register_collection_notification_client(cubeb * context)
+{
+  HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
+                                NULL, CLSCTX_INPROC_SERVER,
+                                IID_PPV_ARGS(context->device_collection_enumerator.receive()));
+  if (FAILED(hr)) {
+    LOG("Could not get device enumerator: %lx", hr);
+    return hr;
+  }
+
+  context->collection_notification_client.reset(new wasapi_collection_notification_client(context));
+
+  hr = context->device_collection_enumerator->RegisterEndpointNotificationCallback(
+                                                context->collection_notification_client.get());
+  if (FAILED(hr)) {
+    LOG("Could not register endpoint notification callback: %lx", hr);
+    context->collection_notification_client.reset();
+    context->device_collection_enumerator.reset();
+  }
+
+  return hr;
+}
+
+HRESULT unregister_collection_notification_client(cubeb * context)
+{
+  HRESULT hr = context->device_collection_enumerator->
+    UnregisterEndpointNotificationCallback(context->collection_notification_client.get());
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  context->collection_notification_client = nullptr;
+  context->device_collection_enumerator = nullptr;
+
+  return hr;
+}
+
+HRESULT get_default_endpoint(com_ptr<IMMDevice> & device, EDataFlow direction, ERole role)
+{
+  com_ptr<IMMDeviceEnumerator> enumerator;
+  HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
+                                NULL, CLSCTX_INPROC_SERVER,
+                                IID_PPV_ARGS(enumerator.receive()));
+  if (FAILED(hr)) {
+    LOG("Could not get device enumerator: %lx", hr);
+    return hr;
+  }
+  hr = enumerator->GetDefaultAudioEndpoint(direction, role, device.receive());
+  if (FAILED(hr)) {
+    LOG("Could not get default audio endpoint: %lx", hr);
+    return hr;
+  }
+
+  return ERROR_SUCCESS;
+}
+
+double
+current_stream_delay(cubeb_stream * stm)
+{
+  stm->stream_reset_lock.assert_current_thread_owns();
+
+  /* If the default audio endpoint went away during playback and we weren't
+     able to configure a new one, it's possible the caller may call this
+     before the error callback has propogated back. */
+  if (!stm->audio_clock) {
+    return 0;
+  }
+
+  UINT64 freq;
+  HRESULT hr = stm->audio_clock->GetFrequency(&freq);
+  if (FAILED(hr)) {
+    LOG("GetFrequency failed: %lx", hr);
+    return 0;
+  }
+
+  UINT64 pos;
+  hr = stm->audio_clock->GetPosition(&pos, NULL);
+  if (FAILED(hr)) {
+    LOG("GetPosition failed: %lx", hr);
+    return 0;
+  }
+
+  double cur_pos = static_cast<double>(pos) / freq;
+  double max_pos = static_cast<double>(stm->frames_written)  / stm->output_mix_params.rate;
+  double delay = max_pos - cur_pos;
+  XASSERT(delay >= 0);
+
+  return delay;
+}
+
+int
+stream_set_volume(cubeb_stream * stm, float volume)
+{
+  stm->stream_reset_lock.assert_current_thread_owns();
+
+  if (!stm->audio_stream_volume) {
+    return CUBEB_ERROR;
+  }
+
+  uint32_t channels;
+  HRESULT hr = stm->audio_stream_volume->GetChannelCount(&channels);
+  if (FAILED(hr)) {
+    LOG("could not get the channel count: %lx", hr);
+    return CUBEB_ERROR;
+  }
+
+  /* up to 9.1 for now */
+  if (channels > 10) {
+    return CUBEB_ERROR_NOT_SUPPORTED;
+  }
+
+  float volumes[10];
+  for (uint32_t i = 0; i < channels; i++) {
+    volumes[i] = volume;
+  }
+
+  hr = stm->audio_stream_volume->SetAllVolumes(channels,  volumes);
+  if (FAILED(hr)) {
+    LOG("could not set the channels volume: %lx", hr);
+    return CUBEB_ERROR;
+  }
+
+  return CUBEB_OK;
+}
+} // namespace anonymous
+
+extern "C" {
+int wasapi_init(cubeb ** context, char const * context_name)
+{
+  /* We don't use the device yet, but need to make sure we can initialize one
+     so that this backend is not incorrectly enabled on platforms that don't
+     support WASAPI. */
+  com_ptr<IMMDevice> device;
+  HRESULT hr = get_default_endpoint(device, eRender, eConsole);
+  if (FAILED(hr)) {
+    XASSERT(hr != CO_E_NOTINITIALIZED);
+    LOG("It wasn't able to find a default rendering device: %lx", hr);
+    hr = get_default_endpoint(device, eCapture, eConsole);
+    if (FAILED(hr)) {
+      LOG("It wasn't able to find a default capture device: %lx", hr);
+      return CUBEB_ERROR;
+    }
+  }
+
+  cubeb * ctx = new cubeb();
+
+  ctx->ops = &wasapi_ops;
+  if (cubeb_strings_init(&ctx->device_ids) != CUBEB_OK) {
+    delete ctx;
+    return CUBEB_ERROR;
+  }
+
+  *context = ctx;
+
+  return CUBEB_OK;
+}
+}
+
+namespace {
+bool stop_and_join_render_thread(cubeb_stream * stm)
+{
+  bool rv = true;
+  LOG("Stop and join render thread.");
+  if (!stm->thread) {
+    LOG("No thread present.");
+    return true;
+  }
+
+  // If we've already leaked the thread, just return,
+  // there is not much we can do.
+  if (!stm->emergency_bailout.load()) {
+    return false;
+  }
+
+  BOOL ok = SetEvent(stm->shutdown_event);
+  if (!ok) {
+    LOG("Destroy SetEvent failed: %lx", GetLastError());
+  }
+
+  /* Wait five seconds for the rendering thread to return. It's supposed to
+   * check its event loop very often, five seconds is rather conservative. */
+  DWORD r = WaitForSingleObject(stm->thread, 5000);
+  if (r != WAIT_OBJECT_0) {
+    /* Something weird happened, leak the thread and continue the shutdown
+     * process. */
+    *(stm->emergency_bailout) = true;
+    // We give the ownership to the rendering thread.
+    stm->emergency_bailout = nullptr;
+    LOG("Destroy WaitForSingleObject on thread failed: %lx, %lx", r, GetLastError());
+    rv = false;
+  }
+
+  // Only attempts to close and null out the thread and event if the
+  // WaitForSingleObject above succeeded, so that calling this function again
+  // attemps to clean up the thread and event each time.
+  if (rv) {
+    LOG("Closing thread.");
+    CloseHandle(stm->thread);
+    stm->thread = NULL;
+
+    CloseHandle(stm->shutdown_event);
+    stm->shutdown_event = 0;
+  }
+
+  return rv;
+}
+
+void wasapi_destroy(cubeb * context)
+{
+  if (context->device_ids) {
+    cubeb_strings_destroy(context->device_ids);
+  }
+
+  delete context;
+}
+
+char const * wasapi_get_backend_id(cubeb * context)
+{
+  return "wasapi";
+}
+
+int
+wasapi_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
+{
+  XASSERT(ctx && max_channels);
+
+  com_ptr<IMMDevice> device;
+  HRESULT hr = get_default_endpoint(device, eRender, eConsole);
+  if (FAILED(hr)) {
+    return CUBEB_ERROR;
+  }
+
+  com_ptr<IAudioClient> client;
+  hr = device->Activate(__uuidof(IAudioClient),
+                        CLSCTX_INPROC_SERVER,
+                        NULL, client.receive_vpp());
+  if (FAILED(hr)) {
+    return CUBEB_ERROR;
+  }
+
+  WAVEFORMATEX * tmp = nullptr;
+  hr = client->GetMixFormat(&tmp);
+  if (FAILED(hr)) {
+    return CUBEB_ERROR;
+  }
+  com_heap_ptr<WAVEFORMATEX> mix_format(tmp);
+
+  *max_channels = mix_format->nChannels;
+
+  return CUBEB_OK;
+}
+
+int
+wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
+{
+  if (params.format != CUBEB_SAMPLE_FLOAT32NE && params.format != CUBEB_SAMPLE_S16NE) {
+    return CUBEB_ERROR_INVALID_FORMAT;
+  }
+
+  ERole role = pref_to_role(params.prefs);
+
+  com_ptr<IMMDevice> device;
+  HRESULT hr = get_default_endpoint(device, eRender, role);
+  if (FAILED(hr)) {
+    LOG("Could not get default endpoint: %lx", hr);
+    return CUBEB_ERROR;
+  }
+
+  com_ptr<IAudioClient> client;
+  hr = device->Activate(__uuidof(IAudioClient),
+                        CLSCTX_INPROC_SERVER,
+                        NULL, client.receive_vpp());
+  if (FAILED(hr)) {
+    LOG("Could not activate device for latency: %lx", hr);
+    return CUBEB_ERROR;
+  }
+
+  REFERENCE_TIME minimum_period;
+  REFERENCE_TIME default_period;
+  hr = client->GetDevicePeriod(&default_period, &minimum_period);
+  if (FAILED(hr)) {
+    LOG("Could not get device period: %lx", hr);
+    return CUBEB_ERROR;
+  }
+
+  LOG("default device period: %I64d, minimum device period: %I64d", default_period, minimum_period);
+
+  /* If we're on Windows 10, we can use IAudioClient3 to get minimal latency.
+     Otherwise, according to the docs, the best latency we can achieve is by
+     synchronizing the stream and the engine.
+     http://msdn.microsoft.com/en-us/library/windows/desktop/dd370871%28v=vs.85%29.aspx */
+
+  #ifdef _WIN32_WINNT_WIN10
+    *latency_frames = hns_to_frames(params.rate, minimum_period);
+  #else
+    *latency_frames = hns_to_frames(params.rate, default_period);
+  #endif
+
+  LOG("Minimum latency in frames: %u", *latency_frames);
+
+  return CUBEB_OK;
+}
+
+int
+wasapi_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
+{
+  com_ptr<IMMDevice> device;
+  HRESULT hr = get_default_endpoint(device, eRender, eConsole);
+  if (FAILED(hr)) {
+    return CUBEB_ERROR;
+  }
+
+  com_ptr<IAudioClient> client;
+  hr = device->Activate(__uuidof(IAudioClient),
+                        CLSCTX_INPROC_SERVER,
+                        NULL, client.receive_vpp());
+  if (FAILED(hr)) {
+    return CUBEB_ERROR;
+  }
+
+  WAVEFORMATEX * tmp = nullptr;
+  hr = client->GetMixFormat(&tmp);
+  if (FAILED(hr)) {
+    return CUBEB_ERROR;
+  }
+  com_heap_ptr<WAVEFORMATEX> mix_format(tmp);
+
+  *rate = mix_format->nSamplesPerSec;
+
+  LOG("Preferred sample rate for output: %u", *rate);
+
+  return CUBEB_OK;
+}
+
+void wasapi_stream_destroy(cubeb_stream * stm);
+
+static void
+waveformatex_update_derived_properties(WAVEFORMATEX * format)
+{
+  format->nBlockAlign = format->wBitsPerSample * format->nChannels / 8;
+  format->nAvgBytesPerSec = format->nSamplesPerSec * format->nBlockAlign;
+  if (format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
+    WAVEFORMATEXTENSIBLE * format_pcm = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(format);
+    format_pcm->Samples.wValidBitsPerSample = format->wBitsPerSample;
+  }
+}
+
+/* Based on the mix format and the stream format, try to find a way to play
+   what the user requested. */
+static void
+handle_channel_layout(cubeb_stream * stm,  EDataFlow direction, com_heap_ptr<WAVEFORMATEX> & mix_format, const cubeb_stream_params * stream_params)
+{
+  com_ptr<IAudioClient> & audio_client = (direction == eRender) ? stm->output_client : stm->input_client;
+  XASSERT(audio_client);
+  /* The docs say that GetMixFormat is always of type WAVEFORMATEXTENSIBLE [1],
+     so the reinterpret_cast below should be safe. In practice, this is not
+     true, and we just want to bail out and let the rest of the code find a good
+     conversion path instead of trying to make WASAPI do it by itself.
+     [1]: http://msdn.microsoft.com/en-us/library/windows/desktop/dd370811%28v=vs.85%29.aspx*/
+  if (mix_format->wFormatTag != WAVE_FORMAT_EXTENSIBLE) {
+    return;
+  }
+
+  WAVEFORMATEXTENSIBLE * format_pcm = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(mix_format.get());
+
+  /* Stash a copy of the original mix format in case we need to restore it later. */
+  WAVEFORMATEXTENSIBLE hw_mix_format = *format_pcm;
+
+  /* Get the channel mask by the channel layout.
+     If the layout is not supported, we will get a closest settings below. */
+  format_pcm->dwChannelMask = stream_params->layout;
+  mix_format->nChannels = stream_params->channels;
+  waveformatex_update_derived_properties(mix_format.get());
+
+  /* Check if wasapi will accept our channel layout request. */
+  WAVEFORMATEX * closest;
+  HRESULT hr = audio_client->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED,
+                                               mix_format.get(),
+                                               &closest);
+  if (hr == S_FALSE) {
+    /* Channel layout not supported, but WASAPI gives us a suggestion. Use it,
+       and handle the eventual upmix/downmix ourselves. Ignore the subformat of
+       the suggestion, since it seems to always be IEEE_FLOAT. */
+    LOG("Using WASAPI suggested format: channels: %d", closest->nChannels);
+    XASSERT(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE);
+    WAVEFORMATEXTENSIBLE * closest_pcm = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(closest);
+    format_pcm->dwChannelMask = closest_pcm->dwChannelMask;
+    mix_format->nChannels = closest->nChannels;
+    waveformatex_update_derived_properties(mix_format.get());
+  } else if (hr == AUDCLNT_E_UNSUPPORTED_FORMAT) {
+    /* Not supported, no suggestion. This should not happen, but it does in the
+       field with some sound cards. We restore the mix format, and let the rest
+       of the code figure out the right conversion path. */
+    XASSERT(mix_format->wFormatTag == WAVE_FORMAT_EXTENSIBLE);
+    *reinterpret_cast<WAVEFORMATEXTENSIBLE *>(mix_format.get()) = hw_mix_format;
+  } else if (hr == S_OK) {
+    LOG("Requested format accepted by WASAPI.");
+  } else {
+    LOG("IsFormatSupported unhandled error: %lx", hr);
+  }
+}
+
+static bool
+initialize_iaudioclient3(com_ptr<IAudioClient> & audio_client,
+                         cubeb_stream * stm,
+                         const com_heap_ptr<WAVEFORMATEX> & mix_format,
+                         DWORD flags,
+                         EDataFlow direction)
+{
+  com_ptr<IAudioClient3> audio_client3;
+  audio_client->QueryInterface<IAudioClient3>(audio_client3.receive());
+  if (!audio_client3) {
+    LOG("Could not get IAudioClient3 interface");
+    return false;
+  }
+
+  if (flags & AUDCLNT_STREAMFLAGS_LOOPBACK) {
+    // IAudioClient3 doesn't work with loopback streams, and will return error
+    // 88890021: AUDCLNT_E_INVALID_STREAM_FLAG
+    LOG("Audio stream is loopback, not using IAudioClient3");
+    return false;
+  }
+
+  // IAudioClient3 doesn't support AUDCLNT_STREAMFLAGS_NOPERSIST, and will return
+  // AUDCLNT_E_INVALID_STREAM_FLAG. This is undocumented.
+  flags = flags ^ AUDCLNT_STREAMFLAGS_NOPERSIST;
+
+  // Some people have reported glitches with capture streams:
+  // http://blog.nirbheek.in/2018/03/low-latency-audio-on-windows-with.html
+  if (direction == eCapture) {
+    LOG("Audio stream is capture, not using IAudioClient3");
+    return false;
+  }
+
+  // Possibly initialize a shared-mode stream using IAudioClient3. Initializing
+  // a stream this way lets you request lower latencies, but also locks the global
+  // WASAPI engine at that latency.
+  // - If we request a shared-mode stream, streams created with IAudioClient will
+  //   have their latency adjusted to match. When  the shared-mode stream is
+  //   closed, they'll go back to normal.
+  // - If there's already a shared-mode stream running, then we cannot request
+  //   the engine change to a different latency - we have to match it.
+  // - It's antisocial to lock the WASAPI engine at its default latency. If we
+  //   would do this, then stop and use IAudioClient instead.
+
+  HRESULT hr;
+  uint32_t default_period = 0, fundamental_period = 0, min_period = 0, max_period = 0;
+  hr = audio_client3->GetSharedModeEnginePeriod(mix_format.get(), &default_period, &fundamental_period, &min_period, &max_period);
+  if (FAILED(hr)) {
+    LOG("Could not get shared mode engine period: error: %lx", hr);
+    return false;
+  }
+  uint32_t requested_latency = stm->latency;
+  if (requested_latency >= default_period) {
+    LOG("Requested latency %i greater than default latency %i, not using IAudioClient3", requested_latency, default_period);
+    return false;
+  }
+  LOG("Got shared mode engine period: default=%i fundamental=%i min=%i max=%i", default_period, fundamental_period, min_period, max_period);
+  // Snap requested latency to a valid value
+  uint32_t old_requested_latency = requested_latency;
+  if (requested_latency < min_period) {
+    requested_latency = min_period;
+  }
+  requested_latency -= (requested_latency - min_period) % fundamental_period;
+  if (requested_latency != old_requested_latency) {
+    LOG("Requested latency %i was adjusted to %i", old_requested_latency, requested_latency);
+  }
+
+  hr = audio_client3->InitializeSharedAudioStream(flags, requested_latency, mix_format.get(), NULL);
+  if (SUCCEEDED(hr)) {
+    return true;
+  }
+  else if (hr == AUDCLNT_E_ENGINE_PERIODICITY_LOCKED) {
+    LOG("Got AUDCLNT_E_ENGINE_PERIODICITY_LOCKED, adjusting latency request");
+  } else {
+    LOG("Could not initialize shared stream with IAudioClient3: error: %lx", hr);
+    return false;
+  }
+
+  uint32_t current_period = 0;
+  WAVEFORMATEX* current_format = nullptr;
+  // We have to pass a valid WAVEFORMATEX** and not nullptr, otherwise
+  // GetCurrentSharedModeEnginePeriod will return E_POINTER
+  hr = audio_client3->GetCurrentSharedModeEnginePeriod(&current_format, &current_period);
+  CoTaskMemFree(current_format);
+  if (FAILED(hr)) {
+    LOG("Could not get current shared mode engine period: error: %lx", hr);
+    return false;
+  }
+
+  if (current_period >= default_period) {
+    LOG("Current shared mode engine period %i too high, not using IAudioClient", current_period);
+    return false;
+  }
+
+  hr = audio_client3->InitializeSharedAudioStream(flags, current_period, mix_format.get(), NULL);
+  if (SUCCEEDED(hr)) {
+    LOG("Current shared mode engine period is %i instead of requested %i", current_period, requested_latency);
+    return true;
+  }
+
+  LOG("Could not initialize shared stream with IAudioClient3: error: %lx", hr);
+  return false;
+}
+
+#define DIRECTION_NAME (direction == eCapture ? "capture" : "render")
+
+template<typename T>
+int setup_wasapi_stream_one_side(cubeb_stream * stm,
+                                 cubeb_stream_params * stream_params,
+                                 wchar_t const * devid,
+                                 EDataFlow direction,
+                                 REFIID riid,
+                                 com_ptr<IAudioClient> & audio_client,
+                                 uint32_t * buffer_frame_count,
+                                 HANDLE & event,
+                                 T & render_or_capture_client,
+                                 cubeb_stream_params * mix_params)
+{
+  com_ptr<IMMDevice> device;
+  HRESULT hr;
+  bool is_loopback = stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK;
+  if (is_loopback && direction != eCapture) {
+    LOG("Loopback pref can only be used with capture streams!\n");
+    return CUBEB_ERROR;
+  }
+
+  stm->stream_reset_lock.assert_current_thread_owns();
+  bool try_again = false;
+  // This loops until we find a device that works, or we've exhausted all
+  // possibilities.
+  do {
+    if (devid) {
+      hr = get_endpoint(device, devid);
+      if (FAILED(hr)) {
+        LOG("Could not get %s endpoint, error: %lx\n", DIRECTION_NAME, hr);
+        return CUBEB_ERROR;
+      }
+    } else {
+      // If caller has requested loopback but not specified a device, look for
+      // the default render device. Otherwise look for the default device
+      // appropriate to the direction.
+      hr = get_default_endpoint(device, is_loopback ? eRender : direction, pref_to_role(stream_params->prefs));
+      if (FAILED(hr)) {
+        if (is_loopback) {
+          LOG("Could not get default render endpoint for loopback, error: %lx\n", hr);
+        } else {
+          LOG("Could not get default %s endpoint, error: %lx\n", DIRECTION_NAME, hr);
+        }
+        return CUBEB_ERROR;
+      }
+    }
+
+    /* Get a client. We will get all other interfaces we need from
+     * this pointer. */
+    hr = device->Activate(__uuidof(IAudioClient3),
+                          CLSCTX_INPROC_SERVER,
+                          NULL, audio_client.receive_vpp());
+    if (hr == E_NOINTERFACE) {
+      hr = device->Activate(__uuidof(IAudioClient),
+                            CLSCTX_INPROC_SERVER,
+                            NULL, audio_client.receive_vpp());
+    }
+
+    if (FAILED(hr)) {
+      LOG("Could not activate the device to get an audio"
+          " client for %s: error: %lx\n", DIRECTION_NAME, hr);
+      // A particular device can't be activated because it has been
+      // unplugged, try fall back to the default audio device.
+      if (devid && hr == AUDCLNT_E_DEVICE_INVALIDATED) {
+        LOG("Trying again with the default %s audio device.", DIRECTION_NAME);
+        devid = nullptr;
+        device = nullptr;
+        try_again = true;
+      } else {
+        return CUBEB_ERROR;
+      }
+    } else {
+      try_again = false;
+    }
+  } while (try_again);
+
+  /* We have to distinguish between the format the mixer uses,
+   * and the format the stream we want to play uses. */
+  WAVEFORMATEX * tmp = nullptr;
+  hr = audio_client->GetMixFormat(&tmp);
+  if (FAILED(hr)) {
+    LOG("Could not fetch current mix format from the audio"
+        " client for %s: error: %lx", DIRECTION_NAME, hr);
+    return CUBEB_ERROR;
+  }
+  com_heap_ptr<WAVEFORMATEX> mix_format(tmp);
+
+  mix_format->wBitsPerSample = stm->bytes_per_sample * 8;
+  if (mix_format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
+    WAVEFORMATEXTENSIBLE * format_pcm = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(mix_format.get());
+    format_pcm->SubFormat = stm->waveformatextensible_sub_format;
+  }
+  waveformatex_update_derived_properties(mix_format.get());
+
+  /* Set channel layout only when there're more than two channels. Otherwise,
+   * use the default setting retrieved from the stream format of the audio
+   * engine's internal processing by GetMixFormat. */
+  if (mix_format->nChannels > 2) {
+    handle_channel_layout(stm, direction, mix_format, stream_params);
+  }
+
+  mix_params->format = stream_params->format;
+  mix_params->rate = mix_format->nSamplesPerSec;
+  mix_params->channels = mix_format->nChannels;
+  mix_params->layout = mask_to_channel_layout(mix_format.get());
+
+  LOG("Setup requested=[f=%d r=%u c=%u l=%u] mix=[f=%d r=%u c=%u l=%u]",
+      stream_params->format, stream_params->rate, stream_params->channels,
+      stream_params->layout,
+      mix_params->format, mix_params->rate, mix_params->channels,
+      mix_params->layout);
+
+  DWORD flags = AUDCLNT_STREAMFLAGS_NOPERSIST;
+
+  // Check if a loopback device should be requested. Note that event callbacks
+  // do not work with loopback devices, so only request these if not looping.
+  if (is_loopback) {
+    flags |= AUDCLNT_STREAMFLAGS_LOOPBACK;
+  } else {
+    flags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
+  }
+
+  if (initialize_iaudioclient3(audio_client, stm, mix_format, flags, direction)) {
+    LOG("Initialized with IAudioClient3");
+  } else {
+    hr = audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED,
+                                  flags,
+                                  frames_to_hns(stm, stm->latency),
+                                  0,
+                                  mix_format.get(),
+                                  NULL);
+  }
+  if (FAILED(hr)) {
+    LOG("Unable to initialize audio client for %s: %lx.", DIRECTION_NAME, hr);
+    return CUBEB_ERROR;
+  }
+
+  hr = audio_client->GetBufferSize(buffer_frame_count);
+  if (FAILED(hr)) {
+    LOG("Could not get the buffer size from the client"
+        " for %s %lx.", DIRECTION_NAME, hr);
+    return CUBEB_ERROR;
+  }
+
+  // Events are used if not looping back
+  if (!is_loopback) {
+    hr = audio_client->SetEventHandle(event);
+    if (FAILED(hr)) {
+      LOG("Could set the event handle for the %s client %lx.",
+          DIRECTION_NAME, hr);
+      return CUBEB_ERROR;
+    }
+  }
+
+  hr = audio_client->GetService(riid, render_or_capture_client.receive_vpp());
+  if (FAILED(hr)) {
+    LOG("Could not get the %s client %lx.", DIRECTION_NAME, hr);
+    return CUBEB_ERROR;
+  }
+
+  return CUBEB_OK;
+}
+
+#undef DIRECTION_NAME
+
+int setup_wasapi_stream(cubeb_stream * stm)
+{
+  int rv;
+
+  stm->stream_reset_lock.assert_current_thread_owns();
+
+  XASSERT((!stm->output_client || !stm->input_client) && "WASAPI stream already setup, close it first.");
+
+  if (has_input(stm)) {
+    LOG("(%p) Setup capture: device=%p", stm, stm->input_device.get());
+    rv = setup_wasapi_stream_one_side(stm,
+                                      &stm->input_stream_params,
+                                      stm->input_device.get(),
+                                      eCapture,
+                                      __uuidof(IAudioCaptureClient),
+                                      stm->input_client,
+                                      &stm->input_buffer_frame_count,
+                                      stm->input_available_event,
+                                      stm->capture_client,
+                                      &stm->input_mix_params);
+    if (rv != CUBEB_OK) {
+      LOG("Failure to open the input side.");
+      return rv;
+    }
+
+    // We initializing an input stream, buffer ahead two buffers worth of silence.
+    // This delays the input side slightly, but allow to not glitch when no input
+    // is available when calling into the resampler to call the callback: the input
+    // refill event will be set shortly after to compensate for this lack of data.
+    // In debug, four buffers are used, to avoid tripping up assertions down the line.
+#if !defined(DEBUG)
+    const int silent_buffer_count = 2;
+#else
+    const int silent_buffer_count = 6;
+#endif
+    stm->linear_input_buffer->push_silence(stm->input_buffer_frame_count *
+                                           stm->input_stream_params.channels *
+                                           silent_buffer_count);
+  }
+
+  // If we don't have an output device but are requesting a loopback device,
+  // we attempt to open that same device in output mode in order to drive the
+  // loopback via the output events.
+  stm->has_dummy_output = false;
+  if (!has_output(stm) && stm->input_stream_params.prefs & CUBEB_STREAM_PREF_LOOPBACK) {
+    stm->output_stream_params.rate = stm->input_stream_params.rate;
+    stm->output_stream_params.channels = stm->input_stream_params.channels;
+    stm->output_stream_params.layout = stm->input_stream_params.layout;
+    if (stm->input_device) {
+      size_t len = wcslen(stm->input_device.get());
+      std::unique_ptr<wchar_t[]> tmp(new wchar_t[len + 1]);
+      if (wcsncpy_s(tmp.get(), len + 1, stm->input_device.get(), len) != 0) {
+        LOG("Failed to copy device identifier while copying input stream"
+            " configuration to output stream configuration to drive loopback.");
+        return CUBEB_ERROR;
+      }
+      stm->output_device = move(tmp);
+    }
+    stm->has_dummy_output = true;
+  }
+
+  if (has_output(stm)) {
+    LOG("(%p) Setup render: device=%p", stm, stm->output_device.get());
+    rv = setup_wasapi_stream_one_side(stm,
+                                      &stm->output_stream_params,
+                                      stm->output_device.get(),
+                                      eRender,
+                                      __uuidof(IAudioRenderClient),
+                                      stm->output_client,
+                                      &stm->output_buffer_frame_count,
+                                      stm->refill_event,
+                                      stm->render_client,
+                                      &stm->output_mix_params);
+    if (rv != CUBEB_OK) {
+      LOG("Failure to open the output side.");
+      return rv;
+    }
+
+    HRESULT hr = stm->output_client->GetService(__uuidof(IAudioStreamVolume),
+                                                stm->audio_stream_volume.receive_vpp());
+    if (FAILED(hr)) {
+      LOG("Could not get the IAudioStreamVolume: %lx", hr);
+      return CUBEB_ERROR;
+    }
+
+    XASSERT(stm->frames_written == 0);
+    hr = stm->output_client->GetService(__uuidof(IAudioClock),
+                                        stm->audio_clock.receive_vpp());
+    if (FAILED(hr)) {
+      LOG("Could not get the IAudioClock: %lx", hr);
+      return CUBEB_ERROR;
+    }
+
+    /* Restore the stream volume over a device change. */
+    if (stream_set_volume(stm, stm->volume) != CUBEB_OK) {
+      LOG("Could not set the volume.");
+      return CUBEB_ERROR;
+    }
+  }
+
+  /* If we have both input and output, we resample to
+   * the highest sample rate available. */
+  int32_t target_sample_rate;
+  if (has_input(stm) && has_output(stm)) {
+    XASSERT(stm->input_stream_params.rate == stm->output_stream_params.rate);
+    target_sample_rate = stm->input_stream_params.rate;
+  } else if (has_input(stm)) {
+    target_sample_rate = stm->input_stream_params.rate;
+  } else {
+    XASSERT(has_output(stm));
+    target_sample_rate = stm->output_stream_params.rate;
+  }
+
+  LOG("Target sample rate: %d", target_sample_rate);
+
+  /* If we are playing/capturing a mono stream, we only resample one channel,
+   and copy it over, so we are always resampling the number
+   of channels of the stream, not the number of channels
+   that WASAPI wants. */
+  cubeb_stream_params input_params = stm->input_mix_params;
+  input_params.channels = stm->input_stream_params.channels;
+  cubeb_stream_params output_params = stm->output_mix_params;
+  output_params.channels = stm->output_stream_params.channels;
+
+  stm->resampler.reset(
+    cubeb_resampler_create(stm,
+                           has_input(stm) ? &input_params : nullptr,
+                           has_output(stm) ? &output_params : nullptr,
+                           target_sample_rate,
+                           stm->data_callback,
+                           stm->user_ptr,
+                           CUBEB_RESAMPLER_QUALITY_DESKTOP));
+  if (!stm->resampler) {
+    LOG("Could not get a resampler");
+    return CUBEB_ERROR;
+  }
+
+  XASSERT(has_input(stm) || has_output(stm));
+
+  if (has_input(stm) && has_output(stm)) {
+    stm->refill_callback = refill_callback_duplex;
+  } else if (has_input(stm)) {
+    stm->refill_callback = refill_callback_input;
+  } else if (has_output(stm)) {
+    stm->refill_callback = refill_callback_output;
+  }
+
+  // Create input mixer.
+  if (has_input(stm) &&
+      ((stm->input_mix_params.layout != CUBEB_LAYOUT_UNDEFINED &&
+        stm->input_mix_params.layout != stm->input_stream_params.layout) ||
+       (stm->input_mix_params.channels != stm->input_stream_params.channels))) {
+    if (stm->input_mix_params.layout == CUBEB_LAYOUT_UNDEFINED) {
+      LOG("Input stream using undefined layout! Any mixing may be "
+          "unpredictable!\n");
+    }
+    stm->input_mixer.reset(cubeb_mixer_create(stm->input_stream_params.format,
+                                              stm->input_mix_params.channels,
+                                              stm->input_mix_params.layout,
+                                              stm->input_stream_params.channels,
+                                              stm->input_stream_params.layout));
+    assert(stm->input_mixer);
+  }
+
+  // Create output mixer.
+  if (has_output(stm) && stm->output_mix_params.layout != stm->output_stream_params.layout) {
+    if (stm->output_mix_params.layout == CUBEB_LAYOUT_UNDEFINED) {
+      LOG("Output stream using undefined layout! Any mixing may be unpredictable!\n");
+    }
+    stm->output_mixer.reset(cubeb_mixer_create(stm->output_stream_params.format,
+                                               stm->output_stream_params.channels,
+                                               stm->output_stream_params.layout,
+                                               stm->output_mix_params.channels,
+                                               stm->output_mix_params.layout));
+    assert(stm->output_mixer);
+    // Input is up/down mixed when depacketized in get_input_buffer.
+    stm->mix_buffer.resize(
+      frames_to_bytes_before_mix(stm, stm->output_buffer_frame_count));
+  }
+
+  return CUBEB_OK;
+}
+
+ERole
+pref_to_role(cubeb_stream_prefs prefs)
+{
+  if (prefs & CUBEB_STREAM_PREF_VOICE) {
+    return eCommunications;
+  }
+
+  return eConsole;
+}
+
+int
+wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
+                   char const * stream_name,
+                   cubeb_devid input_device,
+                   cubeb_stream_params * input_stream_params,
+                   cubeb_devid output_device,
+                   cubeb_stream_params * output_stream_params,
+                   unsigned int latency_frames, cubeb_data_callback data_callback,
+                   cubeb_state_callback state_callback, void * user_ptr)
+{
+  int rv;
+
+  XASSERT(context && stream && (input_stream_params || output_stream_params));
+
+  if (output_stream_params && input_stream_params &&
+      output_stream_params->format != input_stream_params->format) {
+    return CUBEB_ERROR_INVALID_FORMAT;
+  }
+
+  std::unique_ptr<cubeb_stream, decltype(&wasapi_stream_destroy)> stm(new cubeb_stream(), wasapi_stream_destroy);
+
+  stm->context = context;
+  stm->data_callback = data_callback;
+  stm->state_callback = state_callback;
+  stm->user_ptr = user_ptr;
+
+  if (stm->output_stream_params.prefs & CUBEB_STREAM_PREF_VOICE ||
+      stm->input_stream_params.prefs & CUBEB_STREAM_PREF_VOICE) {
+    stm->role = eCommunications;
+  } else {
+    stm->role = eConsole;
+  }
+
+  if (input_stream_params) {
+    stm->input_stream_params = *input_stream_params;
+    stm->input_device = utf8_to_wstr(reinterpret_cast<char const *>(input_device));
+  }
+  if (output_stream_params) {
+    stm->output_stream_params = *output_stream_params;
+    stm->output_device = utf8_to_wstr(reinterpret_cast<char const *>(output_device));
+  }
+
+  switch (output_stream_params ? output_stream_params->format : input_stream_params->format) {
+    case CUBEB_SAMPLE_S16NE:
+      stm->bytes_per_sample = sizeof(short);
+      stm->waveformatextensible_sub_format = KSDATAFORMAT_SUBTYPE_PCM;
+      stm->linear_input_buffer.reset(new auto_array_wrapper_impl<short>);
+      break;
+    case CUBEB_SAMPLE_FLOAT32NE:
+      stm->bytes_per_sample = sizeof(float);
+      stm->waveformatextensible_sub_format = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+      stm->linear_input_buffer.reset(new auto_array_wrapper_impl<float>);
+      break;
+    default:
+      return CUBEB_ERROR_INVALID_FORMAT;
+  }
+
+  stm->latency = latency_frames;
+
+  stm->reconfigure_event = CreateEvent(NULL, 0, 0, NULL);
+  if (!stm->reconfigure_event) {
+    LOG("Can't create the reconfigure event, error: %lx", GetLastError());
+    return CUBEB_ERROR;
+  }
+
+  /* Unconditionally create the two events so that the wait logic is simpler. */
+  stm->refill_event = CreateEvent(NULL, 0, 0, NULL);
+  if (!stm->refill_event) {
+    LOG("Can't create the refill event, error: %lx", GetLastError());
+    return CUBEB_ERROR;
+  }
+
+  stm->input_available_event = CreateEvent(NULL, 0, 0, NULL);
+  if (!stm->input_available_event) {
+    LOG("Can't create the input available event , error: %lx", GetLastError());
+    return CUBEB_ERROR;
+  }
+
+  {
+    /* Locking here is not strictly necessary, because we don't have a
+       notification client that can reset the stream yet, but it lets us
+       assert that the lock is held in the function. */
+    auto_lock lock(stm->stream_reset_lock);
+    rv = setup_wasapi_stream(stm.get());
+  }
+  if (rv != CUBEB_OK) {
+    return rv;
+  }
+
+  if (!((input_stream_params ?
+         (input_stream_params->prefs & CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING) : 0) ||
+        (output_stream_params ?
+         (output_stream_params->prefs & CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING) : 0))) {
+    HRESULT hr = register_notification_client(stm.get());
+    if (FAILED(hr)) {
+      /* this is not fatal, we can still play audio, but we won't be able
+         to keep using the default audio endpoint if it changes. */
+      LOG("failed to register notification client, %lx", hr);
+    }
+  }
+
+  *stream = stm.release();
+
+  LOG("Stream init succesfull (%p)", *stream);
+  return CUBEB_OK;
+}
+
+void close_wasapi_stream(cubeb_stream * stm)
+{
+  XASSERT(stm);
+
+  stm->stream_reset_lock.assert_current_thread_owns();
+
+  stm->output_client = nullptr;
+  stm->render_client = nullptr;
+
+  stm->input_client = nullptr;
+  stm->capture_client = nullptr;
+
+  stm->audio_stream_volume = nullptr;
+
+  stm->audio_clock = nullptr;
+  stm->total_frames_written += static_cast<UINT64>(round(stm->frames_written * stream_to_mix_samplerate_ratio(stm->output_stream_params, stm->output_mix_params)));
+  stm->frames_written = 0;
+
+  stm->resampler.reset();
+  stm->output_mixer.reset();
+  stm->input_mixer.reset();
+  stm->mix_buffer.clear();
+}
+
+void wasapi_stream_destroy(cubeb_stream * stm)
+{
+  XASSERT(stm);
+  LOG("Stream destroy (%p)", stm);
+
+  // Only free stm->emergency_bailout if we could join the thread.
+  // If we could not join the thread, stm->emergency_bailout is true
+  // and is still alive until the thread wakes up and exits cleanly.
+  if (stop_and_join_render_thread(stm)) {
+    delete stm->emergency_bailout.load();
+    stm->emergency_bailout = nullptr;
+  }
+
+  if (stm->notification_client) {
+    unregister_notification_client(stm);
+  }
+
+  CloseHandle(stm->reconfigure_event);
+  CloseHandle(stm->refill_event);
+  CloseHandle(stm->input_available_event);
+
+  // The variables intialized in wasapi_stream_init,
+  // must be destroyed in wasapi_stream_destroy.
+  stm->linear_input_buffer.reset();
+
+  {
+    auto_lock lock(stm->stream_reset_lock);
+    close_wasapi_stream(stm);
+  }
+
+  delete stm;
+}
+
+enum StreamDirection {
+  OUTPUT,
+  INPUT
+};
+
+int stream_start_one_side(cubeb_stream * stm, StreamDirection dir)
+{
+  XASSERT((dir == OUTPUT && stm->output_client) ||
+          (dir == INPUT && stm->input_client));
+
+  HRESULT hr = dir == OUTPUT ? stm->output_client->Start() : stm->input_client->Start();
+  if (hr == AUDCLNT_E_DEVICE_INVALIDATED) {
+    LOG("audioclient invalidated for %s device, reconfiguring",
+        dir == OUTPUT ? "output" : "input");
+
+    BOOL ok = ResetEvent(stm->reconfigure_event);
+    if (!ok) {
+      LOG("resetting reconfig event failed for %s stream: %lx",
+          dir == OUTPUT ? "output" : "input", GetLastError());
+    }
+
+    close_wasapi_stream(stm);
+    int r = setup_wasapi_stream(stm);
+    if (r != CUBEB_OK) {
+      LOG("reconfigure failed");
+      return r;
+    }
+
+    HRESULT hr2 = dir == OUTPUT ? stm->output_client->Start() : stm->input_client->Start();
+    if (FAILED(hr2)) {
+      LOG("could not start the %s stream after reconfig: %lx",
+          dir == OUTPUT ? "output" : "input", hr);
+      return CUBEB_ERROR;
+    }
+  } else if (FAILED(hr)) {
+    LOG("could not start the %s stream: %lx.",
+        dir == OUTPUT ? "output" : "input", hr);
+    return CUBEB_ERROR;
+  }
+
+  return CUBEB_OK;
+}
+
+int wasapi_stream_start(cubeb_stream * stm)
+{
+  auto_lock lock(stm->stream_reset_lock);
+
+  XASSERT(stm && !stm->thread && !stm->shutdown_event);
+  XASSERT(stm->output_client || stm->input_client);
+
+  stm->emergency_bailout = new std::atomic<bool>(false);
+
+  if (stm->output_client) {
+    int rv = stream_start_one_side(stm, OUTPUT);
+    if (rv != CUBEB_OK) {
+      return rv;
+    }
+  }
+
+  if (stm->input_client) {
+    int rv = stream_start_one_side(stm, INPUT);
+    if (rv != CUBEB_OK) {
+      return rv;
+    }
+  }
+
+  stm->shutdown_event = CreateEvent(NULL, 0, 0, NULL);
+  if (!stm->shutdown_event) {
+    LOG("Can't create the shutdown event, error: %lx", GetLastError());
+    return CUBEB_ERROR;
+  }
+
+  stm->thread_ready_event = CreateEvent(NULL, 0, 0, NULL);
+  if (!stm->thread_ready_event) {
+    LOG("Can't create the thread_ready event, error: %lx", GetLastError());
+    return CUBEB_ERROR;
+  }
+
+  cubeb_async_log_reset_threads();
+  stm->thread = (HANDLE) _beginthreadex(NULL, 512 * 1024, wasapi_stream_render_loop, stm, STACK_SIZE_PARAM_IS_A_RESERVATION, NULL);
+  if (stm->thread == NULL) {
+    LOG("could not create WASAPI render thread.");
+    return CUBEB_ERROR;
+  }
+
+  // Wait for wasapi_stream_render_loop to signal that emergency_bailout has
+  // been read, avoiding a bailout situation where we could free `stm`
+  // before wasapi_stream_render_loop had a chance to run.
+  HRESULT hr = WaitForSingleObject(stm->thread_ready_event, INFINITE);
+  XASSERT(hr == WAIT_OBJECT_0);
+  CloseHandle(stm->thread_ready_event);
+  stm->thread_ready_event = 0;
+
+  stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
+
+  return CUBEB_OK;
+}
+
+int wasapi_stream_stop(cubeb_stream * stm)
+{
+  XASSERT(stm);
+  HRESULT hr;
+
+  {
+    auto_lock lock(stm->stream_reset_lock);
+
+    if (stm->output_client) {
+      hr = stm->output_client->Stop();
+      if (FAILED(hr)) {
+        LOG("could not stop AudioClient (output)");
+        return CUBEB_ERROR;
+      }
+    }
+
+    if (stm->input_client) {
+      hr = stm->input_client->Stop();
+      if (FAILED(hr)) {
+        LOG("could not stop AudioClient (input)");
+        return CUBEB_ERROR;
+      }
+    }
+
+    stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
+  }
+
+  if (stop_and_join_render_thread(stm)) {
+    delete stm->emergency_bailout.load();
+    stm->emergency_bailout = nullptr;
+  } else {
+    // If we could not join the thread, put the stream in error.
+    stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
+    return CUBEB_ERROR;
+  }
+
+  return CUBEB_OK;
+}
+
+int wasapi_stream_reset_default_device(cubeb_stream * stm)
+{
+  XASSERT(stm && stm->reconfigure_event);
+  BOOL ok = SetEvent(stm->reconfigure_event);
+  if (!ok) {
+    LOG("SetEvent on reconfigure_event failed: %lx", GetLastError());
+    return CUBEB_ERROR;
+  }
+  return CUBEB_OK;
+}
+
+int wasapi_stream_get_position(cubeb_stream * stm, uint64_t * position)
+{
+  XASSERT(stm && position);
+  auto_lock lock(stm->stream_reset_lock);
+
+  if (!has_output(stm)) {
+    return CUBEB_ERROR;
+  }
+
+  /* Calculate how far behind the current stream head the playback cursor is. */
+  uint64_t stream_delay = static_cast<uint64_t>(current_stream_delay(stm) * stm->output_stream_params.rate);
+
+  /* Calculate the logical stream head in frames at the stream sample rate. */
+  uint64_t max_pos = stm->total_frames_written +
+                     static_cast<uint64_t>(round(stm->frames_written * stream_to_mix_samplerate_ratio(stm->output_stream_params, stm->output_mix_params)));
+
+  *position = max_pos;
+  if (stream_delay <= *position) {
+    *position -= stream_delay;
+  }
+
+  if (*position < stm->prev_position) {
+    *position = stm->prev_position;
+  }
+  stm->prev_position = *position;
+
+  return CUBEB_OK;
+}
+
+int wasapi_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
+{
+  XASSERT(stm && latency);
+
+  if (!has_output(stm)) {
+    return CUBEB_ERROR;
+  }
+
+  auto_lock lock(stm->stream_reset_lock);
+
+  /* The GetStreamLatency method only works if the
+     AudioClient has been initialized. */
+  if (!stm->output_client) {
+    return CUBEB_ERROR;
+  }
+
+  REFERENCE_TIME latency_hns;
+  HRESULT hr = stm->output_client->GetStreamLatency(&latency_hns);
+  if (FAILED(hr)) {
+    return CUBEB_ERROR;
+  }
+  *latency = hns_to_frames(stm, latency_hns);
+
+  return CUBEB_OK;
+}
+
+int wasapi_stream_set_volume(cubeb_stream * stm, float volume)
+{
+  auto_lock lock(stm->stream_reset_lock);
+
+  if (!has_output(stm)) {
+    return CUBEB_ERROR;
+  }
+
+  if (stream_set_volume(stm, volume) != CUBEB_OK) {
+    return CUBEB_ERROR;
+  }
+
+  stm->volume = volume;
+
+  return CUBEB_OK;
+}
+
+static char const *
+wstr_to_utf8(LPCWSTR str)
+{
+  int size = ::WideCharToMultiByte(CP_UTF8, 0, str, -1, nullptr, 0, NULL, NULL);
+  if (size <= 0) {
+    return nullptr;
+  }
+
+  char * ret = static_cast<char *>(malloc(size));
+  ::WideCharToMultiByte(CP_UTF8, 0, str, -1, ret, size, NULL, NULL);
+  return ret;
+}
+
+static std::unique_ptr<wchar_t const []>
+utf8_to_wstr(char const * str)
+{
+  int size = ::MultiByteToWideChar(CP_UTF8, 0, str, -1, nullptr, 0);
+  if (size <= 0) {
+    return nullptr;
+  }
+
+  std::unique_ptr<wchar_t []> ret(new wchar_t[size]);
+  ::MultiByteToWideChar(CP_UTF8, 0, str, -1, ret.get(), size);
+  return ret;
+}
+
+static com_ptr<IMMDevice>
+wasapi_get_device_node(IMMDeviceEnumerator * enumerator, IMMDevice * dev)
+{
+  com_ptr<IMMDevice> ret;
+  com_ptr<IDeviceTopology> devtopo;
+  com_ptr<IConnector> connector;
+
+  if (SUCCEEDED(dev->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, devtopo.receive_vpp())) &&
+      SUCCEEDED(devtopo->GetConnector(0, connector.receive()))) {
+    wchar_t * tmp = nullptr;
+    if (SUCCEEDED(connector->GetDeviceIdConnectedTo(&tmp))) {
+      com_heap_ptr<wchar_t> filterid(tmp);
+      if (FAILED(enumerator->GetDevice(filterid.get(), ret.receive())))
+        ret = NULL;
+    }
+  }
+
+  return ret;
+}
+
+static BOOL
+wasapi_is_default_device(EDataFlow flow, ERole role, LPCWSTR device_id,
+                         IMMDeviceEnumerator * enumerator)
+{
+  BOOL ret = FALSE;
+  com_ptr<IMMDevice> dev;
+  HRESULT hr;
+
+  hr = enumerator->GetDefaultAudioEndpoint(flow, role, dev.receive());
+  if (SUCCEEDED(hr)) {
+    wchar_t * tmp = nullptr;
+    if (SUCCEEDED(dev->GetId(&tmp))) {
+      com_heap_ptr<wchar_t> defdevid(tmp);
+      ret = (wcscmp(defdevid.get(), device_id) == 0);
+    }
+  }
+
+  return ret;
+}
+
+int
+wasapi_create_device(cubeb * ctx, cubeb_device_info& ret, IMMDeviceEnumerator * enumerator, IMMDevice * dev)
+{
+  com_ptr<IMMEndpoint> endpoint;
+  com_ptr<IMMDevice> devnode;
+  com_ptr<IAudioClient> client;
+  EDataFlow flow;
+  DWORD state = DEVICE_STATE_NOTPRESENT;
+  com_ptr<IPropertyStore> propstore;
+  REFERENCE_TIME def_period, min_period;
+  HRESULT hr;
+
+  struct prop_variant : public PROPVARIANT {
+    prop_variant() { PropVariantInit(this); }
+    ~prop_variant() { PropVariantClear(this); }
+    prop_variant(prop_variant const &) = delete;
+    prop_variant & operator=(prop_variant const &) = delete;
+  };
+
+  hr = dev->QueryInterface(IID_PPV_ARGS(endpoint.receive()));
+  if (FAILED(hr)) return CUBEB_ERROR;
+
+  hr = endpoint->GetDataFlow(&flow);
+  if (FAILED(hr)) return CUBEB_ERROR;
+
+  wchar_t * tmp = nullptr;
+  hr = dev->GetId(&tmp);
+  if (FAILED(hr)) return CUBEB_ERROR;
+  com_heap_ptr<wchar_t> device_id(tmp);
+
+  char const * device_id_intern = intern_device_id(ctx, device_id.get());
+  if (!device_id_intern) {
+    return CUBEB_ERROR;
+  }
+
+  hr = dev->OpenPropertyStore(STGM_READ, propstore.receive());
+  if (FAILED(hr)) return CUBEB_ERROR;
+
+  hr = dev->GetState(&state);
+  if (FAILED(hr)) return CUBEB_ERROR;
+
+  ret.device_id = device_id_intern;
+  ret.devid = reinterpret_cast<cubeb_devid>(ret.device_id);
+  prop_variant namevar;
+  hr = propstore->GetValue(PKEY_Device_FriendlyName, &namevar);
+  if (SUCCEEDED(hr))
+    ret.friendly_name = wstr_to_utf8(namevar.pwszVal);
+
+  devnode = wasapi_get_device_node(enumerator, dev);
+  if (devnode) {
+    com_ptr<IPropertyStore> ps;
+    hr = devnode->OpenPropertyStore(STGM_READ, ps.receive());
+    if (FAILED(hr)) return CUBEB_ERROR;
+
+    prop_variant instancevar;
+    hr = ps->GetValue(PKEY_Device_InstanceId, &instancevar);
+    if (SUCCEEDED(hr)) {
+      ret.group_id = wstr_to_utf8(instancevar.pwszVal);
+    }
+  }
+
+  ret.preferred = CUBEB_DEVICE_PREF_NONE;
+  if (wasapi_is_default_device(flow, eConsole, device_id.get(), enumerator))
+    ret.preferred = (cubeb_device_pref)(ret.preferred | CUBEB_DEVICE_PREF_MULTIMEDIA);
+  if (wasapi_is_default_device(flow, eCommunications, device_id.get(), enumerator))
+    ret.preferred = (cubeb_device_pref)(ret.preferred | CUBEB_DEVICE_PREF_VOICE);
+  if (wasapi_is_default_device(flow, eConsole, device_id.get(), enumerator))
+    ret.preferred = (cubeb_device_pref)(ret.preferred | CUBEB_DEVICE_PREF_NOTIFICATION);
+
+  if (flow == eRender) ret.type = CUBEB_DEVICE_TYPE_OUTPUT;
+  else if (flow == eCapture) ret.type = CUBEB_DEVICE_TYPE_INPUT;
+  switch (state) {
+    case DEVICE_STATE_ACTIVE:
+      ret.state = CUBEB_DEVICE_STATE_ENABLED;
+      break;
+    case DEVICE_STATE_UNPLUGGED:
+      ret.state = CUBEB_DEVICE_STATE_UNPLUGGED;
+      break;
+    default:
+      ret.state = CUBEB_DEVICE_STATE_DISABLED;
+      break;
+  };
+
+  ret.format = static_cast<cubeb_device_fmt>(CUBEB_DEVICE_FMT_F32NE | CUBEB_DEVICE_FMT_S16NE);
+  ret.default_format = CUBEB_DEVICE_FMT_F32NE;
+  prop_variant fmtvar;
+  hr = propstore->GetValue(PKEY_AudioEngine_DeviceFormat, &fmtvar);
+  if (SUCCEEDED(hr) && fmtvar.vt == VT_BLOB) {
+    if (fmtvar.blob.cbSize == sizeof(PCMWAVEFORMAT)) {
+      const PCMWAVEFORMAT * pcm = reinterpret_cast<const PCMWAVEFORMAT *>(fmtvar.blob.pBlobData);
+
+      ret.max_rate = ret.min_rate = ret.default_rate = pcm->wf.nSamplesPerSec;
+      ret.max_channels = pcm->wf.nChannels;
+    } else if (fmtvar.blob.cbSize >= sizeof(WAVEFORMATEX)) {
+      WAVEFORMATEX* wfx = reinterpret_cast<WAVEFORMATEX*>(fmtvar.blob.pBlobData);
+
+      if (fmtvar.blob.cbSize >= sizeof(WAVEFORMATEX) + wfx->cbSize ||
+          wfx->wFormatTag == WAVE_FORMAT_PCM) {
+        ret.max_rate = ret.min_rate = ret.default_rate = wfx->nSamplesPerSec;
+        ret.max_channels = wfx->nChannels;
+      }
+    }
+  }
+
+  if (SUCCEEDED(dev->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, client.receive_vpp())) &&
+      SUCCEEDED(client->GetDevicePeriod(&def_period, &min_period))) {
+    ret.latency_lo = hns_to_frames(ret.default_rate, min_period);
+    ret.latency_hi = hns_to_frames(ret.default_rate, def_period);
+  } else {
+    ret.latency_lo = 0;
+    ret.latency_hi = 0;
+  }
+
+  return CUBEB_OK;
+}
+
+static int
+wasapi_enumerate_devices(cubeb * context, cubeb_device_type type,
+                         cubeb_device_collection * out)
+{
+  com_ptr<IMMDeviceEnumerator> enumerator;
+  com_ptr<IMMDeviceCollection> collection;
+  HRESULT hr;
+  UINT cc, i;
+  EDataFlow flow;
+
+  hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL,
+                        CLSCTX_INPROC_SERVER, IID_PPV_ARGS(enumerator.receive()));
+  if (FAILED(hr)) {
+    LOG("Could not get device enumerator: %lx", hr);
+    return CUBEB_ERROR;
+  }
+
+  if (type == CUBEB_DEVICE_TYPE_OUTPUT) flow = eRender;
+  else if (type == CUBEB_DEVICE_TYPE_INPUT) flow = eCapture;
+  else if (type & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) flow = eAll;
+  else return CUBEB_ERROR;
+
+  hr = enumerator->EnumAudioEndpoints(flow, DEVICE_STATEMASK_ALL, collection.receive());
+  if (FAILED(hr)) {
+    LOG("Could not enumerate audio endpoints: %lx", hr);
+    return CUBEB_ERROR;
+  }
+
+  hr = collection->GetCount(&cc);
+  if (FAILED(hr)) {
+    LOG("IMMDeviceCollection::GetCount() failed: %lx", hr);
+    return CUBEB_ERROR;
+  }
+  cubeb_device_info * devices = new cubeb_device_info[cc];
+  if (!devices)
+    return CUBEB_ERROR;
+
+  PodZero(devices, cc);
+  out->count = 0;
+  for (i = 0; i < cc; i++) {
+    com_ptr<IMMDevice> dev;
+    hr = collection->Item(i, dev.receive());
+    if (FAILED(hr)) {
+      LOG("IMMDeviceCollection::Item(%u) failed: %lx", i-1, hr);
+      continue;
+    }
+    if (wasapi_create_device(context, devices[out->count],
+                             enumerator.get(), dev.get()) == CUBEB_OK) {
+      out->count += 1;
+    }
+  }
+
+  out->device = devices;
+  return CUBEB_OK;
+}
+
+static int
+wasapi_device_collection_destroy(cubeb * /*ctx*/, cubeb_device_collection * collection)
+{
+  XASSERT(collection);
+
+  for (size_t n = 0; n < collection->count; n++) {
+    cubeb_device_info& dev = collection->device[n];
+    delete [] dev.friendly_name;
+    delete [] dev.group_id;
+  }
+
+  delete [] collection->device;
+  return CUBEB_OK;
+}
+
+static int
+wasapi_register_device_collection_changed(cubeb * context,
+                                          cubeb_device_type devtype,
+                                          cubeb_device_collection_changed_callback collection_changed_callback,
+                                          void * user_ptr)
+{
+  if (devtype == CUBEB_DEVICE_TYPE_UNKNOWN) {
+    return CUBEB_ERROR_INVALID_PARAMETER;
+  }
+
+  if (collection_changed_callback) {
+    // Make sure it has been unregistered first.
+    XASSERT(((devtype & CUBEB_DEVICE_TYPE_INPUT) &&
+             !context->input_collection_changed_callback) ||
+            ((devtype & CUBEB_DEVICE_TYPE_OUTPUT) &&
+             !context->output_collection_changed_callback));
+
+    // Stop the notification client. Notifications arrive on
+    // a separate thread. We stop them here to avoid
+    // synchronization issues during the update.
+    if (context->device_collection_enumerator.get()) {
+      HRESULT hr = unregister_collection_notification_client(context);
+      if (FAILED(hr)) {
+        return CUBEB_ERROR;
+      }
+    }
+
+    if (devtype & CUBEB_DEVICE_TYPE_INPUT) {
+      context->input_collection_changed_callback = collection_changed_callback;
+      context->input_collection_changed_user_ptr = user_ptr;
+    }
+    if (devtype & CUBEB_DEVICE_TYPE_OUTPUT) {
+      context->output_collection_changed_callback = collection_changed_callback;
+      context->output_collection_changed_user_ptr = user_ptr;
+    }
+
+    HRESULT hr = register_collection_notification_client(context);
+    if (FAILED(hr)) {
+      return CUBEB_ERROR;
+    }
+  } else {
+    if (!context->device_collection_enumerator.get()) {
+      // Already unregistered, ignore it.
+      return CUBEB_OK;
+    }
+
+    HRESULT hr = unregister_collection_notification_client(context);
+    if (FAILED(hr)) {
+      return CUBEB_ERROR;
+    }
+    if (devtype & CUBEB_DEVICE_TYPE_INPUT) {
+      context->input_collection_changed_callback = nullptr;
+      context->input_collection_changed_user_ptr = nullptr;
+    }
+    if (devtype & CUBEB_DEVICE_TYPE_OUTPUT) {
+      context->output_collection_changed_callback = nullptr;
+      context->output_collection_changed_user_ptr = nullptr;
+    }
+
+    // If after the updates we still have registered
+    // callbacks restart the notification client.
+    if (context->input_collection_changed_callback ||
+        context->output_collection_changed_callback) {
+      hr = register_collection_notification_client(context);
+      if (FAILED(hr)) {
+        return CUBEB_ERROR;
+      }
+    }
+  }
+
+  return CUBEB_OK;
+}
+
+cubeb_ops const wasapi_ops = {
+  /*.init =*/ wasapi_init,
+  /*.get_backend_id =*/ wasapi_get_backend_id,
+  /*.get_max_channel_count =*/ wasapi_get_max_channel_count,
+  /*.get_min_latency =*/ wasapi_get_min_latency,
+  /*.get_preferred_sample_rate =*/ wasapi_get_preferred_sample_rate,
+  /*.enumerate_devices =*/ wasapi_enumerate_devices,
+  /*.device_collection_destroy =*/ wasapi_device_collection_destroy,
+  /*.destroy =*/ wasapi_destroy,
+  /*.stream_init =*/ wasapi_stream_init,
+  /*.stream_destroy =*/ wasapi_stream_destroy,
+  /*.stream_start =*/ wasapi_stream_start,
+  /*.stream_stop =*/ wasapi_stream_stop,
+  /*.stream_reset_default_device =*/ wasapi_stream_reset_default_device,
+  /*.stream_get_position =*/ wasapi_stream_get_position,
+  /*.stream_get_latency =*/ wasapi_stream_get_latency,
+  /*.stream_set_volume =*/ wasapi_stream_set_volume,
+  /*.stream_get_current_device =*/ NULL,
+  /*.stream_device_destroy =*/ NULL,
+  /*.stream_register_device_changed_callback =*/ NULL,
+  /*.register_device_collection_changed =*/ wasapi_register_device_collection_changed,
+};
+} // namespace anonymous
diff --git a/dep/cubeb/src/cubeb_winmm.c b/dep/cubeb/src/cubeb_winmm.c
new file mode 100644
index 000000000..e064ca079
--- /dev/null
+++ b/dep/cubeb/src/cubeb_winmm.c
@@ -0,0 +1,1067 @@
+/*
+ * Copyright © 2011 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+#undef WINVER
+#define WINVER 0x0501
+#undef WIN32_LEAN_AND_MEAN
+
+#include <malloc.h>
+#include <windows.h>
+#include <mmreg.h>
+#include <mmsystem.h>
+#include <process.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "cubeb/cubeb.h"
+#include "cubeb-internal.h"
+
+/* This is missing from the MinGW headers. Use a safe fallback. */
+#if !defined(MEMORY_ALLOCATION_ALIGNMENT)
+#define MEMORY_ALLOCATION_ALIGNMENT 16
+#endif
+
+/**This is also missing from the MinGW headers. It  also appears to be undocumented by Microsoft.*/
+#ifndef WAVE_FORMAT_48M08
+#define WAVE_FORMAT_48M08      0x00001000       /* 48     kHz, Mono, 8-bit */
+#endif
+#ifndef WAVE_FORMAT_48M16
+#define WAVE_FORMAT_48M16      0x00002000       /* 48     kHz, Mono, 16-bit */
+#endif
+#ifndef WAVE_FORMAT_48S08
+#define WAVE_FORMAT_48S08      0x00004000       /* 48     kHz, Stereo, 8-bit */
+#endif
+#ifndef WAVE_FORMAT_48S16
+#define WAVE_FORMAT_48S16      0x00008000       /* 48     kHz, Stereo, 16-bit */
+#endif
+#ifndef WAVE_FORMAT_96M08
+#define WAVE_FORMAT_96M08      0x00010000       /* 96     kHz, Mono, 8-bit */
+#endif
+#ifndef WAVE_FORMAT_96M16
+#define WAVE_FORMAT_96M16      0x00020000       /* 96     kHz, Mono, 16-bit */
+#endif
+#ifndef WAVE_FORMAT_96S08
+#define WAVE_FORMAT_96S08      0x00040000       /* 96     kHz, Stereo, 8-bit */
+#endif
+#ifndef WAVE_FORMAT_96S16
+#define WAVE_FORMAT_96S16      0x00080000       /* 96     kHz, Stereo, 16-bit */
+#endif
+
+/**Taken from winbase.h, also not in MinGW.*/
+#ifndef STACK_SIZE_PARAM_IS_A_RESERVATION
+#define STACK_SIZE_PARAM_IS_A_RESERVATION   0x00010000    // Threads only
+#endif
+
+#ifndef DRVM_MAPPER
+#define DRVM_MAPPER             (0x2000)
+#endif
+#ifndef DRVM_MAPPER_PREFERRED_GET
+#define DRVM_MAPPER_PREFERRED_GET                 (DRVM_MAPPER+21)
+#endif
+#ifndef DRVM_MAPPER_CONSOLEVOICECOM_GET
+#define DRVM_MAPPER_CONSOLEVOICECOM_GET           (DRVM_MAPPER+23)
+#endif
+
+#define CUBEB_STREAM_MAX 32
+#define NBUFS 4
+
+const GUID KSDATAFORMAT_SUBTYPE_PCM =
+{ 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
+const GUID KSDATAFORMAT_SUBTYPE_IEEE_FLOAT =
+{ 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
+
+struct cubeb_stream_item {
+  SLIST_ENTRY head;
+  cubeb_stream * stream;
+};
+
+static struct cubeb_ops const winmm_ops;
+
+struct cubeb {
+  struct cubeb_ops const * ops;
+  HANDLE event;
+  HANDLE thread;
+  int shutdown;
+  PSLIST_HEADER work;
+  CRITICAL_SECTION lock;
+  unsigned int active_streams;
+  unsigned int minimum_latency_ms;
+};
+
+struct cubeb_stream {
+  /* Note: Must match cubeb_stream layout in cubeb.c. */
+  cubeb * context;
+  void * user_ptr;
+  /**/
+  cubeb_stream_params params;
+  cubeb_data_callback data_callback;
+  cubeb_state_callback state_callback;
+  WAVEHDR buffers[NBUFS];
+  size_t buffer_size;
+  int next_buffer;
+  int free_buffers;
+  int shutdown;
+  int draining;
+  HANDLE event;
+  HWAVEOUT waveout;
+  CRITICAL_SECTION lock;
+  uint64_t written;
+  float soft_volume;
+};
+
+static size_t
+bytes_per_frame(cubeb_stream_params params)
+{
+  size_t bytes;
+
+  switch (params.format) {
+  case CUBEB_SAMPLE_S16LE:
+    bytes = sizeof(signed short);
+    break;
+  case CUBEB_SAMPLE_FLOAT32LE:
+    bytes = sizeof(float);
+    break;
+  default:
+    XASSERT(0);
+  }
+
+  return bytes * params.channels;
+}
+
+static WAVEHDR *
+winmm_get_next_buffer(cubeb_stream * stm)
+{
+  WAVEHDR * hdr = NULL;
+
+  XASSERT(stm->free_buffers > 0 && stm->free_buffers <= NBUFS);
+  hdr = &stm->buffers[stm->next_buffer];
+  XASSERT(hdr->dwFlags & WHDR_PREPARED ||
+          (hdr->dwFlags & WHDR_DONE && !(hdr->dwFlags & WHDR_INQUEUE)));
+  stm->next_buffer = (stm->next_buffer + 1) % NBUFS;
+  stm->free_buffers -= 1;
+
+  return hdr;
+}
+
+static void
+winmm_refill_stream(cubeb_stream * stm)
+{
+  WAVEHDR * hdr;
+  long got;
+  long wanted;
+  MMRESULT r;
+
+  EnterCriticalSection(&stm->lock);
+  stm->free_buffers += 1;
+  XASSERT(stm->free_buffers > 0 && stm->free_buffers <= NBUFS);
+
+  if (stm->draining) {
+    LeaveCriticalSection(&stm->lock);
+    if (stm->free_buffers == NBUFS) {
+      stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
+    }
+    SetEvent(stm->event);
+    return;
+  }
+
+  if (stm->shutdown) {
+    LeaveCriticalSection(&stm->lock);
+    SetEvent(stm->event);
+    return;
+  }
+
+  hdr = winmm_get_next_buffer(stm);
+
+  wanted = (DWORD) stm->buffer_size / bytes_per_frame(stm->params);
+
+  /* It is assumed that the caller is holding this lock.  It must be dropped
+     during the callback to avoid deadlocks. */
+  LeaveCriticalSection(&stm->lock);
+  got = stm->data_callback(stm, stm->user_ptr, NULL, hdr->lpData, wanted);
+  EnterCriticalSection(&stm->lock);
+  if (got < 0) {
+    LeaveCriticalSection(&stm->lock);
+    /* XXX handle this case */
+    XASSERT(0);
+    return;
+  } else if (got < wanted) {
+    stm->draining = 1;
+  }
+  stm->written += got;
+
+  XASSERT(hdr->dwFlags & WHDR_PREPARED);
+
+  hdr->dwBufferLength = got * bytes_per_frame(stm->params);
+  XASSERT(hdr->dwBufferLength <= stm->buffer_size);
+
+  if (stm->soft_volume != -1.0) {
+    if (stm->params.format == CUBEB_SAMPLE_FLOAT32NE) {
+      float * b = (float *) hdr->lpData;
+      uint32_t i;
+      for (i = 0; i < got * stm->params.channels; i++) {
+        b[i] *= stm->soft_volume;
+      }
+    } else {
+      short * b = (short *) hdr->lpData;
+      uint32_t i;
+      for (i = 0; i < got * stm->params.channels; i++) {
+        b[i] = (short) (b[i] * stm->soft_volume);
+      }
+    }
+  }
+
+  r = waveOutWrite(stm->waveout, hdr, sizeof(*hdr));
+  if (r != MMSYSERR_NOERROR) {
+    LeaveCriticalSection(&stm->lock);
+    stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
+    return;
+  }
+
+  LeaveCriticalSection(&stm->lock);
+}
+
+static unsigned __stdcall
+winmm_buffer_thread(void * user_ptr)
+{
+  cubeb * ctx = (cubeb *) user_ptr;
+  XASSERT(ctx);
+
+  for (;;) {
+    DWORD r;
+    PSLIST_ENTRY item;
+
+    r = WaitForSingleObject(ctx->event, INFINITE);
+    XASSERT(r == WAIT_OBJECT_0);
+
+    /* Process work items in batches so that a single stream can't
+       starve the others by continuously adding new work to the top of
+       the work item stack. */
+    item = InterlockedFlushSList(ctx->work);
+    while (item != NULL) {
+      PSLIST_ENTRY tmp = item;
+      winmm_refill_stream(((struct cubeb_stream_item *) tmp)->stream);
+      item = item->Next;
+      _aligned_free(tmp);
+    }
+
+    if (ctx->shutdown) {
+      break;
+    }
+  }
+
+  return 0;
+}
+
+static void CALLBACK
+winmm_buffer_callback(HWAVEOUT waveout, UINT msg, DWORD_PTR user_ptr, DWORD_PTR p1, DWORD_PTR p2)
+{
+  cubeb_stream * stm = (cubeb_stream *) user_ptr;
+  struct cubeb_stream_item * item;
+
+  if (msg != WOM_DONE) {
+    return;
+  }
+
+  item = _aligned_malloc(sizeof(struct cubeb_stream_item), MEMORY_ALLOCATION_ALIGNMENT);
+  XASSERT(item);
+  item->stream = stm;
+  InterlockedPushEntrySList(stm->context->work, &item->head);
+
+  SetEvent(stm->context->event);
+}
+
+static unsigned int
+calculate_minimum_latency(void)
+{
+  OSVERSIONINFOEX osvi;
+  DWORDLONG mask;
+
+  /* Running under Terminal Services results in underruns with low latency. */
+  if (GetSystemMetrics(SM_REMOTESESSION) == TRUE) {
+    return 500;
+  }
+
+  /* Vista's WinMM implementation underruns when less than 200ms of audio is buffered. */
+  memset(&osvi, 0, sizeof(OSVERSIONINFOEX));
+  osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+  osvi.dwMajorVersion = 6;
+  osvi.dwMinorVersion = 0;
+
+  mask = 0;
+  VER_SET_CONDITION(mask, VER_MAJORVERSION, VER_EQUAL);
+  VER_SET_CONDITION(mask, VER_MINORVERSION, VER_EQUAL);
+
+  if (VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION, mask) != 0) {
+    return 200;
+  }
+
+  return 100;
+}
+
+static void winmm_destroy(cubeb * ctx);
+
+/*static*/ int
+winmm_init(cubeb ** context, char const * context_name)
+{
+  cubeb * ctx;
+
+  XASSERT(context);
+  *context = NULL;
+
+  /* Don't initialize a context if there are no devices available. */
+  if (waveOutGetNumDevs() == 0) {
+    return CUBEB_ERROR;
+  }
+
+  ctx = calloc(1, sizeof(*ctx));
+  XASSERT(ctx);
+
+  ctx->ops = &winmm_ops;
+
+  ctx->work = _aligned_malloc(sizeof(*ctx->work), MEMORY_ALLOCATION_ALIGNMENT);
+  XASSERT(ctx->work);
+  InitializeSListHead(ctx->work);
+
+  ctx->event = CreateEvent(NULL, FALSE, FALSE, NULL);
+  if (!ctx->event) {
+    winmm_destroy(ctx);
+    return CUBEB_ERROR;
+  }
+
+  ctx->thread = (HANDLE) _beginthreadex(NULL, 256 * 1024, winmm_buffer_thread, ctx, STACK_SIZE_PARAM_IS_A_RESERVATION, NULL);
+  if (!ctx->thread) {
+    winmm_destroy(ctx);
+    return CUBEB_ERROR;
+  }
+
+  SetThreadPriority(ctx->thread, THREAD_PRIORITY_TIME_CRITICAL);
+
+  InitializeCriticalSection(&ctx->lock);
+  ctx->active_streams = 0;
+
+  ctx->minimum_latency_ms = calculate_minimum_latency();
+
+  *context = ctx;
+
+  return CUBEB_OK;
+}
+
+static char const *
+winmm_get_backend_id(cubeb * ctx)
+{
+  return "winmm";
+}
+
+static void
+winmm_destroy(cubeb * ctx)
+{
+  DWORD r;
+
+  XASSERT(ctx->active_streams == 0);
+  XASSERT(!InterlockedPopEntrySList(ctx->work));
+
+  DeleteCriticalSection(&ctx->lock);
+
+  if (ctx->thread) {
+    ctx->shutdown = 1;
+    SetEvent(ctx->event);
+    r = WaitForSingleObject(ctx->thread, INFINITE);
+    XASSERT(r == WAIT_OBJECT_0);
+    CloseHandle(ctx->thread);
+  }
+
+  if (ctx->event) {
+    CloseHandle(ctx->event);
+  }
+
+  _aligned_free(ctx->work);
+
+  free(ctx);
+}
+
+static void winmm_stream_destroy(cubeb_stream * stm);
+
+static int
+winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
+                  cubeb_devid input_device,
+                  cubeb_stream_params * input_stream_params,
+                  cubeb_devid output_device,
+                  cubeb_stream_params * output_stream_params,
+                  unsigned int latency_frames,
+                  cubeb_data_callback data_callback,
+                  cubeb_state_callback state_callback,
+                  void * user_ptr)
+{
+  MMRESULT r;
+  WAVEFORMATEXTENSIBLE wfx;
+  cubeb_stream * stm;
+  int i;
+  size_t bufsz;
+
+  XASSERT(context);
+  XASSERT(stream);
+  XASSERT(output_stream_params);
+
+  if (input_stream_params) {
+    /* Capture support not yet implemented. */
+    return CUBEB_ERROR_NOT_SUPPORTED;
+  }
+
+  if (input_device || output_device) {
+    /* Device selection not yet implemented. */
+    return CUBEB_ERROR_DEVICE_UNAVAILABLE;
+  }
+
+  if (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
+    /* Loopback is not supported */
+    return CUBEB_ERROR_NOT_SUPPORTED;
+  }
+
+  *stream = NULL;
+
+  memset(&wfx, 0, sizeof(wfx));
+  if (output_stream_params->channels > 2) {
+    wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+    wfx.Format.cbSize = sizeof(wfx) - sizeof(wfx.Format);
+  } else {
+    wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
+    if (output_stream_params->format == CUBEB_SAMPLE_FLOAT32LE) {
+      wfx.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
+    }
+    wfx.Format.cbSize = 0;
+  }
+  wfx.Format.nChannels = output_stream_params->channels;
+  wfx.Format.nSamplesPerSec = output_stream_params->rate;
+
+  /* XXX fix channel mappings */
+  wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
+
+  switch (output_stream_params->format) {
+  case CUBEB_SAMPLE_S16LE:
+    wfx.Format.wBitsPerSample = 16;
+    wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+    break;
+  case CUBEB_SAMPLE_FLOAT32LE:
+    wfx.Format.wBitsPerSample = 32;
+    wfx.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+    break;
+  default:
+    return CUBEB_ERROR_INVALID_FORMAT;
+  }
+
+  wfx.Format.nBlockAlign = (wfx.Format.wBitsPerSample * wfx.Format.nChannels) / 8;
+  wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
+  wfx.Samples.wValidBitsPerSample = wfx.Format.wBitsPerSample;
+
+  EnterCriticalSection(&context->lock);
+  /* CUBEB_STREAM_MAX is a horrible hack to avoid a situation where, when
+     many streams are active at once, a subset of them will not consume (via
+     playback) or release (via waveOutReset) their buffers. */
+  if (context->active_streams >= CUBEB_STREAM_MAX) {
+    LeaveCriticalSection(&context->lock);
+    return CUBEB_ERROR;
+  }
+  context->active_streams += 1;
+  LeaveCriticalSection(&context->lock);
+
+  stm = calloc(1, sizeof(*stm));
+  XASSERT(stm);
+
+  stm->context = context;
+
+  stm->params = *output_stream_params;
+
+  stm->data_callback = data_callback;
+  stm->state_callback = state_callback;
+  stm->user_ptr = user_ptr;
+  stm->written = 0;
+
+  uint32_t latency_ms = latency_frames * 1000 / output_stream_params->rate;
+
+  if (latency_ms < context->minimum_latency_ms) {
+    latency_ms = context->minimum_latency_ms;
+  }
+
+  bufsz = (size_t) (stm->params.rate / 1000.0 * latency_ms * bytes_per_frame(stm->params) / NBUFS);
+  if (bufsz % bytes_per_frame(stm->params) != 0) {
+    bufsz += bytes_per_frame(stm->params) - (bufsz % bytes_per_frame(stm->params));
+  }
+  XASSERT(bufsz % bytes_per_frame(stm->params) == 0);
+
+  stm->buffer_size = bufsz;
+
+  InitializeCriticalSection(&stm->lock);
+
+  stm->event = CreateEvent(NULL, FALSE, FALSE, NULL);
+  if (!stm->event) {
+    winmm_stream_destroy(stm);
+    return CUBEB_ERROR;
+  }
+
+  stm->soft_volume = -1.0;
+
+  /* winmm_buffer_callback will be called during waveOutOpen, so all
+     other initialization must be complete before calling it. */
+  r = waveOutOpen(&stm->waveout, WAVE_MAPPER, &wfx.Format,
+                  (DWORD_PTR) winmm_buffer_callback, (DWORD_PTR) stm,
+                  CALLBACK_FUNCTION);
+  if (r != MMSYSERR_NOERROR) {
+    winmm_stream_destroy(stm);
+    return CUBEB_ERROR;
+  }
+
+  r = waveOutPause(stm->waveout);
+  if (r != MMSYSERR_NOERROR) {
+    winmm_stream_destroy(stm);
+    return CUBEB_ERROR;
+  }
+
+  for (i = 0; i < NBUFS; ++i) {
+    WAVEHDR * hdr = &stm->buffers[i];
+
+    hdr->lpData = calloc(1, bufsz);
+    XASSERT(hdr->lpData);
+    hdr->dwBufferLength = bufsz;
+    hdr->dwFlags = 0;
+
+    r = waveOutPrepareHeader(stm->waveout, hdr, sizeof(*hdr));
+    if (r != MMSYSERR_NOERROR) {
+      winmm_stream_destroy(stm);
+      return CUBEB_ERROR;
+    }
+
+    winmm_refill_stream(stm);
+  }
+
+  *stream = stm;
+
+  return CUBEB_OK;
+}
+
+static void
+winmm_stream_destroy(cubeb_stream * stm)
+{
+  int i;
+
+  if (stm->waveout) {
+    MMTIME time;
+    MMRESULT r;
+    int device_valid;
+    int enqueued;
+
+    EnterCriticalSection(&stm->lock);
+    stm->shutdown = 1;
+
+    waveOutReset(stm->waveout);
+
+    /* Don't need this value, we just want the result to detect invalid
+       handle/no device errors than waveOutReset doesn't seem to report. */
+    time.wType = TIME_SAMPLES;
+    r = waveOutGetPosition(stm->waveout, &time, sizeof(time));
+    device_valid = !(r == MMSYSERR_INVALHANDLE || r == MMSYSERR_NODRIVER);
+
+    enqueued = NBUFS - stm->free_buffers;
+    LeaveCriticalSection(&stm->lock);
+
+    /* Wait for all blocks to complete. */
+    while (device_valid && enqueued > 0) {
+      DWORD rv = WaitForSingleObject(stm->event, INFINITE);
+      XASSERT(rv == WAIT_OBJECT_0);
+
+      EnterCriticalSection(&stm->lock);
+      enqueued = NBUFS - stm->free_buffers;
+      LeaveCriticalSection(&stm->lock);
+    }
+
+    EnterCriticalSection(&stm->lock);
+
+    for (i = 0; i < NBUFS; ++i) {
+      if (stm->buffers[i].dwFlags & WHDR_PREPARED) {
+        waveOutUnprepareHeader(stm->waveout, &stm->buffers[i], sizeof(stm->buffers[i]));
+      }
+    }
+
+    waveOutClose(stm->waveout);
+
+    LeaveCriticalSection(&stm->lock);
+  }
+
+  if (stm->event) {
+    CloseHandle(stm->event);
+  }
+
+  DeleteCriticalSection(&stm->lock);
+
+  for (i = 0; i < NBUFS; ++i) {
+    free(stm->buffers[i].lpData);
+  }
+
+  EnterCriticalSection(&stm->context->lock);
+  XASSERT(stm->context->active_streams >= 1);
+  stm->context->active_streams -= 1;
+  LeaveCriticalSection(&stm->context->lock);
+
+  free(stm);
+}
+
+static int
+winmm_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
+{
+  XASSERT(ctx && max_channels);
+
+  /* We don't support more than two channels in this backend. */
+  *max_channels = 2;
+
+  return CUBEB_OK;
+}
+
+static int
+winmm_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency)
+{
+  // 100ms minimum, if we are not in a bizarre configuration.
+  *latency = ctx->minimum_latency_ms * params.rate / 1000;
+
+  return CUBEB_OK;
+}
+
+static int
+winmm_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
+{
+  WAVEOUTCAPS woc;
+  MMRESULT r;
+
+  r = waveOutGetDevCaps(WAVE_MAPPER, &woc, sizeof(WAVEOUTCAPS));
+  if (r != MMSYSERR_NOERROR) {
+    return CUBEB_ERROR;
+  }
+
+  /* Check if we support 48kHz, but not 44.1kHz. */
+  if (!(woc.dwFormats & WAVE_FORMAT_4S16) &&
+      woc.dwFormats & WAVE_FORMAT_48S16) {
+    *rate = 48000;
+    return CUBEB_OK;
+  }
+  /* Prefer 44.1kHz between 44.1kHz and 48kHz. */
+  *rate = 44100;
+
+  return CUBEB_OK;
+}
+
+static int
+winmm_stream_start(cubeb_stream * stm)
+{
+  MMRESULT r;
+
+  EnterCriticalSection(&stm->lock);
+  r = waveOutRestart(stm->waveout);
+  LeaveCriticalSection(&stm->lock);
+
+  if (r != MMSYSERR_NOERROR) {
+    return CUBEB_ERROR;
+  }
+
+  stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
+
+  return CUBEB_OK;
+}
+
+static int
+winmm_stream_stop(cubeb_stream * stm)
+{
+  MMRESULT r;
+
+  EnterCriticalSection(&stm->lock);
+  r = waveOutPause(stm->waveout);
+  LeaveCriticalSection(&stm->lock);
+
+  if (r != MMSYSERR_NOERROR) {
+    return CUBEB_ERROR;
+  }
+
+  stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
+
+  return CUBEB_OK;
+}
+
+static int
+winmm_stream_get_position(cubeb_stream * stm, uint64_t * position)
+{
+  MMRESULT r;
+  MMTIME time;
+
+  EnterCriticalSection(&stm->lock);
+  time.wType = TIME_SAMPLES;
+  r = waveOutGetPosition(stm->waveout, &time, sizeof(time));
+  LeaveCriticalSection(&stm->lock);
+
+  if (r != MMSYSERR_NOERROR || time.wType != TIME_SAMPLES) {
+    return CUBEB_ERROR;
+  }
+
+  *position = time.u.sample;
+
+  return CUBEB_OK;
+}
+
+static int
+winmm_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
+{
+  MMRESULT r;
+  MMTIME time;
+  uint64_t written;
+
+  EnterCriticalSection(&stm->lock);
+  time.wType = TIME_SAMPLES;
+  r = waveOutGetPosition(stm->waveout, &time, sizeof(time));
+  written = stm->written;
+  LeaveCriticalSection(&stm->lock);
+
+  if (r != MMSYSERR_NOERROR || time.wType != TIME_SAMPLES) {
+    return CUBEB_ERROR;
+  }
+
+  XASSERT(written - time.u.sample <= UINT32_MAX);
+  *latency = (uint32_t) (written - time.u.sample);
+
+  return CUBEB_OK;
+}
+
+static int
+winmm_stream_set_volume(cubeb_stream * stm, float volume)
+{
+  EnterCriticalSection(&stm->lock);
+  stm->soft_volume = volume;
+  LeaveCriticalSection(&stm->lock);
+  return CUBEB_OK;
+}
+
+#define MM_11025HZ_MASK (WAVE_FORMAT_1M08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_1S08 | WAVE_FORMAT_1S16)
+#define MM_22050HZ_MASK (WAVE_FORMAT_2M08 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S08 | WAVE_FORMAT_2S16)
+#define MM_44100HZ_MASK (WAVE_FORMAT_4M08 | WAVE_FORMAT_4M16 | WAVE_FORMAT_4S08 | WAVE_FORMAT_4S16)
+#define MM_48000HZ_MASK (WAVE_FORMAT_48M08 | WAVE_FORMAT_48M16 | WAVE_FORMAT_48S08 | WAVE_FORMAT_48S16)
+#define MM_96000HZ_MASK (WAVE_FORMAT_96M08 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S08 | WAVE_FORMAT_96S16)
+static void
+winmm_calculate_device_rate(cubeb_device_info * info, DWORD formats)
+{
+  if (formats & MM_11025HZ_MASK) {
+    info->min_rate = 11025;
+    info->default_rate = 11025;
+    info->max_rate = 11025;
+  }
+  if (formats & MM_22050HZ_MASK) {
+    if (info->min_rate == 0) info->min_rate = 22050;
+    info->max_rate = 22050;
+    info->default_rate = 22050;
+  }
+  if (formats & MM_44100HZ_MASK) {
+    if (info->min_rate == 0) info->min_rate = 44100;
+    info->max_rate = 44100;
+    info->default_rate = 44100;
+  }
+  if (formats & MM_48000HZ_MASK) {
+    if (info->min_rate == 0) info->min_rate = 48000;
+    info->max_rate = 48000;
+    info->default_rate = 48000;
+  }
+  if (formats & MM_96000HZ_MASK) {
+    if (info->min_rate == 0) {
+      info->min_rate = 96000;
+      info->default_rate = 96000;
+    }
+    info->max_rate = 96000;
+  }
+}
+
+#define MM_S16_MASK (WAVE_FORMAT_1M16 | WAVE_FORMAT_1S16 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S16 | WAVE_FORMAT_4M16 | \
+    WAVE_FORMAT_4S16 | WAVE_FORMAT_48M16 | WAVE_FORMAT_48S16 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S16)
+static int
+winmm_query_supported_formats(UINT devid, DWORD formats,
+    cubeb_device_fmt * supfmt, cubeb_device_fmt * deffmt)
+{
+  WAVEFORMATEXTENSIBLE wfx;
+
+  if (formats & MM_S16_MASK)
+    *deffmt = *supfmt = CUBEB_DEVICE_FMT_S16LE;
+  else
+    *deffmt = *supfmt = 0;
+
+  ZeroMemory(&wfx, sizeof(WAVEFORMATEXTENSIBLE));
+  wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+  wfx.Format.nChannels = 2;
+  wfx.Format.nSamplesPerSec = 44100;
+  wfx.Format.wBitsPerSample = 32;
+  wfx.Format.nBlockAlign = (wfx.Format.wBitsPerSample * wfx.Format.nChannels) / 8;
+  wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
+  wfx.Format.cbSize = 22;
+  wfx.Samples.wValidBitsPerSample = wfx.Format.wBitsPerSample;
+  wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
+  wfx.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+  if (waveOutOpen(NULL, devid, &wfx.Format, 0, 0, WAVE_FORMAT_QUERY) == MMSYSERR_NOERROR)
+    *supfmt = (cubeb_device_fmt)(*supfmt | CUBEB_DEVICE_FMT_F32LE);
+
+  return (*deffmt != 0) ? CUBEB_OK : CUBEB_ERROR;
+}
+
+static char *
+guid_to_cstr(LPGUID guid)
+{
+  char * ret = malloc(40);
+  if (!ret) {
+    return NULL;
+  }
+  _snprintf_s(ret, 40, _TRUNCATE,
+      "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
+      guid->Data1, guid->Data2, guid->Data3,
+      guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3],
+      guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]);
+  return ret;
+}
+
+static cubeb_device_pref
+winmm_query_preferred_out_device(UINT devid)
+{
+  DWORD mmpref = WAVE_MAPPER, compref = WAVE_MAPPER, status;
+  cubeb_device_pref ret = CUBEB_DEVICE_PREF_NONE;
+
+  if (waveOutMessage((HWAVEOUT) WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET,
+        (DWORD_PTR)&mmpref, (DWORD_PTR)&status) == MMSYSERR_NOERROR &&
+      devid == mmpref)
+    ret |= CUBEB_DEVICE_PREF_MULTIMEDIA | CUBEB_DEVICE_PREF_NOTIFICATION;
+
+  if (waveOutMessage((HWAVEOUT) WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET,
+        (DWORD_PTR)&compref, (DWORD_PTR)&status) == MMSYSERR_NOERROR &&
+      devid == compref)
+    ret |= CUBEB_DEVICE_PREF_VOICE;
+
+  return ret;
+}
+
+static char *
+device_id_idx(UINT devid)
+{
+  char * ret = malloc(16);
+  if (!ret) {
+    return NULL;
+  }
+  _snprintf_s(ret, 16, _TRUNCATE, "%u", devid);
+  return ret;
+}
+
+static void
+winmm_create_device_from_outcaps2(cubeb_device_info * ret, LPWAVEOUTCAPS2A caps, UINT devid)
+{
+  XASSERT(ret);
+  ret->devid = (cubeb_devid) devid;
+  ret->device_id = device_id_idx(devid);
+  ret->friendly_name = _strdup(caps->szPname);
+  ret->group_id = guid_to_cstr(&caps->ProductGuid);
+  ret->vendor_name = guid_to_cstr(&caps->ManufacturerGuid);
+
+  ret->type = CUBEB_DEVICE_TYPE_OUTPUT;
+  ret->state = CUBEB_DEVICE_STATE_ENABLED;
+  ret->preferred = winmm_query_preferred_out_device(devid);
+
+  ret->max_channels = caps->wChannels;
+  winmm_calculate_device_rate(ret, caps->dwFormats);
+  winmm_query_supported_formats(devid, caps->dwFormats,
+      &ret->format, &ret->default_format);
+
+  /* Hardcoded latency estimates... */
+  ret->latency_lo = 100 * ret->default_rate / 1000;
+  ret->latency_hi = 200 * ret->default_rate / 1000;
+}
+
+static void
+winmm_create_device_from_outcaps(cubeb_device_info * ret, LPWAVEOUTCAPSA caps, UINT devid)
+{
+  XASSERT(ret);
+  ret->devid = (cubeb_devid) devid;
+  ret->device_id = device_id_idx(devid);
+  ret->friendly_name = _strdup(caps->szPname);
+  ret->group_id = NULL;
+  ret->vendor_name = NULL;
+
+  ret->type = CUBEB_DEVICE_TYPE_OUTPUT;
+  ret->state = CUBEB_DEVICE_STATE_ENABLED;
+  ret->preferred = winmm_query_preferred_out_device(devid);
+
+  ret->max_channels = caps->wChannels;
+  winmm_calculate_device_rate(ret, caps->dwFormats);
+  winmm_query_supported_formats(devid, caps->dwFormats,
+      &ret->format, &ret->default_format);
+
+  /* Hardcoded latency estimates... */
+  ret->latency_lo = 100 * ret->default_rate / 1000;
+  ret->latency_hi = 200 * ret->default_rate / 1000;
+}
+
+static cubeb_device_pref
+winmm_query_preferred_in_device(UINT devid)
+{
+  DWORD mmpref = WAVE_MAPPER, compref = WAVE_MAPPER, status;
+  cubeb_device_pref ret = CUBEB_DEVICE_PREF_NONE;
+
+  if (waveInMessage((HWAVEIN) WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET,
+        (DWORD_PTR)&mmpref, (DWORD_PTR)&status) == MMSYSERR_NOERROR &&
+      devid == mmpref)
+    ret |= CUBEB_DEVICE_PREF_MULTIMEDIA | CUBEB_DEVICE_PREF_NOTIFICATION;
+
+  if (waveInMessage((HWAVEIN) WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET,
+        (DWORD_PTR)&compref, (DWORD_PTR)&status) == MMSYSERR_NOERROR &&
+      devid == compref)
+    ret |= CUBEB_DEVICE_PREF_VOICE;
+
+  return ret;
+}
+
+static void
+winmm_create_device_from_incaps2(cubeb_device_info * ret, LPWAVEINCAPS2A caps, UINT devid)
+{
+  XASSERT(ret);
+  ret->devid = (cubeb_devid) devid;
+  ret->device_id = device_id_idx(devid);
+  ret->friendly_name = _strdup(caps->szPname);
+  ret->group_id = guid_to_cstr(&caps->ProductGuid);
+  ret->vendor_name = guid_to_cstr(&caps->ManufacturerGuid);
+
+  ret->type = CUBEB_DEVICE_TYPE_INPUT;
+  ret->state = CUBEB_DEVICE_STATE_ENABLED;
+  ret->preferred = winmm_query_preferred_in_device(devid);
+
+  ret->max_channels = caps->wChannels;
+  winmm_calculate_device_rate(ret, caps->dwFormats);
+  winmm_query_supported_formats(devid, caps->dwFormats,
+      &ret->format, &ret->default_format);
+
+  /* Hardcoded latency estimates... */
+  ret->latency_lo = 100 * ret->default_rate / 1000;
+  ret->latency_hi = 200 * ret->default_rate / 1000;
+}
+
+static void
+winmm_create_device_from_incaps(cubeb_device_info * ret, LPWAVEINCAPSA caps, UINT devid)
+{
+  XASSERT(ret);
+  ret->devid = (cubeb_devid) devid;
+  ret->device_id = device_id_idx(devid);
+  ret->friendly_name = _strdup(caps->szPname);
+  ret->group_id = NULL;
+  ret->vendor_name = NULL;
+
+  ret->type = CUBEB_DEVICE_TYPE_INPUT;
+  ret->state = CUBEB_DEVICE_STATE_ENABLED;
+  ret->preferred = winmm_query_preferred_in_device(devid);
+
+  ret->max_channels = caps->wChannels;
+  winmm_calculate_device_rate(ret, caps->dwFormats);
+  winmm_query_supported_formats(devid, caps->dwFormats,
+      &ret->format, &ret->default_format);
+
+  /* Hardcoded latency estimates... */
+  ret->latency_lo = 100 * ret->default_rate / 1000;
+  ret->latency_hi = 200 * ret->default_rate / 1000;
+}
+
+static int
+winmm_enumerate_devices(cubeb * context, cubeb_device_type type,
+                        cubeb_device_collection * collection)
+{
+  UINT i, incount, outcount, total;
+  cubeb_device_info * devices;
+  cubeb_device_info * dev;
+
+  outcount = waveOutGetNumDevs();
+  incount = waveInGetNumDevs();
+  total = outcount + incount;
+
+  devices = calloc(total, sizeof(cubeb_device_info));
+  collection->count = 0;
+
+  if (type & CUBEB_DEVICE_TYPE_OUTPUT) {
+    WAVEOUTCAPSA woc;
+    WAVEOUTCAPS2A woc2;
+
+    ZeroMemory(&woc, sizeof(woc));
+    ZeroMemory(&woc2, sizeof(woc2));
+
+    for (i = 0; i < outcount; i++) {
+      dev = &devices[collection->count];
+      if (waveOutGetDevCapsA(i, (LPWAVEOUTCAPSA)&woc2, sizeof(woc2)) == MMSYSERR_NOERROR) {
+        winmm_create_device_from_outcaps2(dev, &woc2, i);
+        collection->count += 1;
+      } else if (waveOutGetDevCapsA(i, &woc, sizeof(woc)) == MMSYSERR_NOERROR) {
+        winmm_create_device_from_outcaps(dev, &woc, i);
+        collection->count += 1;
+      }
+    }
+  }
+
+  if (type & CUBEB_DEVICE_TYPE_INPUT) {
+    WAVEINCAPSA wic;
+    WAVEINCAPS2A wic2;
+
+    ZeroMemory(&wic, sizeof(wic));
+    ZeroMemory(&wic2, sizeof(wic2));
+
+    for (i = 0; i < incount; i++) {
+      dev = &devices[collection->count];
+      if (waveInGetDevCapsA(i, (LPWAVEINCAPSA)&wic2, sizeof(wic2)) == MMSYSERR_NOERROR) {
+        winmm_create_device_from_incaps2(dev, &wic2, i);
+        collection->count += 1;
+      } else if (waveInGetDevCapsA(i, &wic, sizeof(wic)) == MMSYSERR_NOERROR) {
+        winmm_create_device_from_incaps(dev, &wic, i);
+        collection->count += 1;
+      }
+    }
+  }
+
+  collection->device = devices;
+
+  return CUBEB_OK;
+}
+
+static int
+winmm_device_collection_destroy(cubeb * ctx,
+                                cubeb_device_collection * collection)
+{
+  uint32_t i;
+  XASSERT(collection);
+
+  (void) ctx;
+
+  for (i = 0; i < collection->count; i++) {
+    free((void *) collection->device[i].device_id);
+    free((void *) collection->device[i].friendly_name);
+    free((void *) collection->device[i].group_id);
+    free((void *) collection->device[i].vendor_name);
+  }
+
+  free(collection->device);
+  return CUBEB_OK;
+}
+
+static struct cubeb_ops const winmm_ops = {
+  /*.init =*/ winmm_init,
+  /*.get_backend_id =*/ winmm_get_backend_id,
+  /*.get_max_channel_count=*/ winmm_get_max_channel_count,
+  /*.get_min_latency=*/ winmm_get_min_latency,
+  /*.get_preferred_sample_rate =*/ winmm_get_preferred_sample_rate,
+  /*.enumerate_devices =*/ winmm_enumerate_devices,
+  /*.device_collection_destroy =*/ winmm_device_collection_destroy,
+  /*.destroy =*/ winmm_destroy,
+  /*.stream_init =*/ winmm_stream_init,
+  /*.stream_destroy =*/ winmm_stream_destroy,
+  /*.stream_start =*/ winmm_stream_start,
+  /*.stream_stop =*/ winmm_stream_stop,
+  /*.stream_reset_default_device =*/ NULL,
+  /*.stream_get_position =*/ winmm_stream_get_position,
+  /*.stream_get_latency = */ winmm_stream_get_latency,
+  /*.stream_set_volume =*/ winmm_stream_set_volume,
+  /*.stream_get_current_device =*/ NULL,
+  /*.stream_device_destroy =*/ NULL,
+  /*.stream_register_device_changed_callback=*/ NULL,
+  /*.register_device_collection_changed =*/ NULL
+};
diff --git a/dep/cubeb/src/speex/arch.h b/dep/cubeb/src/speex/arch.h
new file mode 100644
index 000000000..73a45a069
--- /dev/null
+++ b/dep/cubeb/src/speex/arch.h
@@ -0,0 +1,235 @@
+/* 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 (ARM4_ASM)) && defined(BFIN_ASM)) || (defined (ARM4_ASM)&&defined(ARM5E_ASM))
+#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_POINT_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_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_Q11(a,b)     ((a)*(b))
+#define MULT16_32_Q13(a,b)     ((a)*(b))
+#define MULT16_32_Q14(a,b)     ((a)*(b))
+#define MULT16_32_Q15(a,b)     ((a)*(b))
+#define MULT16_32_P15(a,b)     ((a)*(b))
+
+#define MAC16_32_Q11(c,a,b)     ((c)+(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/dep/cubeb/src/speex/fixed_generic.h b/dep/cubeb/src/speex/fixed_generic.h
new file mode 100644
index 000000000..12d27aac5
--- /dev/null
+++ b/dep/cubeb/src/speex/fixed_generic.h
@@ -0,0 +1,110 @@
+/* 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))))
+
+/* (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_Q12(a,b) ADD32(MULT16_16((a),SHR((b),12)), SHR(MULT16_16((a),((b)&0x00000fff)),12))
+#define MULT16_32_Q13(a,b) ADD32(MULT16_16((a),SHR((b),13)), SHR(MULT16_16((a),((b)&0x00001fff)),13))
+#define MULT16_32_Q14(a,b) ADD32(MULT16_16((a),SHR((b),14)), SHR(MULT16_16((a),((b)&0x00003fff)),14))
+
+#define MULT16_32_Q11(a,b) ADD32(MULT16_16((a),SHR((b),11)), SHR(MULT16_16((a),((b)&0x000007ff)),11))
+#define MAC16_32_Q11(c,a,b) ADD32(c,ADD32(MULT16_16((a),SHR((b),11)), SHR(MULT16_16((a),((b)&0x000007ff)),11)))
+
+#define MULT16_32_P15(a,b) ADD32(MULT16_16((a),SHR((b),15)), PSHR(MULT16_16((a),((b)&0x00007fff)),15))
+#define MULT16_32_Q15(a,b) ADD32(MULT16_16((a),SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15))
+#define MAC16_32_Q15(c,a,b) ADD32(c,ADD32(MULT16_16((a),SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15)))
+
+
+#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/dep/cubeb/src/speex/resample.c b/dep/cubeb/src/speex/resample.c
new file mode 100644
index 000000000..10cb06593
--- /dev/null
+++ b/dep/cubeb/src/speex/resample.c
@@ -0,0 +1,1240 @@
+/* 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 http://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);}
+#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 "stack_alloc.h"
+#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 4294967296U
+#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 = {kaiser12_table, 64};
+#define KAISER12 (&_KAISER12)
+/*static struct FuncDef _KAISER12 = {kaiser12_table, 32};
+#define KAISER12 (&_KAISER12)*/
+static const struct FuncDef _KAISER10 = {kaiser10_table, 32};
+#define KAISER10 (&_KAISER10)
+static const struct FuncDef _KAISER8 = {kaiser8_table, 32};
+#define KAISER8 (&_KAISER8)
+static const struct FuncDef _KAISER6 = {kaiser6_table, 32};
+#define KAISER6 (&_KAISER6)
+
+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],SHR32(accum[0], 1)) + MULT16_32_Q15(interp[1],SHR32(accum[1], 1)) + MULT16_32_Q15(interp[2],SHR32(accum[2], 1)) + MULT16_32_Q15(interp[3],SHR32(accum[3], 1));
+      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;
+
+   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 _muldiv(spx_uint32_t *result, spx_uint32_t value, spx_uint32_t mul, spx_uint32_t div)
+{
+   speex_assert(result);
+   spx_uint32_t major = value / div;
+   spx_uint32_t remainder = value % div;
+   /* TODO: Could use 64 bits operation to check for overflow. But only guaranteed in C99+ */
+   if (remainder > UINT32_MAX / mul || major > UINT32_MAX / mul
+       || major * mul > UINT32_MAX - remainder * mul / div)
+      return RESAMPLER_ERR_OVERFLOW;
+   *result = remainder * mul / div + major * mul;
+   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 (_muldiv(&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;
+   }
+
+   /* Choose the resampling type that requires the least amount of memory */
+#ifdef RESAMPLE_FULL_SINC_TABLE
+   use_direct = 1;
+   if (INT_MAX/sizeof(spx_word16_t)/st->den_rate < st->filt_len)
+      goto fail;
+#else
+   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;
+   VARDECL(spx_word16_t *ystack);
+   ALLOC(ystack, ylen, spx_word16_t);
+#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 _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 = _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 (_muldiv(&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/dep/cubeb/src/speex/resample_neon.h b/dep/cubeb/src/speex/resample_neon.h
new file mode 100644
index 000000000..0acbd27b9
--- /dev/null
+++ b/dep/cubeb/src/speex/resample_neon.h
@@ -0,0 +1,201 @@
+/* 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.
+*/
+
+#include <arm_neon.h>
+
+#ifdef FIXED_POINT
+#ifdef __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 */
+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;
+}
+#elif defined(FLOATING_POINT)
+
+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;
+}
+#undef WORD2INT
+#define WORD2INT(x) (saturate_float_to_16bit(x))
+
+#define OVERRIDE_INNER_PRODUCT_SINGLE
+/* Only works when len % 4 == 0 */
+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
diff --git a/dep/cubeb/src/speex/resample_sse.h b/dep/cubeb/src/speex/resample_sse.h
new file mode 100644
index 000000000..fed5b8276
--- /dev/null
+++ b/dep/cubeb/src/speex/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/dep/cubeb/src/speex/speex_config_types.h b/dep/cubeb/src/speex/speex_config_types.h
new file mode 100644
index 000000000..cea362890
--- /dev/null
+++ b/dep/cubeb/src/speex/speex_config_types.h
@@ -0,0 +1,10 @@
+#ifndef __SPEEX_TYPES_H__
+#define __SPEEX_TYPES_H__
+
+/* these are filled in by configure */
+typedef int16_t spx_int16_t;
+typedef uint16_t spx_uint16_t;
+typedef int32_t spx_int32_t;
+typedef uint32_t spx_uint32_t;
+
+ #endif
diff --git a/dep/cubeb/src/speex/speex_resampler.h b/dep/cubeb/src/speex/speex_resampler.h
new file mode 100644
index 000000000..901de37b3
--- /dev/null
+++ b/dep/cubeb/src/speex/speex_resampler.h
@@ -0,0 +1,343 @@
+/* Copyright (C) 2007 Jean-Marc Valin
+
+   File: speex_resampler.h
+   Resampling code
+
+   The design goals of this code are:
+      - Very fast algorithm
+      - Low memory requirement
+      - Good *perceptual* quality (and not best SNR)
+
+   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 SPEEX_RESAMPLER_H
+#define SPEEX_RESAMPLER_H
+
+#ifdef OUTSIDE_SPEEX
+
+/********* WARNING: MENTAL SANITY ENDS HERE *************/
+
+/* If the resampler is defined outside of Speex, we change the symbol names so that
+   there won't be any clash if linking with Speex later on. */
+
+/* #define RANDOM_PREFIX your software name here */
+#ifndef RANDOM_PREFIX
+#error "Please define RANDOM_PREFIX (above) to something specific to your project to prevent symbol name clashes"
+#endif
+
+#define CAT_PREFIX2(a,b) a ## b
+#define CAT_PREFIX(a,b) CAT_PREFIX2(a, b)
+
+#define speex_resampler_init CAT_PREFIX(RANDOM_PREFIX,_resampler_init)
+#define speex_resampler_init_frac CAT_PREFIX(RANDOM_PREFIX,_resampler_init_frac)
+#define speex_resampler_destroy CAT_PREFIX(RANDOM_PREFIX,_resampler_destroy)
+#define speex_resampler_process_float CAT_PREFIX(RANDOM_PREFIX,_resampler_process_float)
+#define speex_resampler_process_int CAT_PREFIX(RANDOM_PREFIX,_resampler_process_int)
+#define speex_resampler_process_interleaved_float CAT_PREFIX(RANDOM_PREFIX,_resampler_process_interleaved_float)
+#define speex_resampler_process_interleaved_int CAT_PREFIX(RANDOM_PREFIX,_resampler_process_interleaved_int)
+#define speex_resampler_set_rate CAT_PREFIX(RANDOM_PREFIX,_resampler_set_rate)
+#define speex_resampler_get_rate CAT_PREFIX(RANDOM_PREFIX,_resampler_get_rate)
+#define speex_resampler_set_rate_frac CAT_PREFIX(RANDOM_PREFIX,_resampler_set_rate_frac)
+#define speex_resampler_get_ratio CAT_PREFIX(RANDOM_PREFIX,_resampler_get_ratio)
+#define speex_resampler_set_quality CAT_PREFIX(RANDOM_PREFIX,_resampler_set_quality)
+#define speex_resampler_get_quality CAT_PREFIX(RANDOM_PREFIX,_resampler_get_quality)
+#define speex_resampler_set_input_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_set_input_stride)
+#define speex_resampler_get_input_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_get_input_stride)
+#define speex_resampler_set_output_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_set_output_stride)
+#define speex_resampler_get_output_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_get_output_stride)
+#define speex_resampler_get_input_latency CAT_PREFIX(RANDOM_PREFIX,_resampler_get_input_latency)
+#define speex_resampler_get_output_latency CAT_PREFIX(RANDOM_PREFIX,_resampler_get_output_latency)
+#define speex_resampler_skip_zeros CAT_PREFIX(RANDOM_PREFIX,_resampler_skip_zeros)
+#define speex_resampler_reset_mem CAT_PREFIX(RANDOM_PREFIX,_resampler_reset_mem)
+#define speex_resampler_strerror CAT_PREFIX(RANDOM_PREFIX,_resampler_strerror)
+
+#define spx_int16_t short
+#define spx_int32_t int
+#define spx_uint16_t unsigned short
+#define spx_uint32_t unsigned int
+
+#define speex_assert(cond)
+
+#else /* OUTSIDE_SPEEX */
+
+#include "speexdsp_types.h"
+
+#endif /* OUTSIDE_SPEEX */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SPEEX_RESAMPLER_QUALITY_MAX 10
+#define SPEEX_RESAMPLER_QUALITY_MIN 0
+#define SPEEX_RESAMPLER_QUALITY_DEFAULT 4
+#define SPEEX_RESAMPLER_QUALITY_VOIP 3
+#define SPEEX_RESAMPLER_QUALITY_DESKTOP 5
+
+enum {
+   RESAMPLER_ERR_SUCCESS         = 0,
+   RESAMPLER_ERR_ALLOC_FAILED    = 1,
+   RESAMPLER_ERR_BAD_STATE       = 2,
+   RESAMPLER_ERR_INVALID_ARG     = 3,
+   RESAMPLER_ERR_PTR_OVERLAP     = 4,
+   RESAMPLER_ERR_OVERFLOW        = 5,
+
+   RESAMPLER_ERR_MAX_ERROR
+};
+
+struct SpeexResamplerState_;
+typedef struct SpeexResamplerState_ SpeexResamplerState;
+
+/** Create a new resampler with integer input and output rates.
+ * @param nb_channels Number of channels to be processed
+ * @param in_rate Input sampling rate (integer number of Hz).
+ * @param out_rate Output sampling rate (integer number of Hz).
+ * @param quality Resampling quality between 0 and 10, where 0 has poor quality
+ * and 10 has very high quality.
+ * @return Newly created resampler state
+ * @retval NULL Error: not enough memory
+ */
+SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels,
+                                          spx_uint32_t in_rate,
+                                          spx_uint32_t out_rate,
+                                          int quality,
+                                          int *err);
+
+/** Create a new resampler with fractional input/output rates. The sampling
+ * rate ratio is an arbitrary rational number with both the numerator and
+ * denominator being 32-bit integers.
+ * @param nb_channels Number of channels to be processed
+ * @param ratio_num Numerator of the sampling rate ratio
+ * @param ratio_den Denominator of the sampling rate ratio
+ * @param in_rate Input sampling rate rounded to the nearest integer (in Hz).
+ * @param out_rate Output sampling rate rounded to the nearest integer (in Hz).
+ * @param quality Resampling quality between 0 and 10, where 0 has poor quality
+ * and 10 has very high quality.
+ * @return Newly created resampler state
+ * @retval NULL Error: not enough memory
+ */
+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);
+
+/** Destroy a resampler state.
+ * @param st Resampler state
+ */
+void speex_resampler_destroy(SpeexResamplerState *st);
+
+/** Resample a float array. The input and output buffers must *not* overlap.
+ * @param st Resampler state
+ * @param channel_index Index of the channel to process for the multi-channel
+ * base (0 otherwise)
+ * @param in Input buffer
+ * @param in_len Number of input samples in the input buffer. Returns the
+ * number of samples processed
+ * @param out Output buffer
+ * @param out_len Size of the output buffer. Returns the number of samples written
+ */
+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);
+
+/** Resample an int array. The input and output buffers must *not* overlap.
+ * @param st Resampler state
+ * @param channel_index Index of the channel to process for the multi-channel
+ * base (0 otherwise)
+ * @param in Input buffer
+ * @param in_len Number of input samples in the input buffer. Returns the number
+ * of samples processed
+ * @param out Output buffer
+ * @param out_len Size of the output buffer. Returns the number of samples written
+ */
+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);
+
+/** Resample an interleaved float array. The input and output buffers must *not* overlap.
+ * @param st Resampler state
+ * @param in Input buffer
+ * @param in_len Number of input samples in the input buffer. Returns the number
+ * of samples processed. This is all per-channel.
+ * @param out Output buffer
+ * @param out_len Size of the output buffer. Returns the number of samples written.
+ * This is all per-channel.
+ */
+int speex_resampler_process_interleaved_float(SpeexResamplerState *st,
+                                               const float *in,
+                                               spx_uint32_t *in_len,
+                                               float *out,
+                                               spx_uint32_t *out_len);
+
+/** Resample an interleaved int array. The input and output buffers must *not* overlap.
+ * @param st Resampler state
+ * @param in Input buffer
+ * @param in_len Number of input samples in the input buffer. Returns the number
+ * of samples processed. This is all per-channel.
+ * @param out Output buffer
+ * @param out_len Size of the output buffer. Returns the number of samples written.
+ * This is all per-channel.
+ */
+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);
+
+/** Set (change) the input/output sampling rates (integer value).
+ * @param st Resampler state
+ * @param in_rate Input sampling rate (integer number of Hz).
+ * @param out_rate Output sampling rate (integer number of Hz).
+ */
+int speex_resampler_set_rate(SpeexResamplerState *st,
+                              spx_uint32_t in_rate,
+                              spx_uint32_t out_rate);
+
+/** Get the current input/output sampling rates (integer value).
+ * @param st Resampler state
+ * @param in_rate Input sampling rate (integer number of Hz) copied.
+ * @param out_rate Output sampling rate (integer number of Hz) copied.
+ */
+void speex_resampler_get_rate(SpeexResamplerState *st,
+                              spx_uint32_t *in_rate,
+                              spx_uint32_t *out_rate);
+
+/** Set (change) the input/output sampling rates and resampling ratio
+ * (fractional values in Hz supported).
+ * @param st Resampler state
+ * @param ratio_num Numerator of the sampling rate ratio
+ * @param ratio_den Denominator of the sampling rate ratio
+ * @param in_rate Input sampling rate rounded to the nearest integer (in Hz).
+ * @param out_rate Output sampling rate rounded to the nearest integer (in Hz).
+ */
+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);
+
+/** Get the current resampling ratio. This will be reduced to the least
+ * common denominator.
+ * @param st Resampler state
+ * @param ratio_num Numerator of the sampling rate ratio copied
+ * @param ratio_den Denominator of the sampling rate ratio copied
+ */
+void speex_resampler_get_ratio(SpeexResamplerState *st,
+                               spx_uint32_t *ratio_num,
+                               spx_uint32_t *ratio_den);
+
+/** Set (change) the conversion quality.
+ * @param st Resampler state
+ * @param quality Resampling quality between 0 and 10, where 0 has poor
+ * quality and 10 has very high quality.
+ */
+int speex_resampler_set_quality(SpeexResamplerState *st,
+                                 int quality);
+
+/** Get the conversion quality.
+ * @param st Resampler state
+ * @param quality Resampling quality between 0 and 10, where 0 has poor
+ * quality and 10 has very high quality.
+ */
+void speex_resampler_get_quality(SpeexResamplerState *st,
+                                 int *quality);
+
+/** Set (change) the input stride.
+ * @param st Resampler state
+ * @param stride Input stride
+ */
+void speex_resampler_set_input_stride(SpeexResamplerState *st,
+                                      spx_uint32_t stride);
+
+/** Get the input stride.
+ * @param st Resampler state
+ * @param stride Input stride copied
+ */
+void speex_resampler_get_input_stride(SpeexResamplerState *st,
+                                      spx_uint32_t *stride);
+
+/** Set (change) the output stride.
+ * @param st Resampler state
+ * @param stride Output stride
+ */
+void speex_resampler_set_output_stride(SpeexResamplerState *st,
+                                      spx_uint32_t stride);
+
+/** Get the output stride.
+ * @param st Resampler state copied
+ * @param stride Output stride
+ */
+void speex_resampler_get_output_stride(SpeexResamplerState *st,
+                                      spx_uint32_t *stride);
+
+/** Get the latency introduced by the resampler measured in input samples.
+ * @param st Resampler state
+ */
+int speex_resampler_get_input_latency(SpeexResamplerState *st);
+
+/** Get the latency introduced by the resampler measured in output samples.
+ * @param st Resampler state
+ */
+int speex_resampler_get_output_latency(SpeexResamplerState *st);
+
+/** Make sure that the first samples to go out of the resamplers don't have
+ * leading zeros. This is only useful before starting to use a newly created
+ * resampler. It is recommended to use that when resampling an audio file, as
+ * it will generate a file with the same length. For real-time processing,
+ * it is probably easier not to use this call (so that the output duration
+ * is the same for the first frame).
+ * @param st Resampler state
+ */
+int speex_resampler_skip_zeros(SpeexResamplerState *st);
+
+/** Reset a resampler so a new (unrelated) stream can be processed.
+ * @param st Resampler state
+ */
+int speex_resampler_reset_mem(SpeexResamplerState *st);
+
+/** Returns the English meaning for an error code
+ * @param err Error code
+ * @return English string
+ */
+const char *speex_resampler_strerror(int err);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/dep/cubeb/src/speex/stack_alloc.h b/dep/cubeb/src/speex/stack_alloc.h
new file mode 100644
index 000000000..6c56334f8
--- /dev/null
+++ b/dep/cubeb/src/speex/stack_alloc.h
@@ -0,0 +1,115 @@
+/* Copyright (C) 2002 Jean-Marc Valin */
+/**
+   @file stack_alloc.h
+   @brief Temporary memory allocation on stack
+*/
+/*
+   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 STACK_ALLOC_H
+#define STACK_ALLOC_H
+
+#ifdef USE_ALLOCA
+# ifdef WIN32
+#  include <malloc.h>
+# else
+#  ifdef HAVE_ALLOCA_H
+#   include <alloca.h>
+#  else
+#   include <stdlib.h>
+#  endif
+# endif
+#endif
+
+/**
+ * @def ALIGN(stack, size)
+ *
+ * Aligns the stack to a 'size' boundary
+ *
+ * @param stack Stack
+ * @param size  New size boundary
+ */
+
+/**
+ * @def PUSH(stack, size, type)
+ *
+ * Allocates 'size' elements of type 'type' on the stack
+ *
+ * @param stack Stack
+ * @param size  Number of elements
+ * @param type  Type of element
+ */
+
+/**
+ * @def VARDECL(var)
+ *
+ * Declare variable on stack
+ *
+ * @param var Variable to declare
+ */
+
+/**
+ * @def ALLOC(var, size, type)
+ *
+ * Allocate 'size' elements of 'type' on stack
+ *
+ * @param var  Name of variable to allocate
+ * @param size Number of elements
+ * @param type Type of element
+ */
+
+#ifdef ENABLE_VALGRIND
+
+#include <valgrind/memcheck.h>
+
+#define ALIGN(stack, size) ((stack) += ((size) - (long)(stack)) & ((size) - 1))
+
+#define PUSH(stack, size, type) (VALGRIND_MAKE_NOACCESS(stack, 1000),ALIGN((stack),sizeof(type)),VALGRIND_MAKE_WRITABLE(stack, ((size)*sizeof(type))),(stack)+=((size)*sizeof(type)),(type*)((stack)-((size)*sizeof(type))))
+
+#else
+
+#define ALIGN(stack, size) ((stack) += ((size) - (long)(stack)) & ((size) - 1))
+
+#define PUSH(stack, size, type) (ALIGN((stack),sizeof(type)),(stack)+=((size)*sizeof(type)),(type*)((stack)-((size)*sizeof(type))))
+
+#endif
+
+#if defined(VAR_ARRAYS)
+#define VARDECL(var)
+#define ALLOC(var, size, type) type var[size]
+#elif defined(USE_ALLOCA)
+#define VARDECL(var) var
+#define ALLOC(var, size, type) var = alloca(sizeof(type)*(size))
+#else
+#define VARDECL(var) var
+#define ALLOC(var, size, type) var = PUSH(stack, size, type)
+#endif
+
+
+#endif
diff --git a/duckstation.sln b/duckstation.sln
index 9681c0464..ea78763ad 100644
--- a/duckstation.sln
+++ b/duckstation.sln
@@ -27,6 +27,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tinyxml2", "dep\tinyxml2\ti
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "duckstation-qt", "src\duckstation-qt\duckstation-qt.vcxproj", "{28F14272-0EC4-41BB-849F-182ADB81AF70}"
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cubeb", "dep\cubeb\cubeb.vcxproj", "{72F9423C-91EE-4487-AAC6-555ED6F61AA1}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|x64 = Debug|x64
@@ -211,6 +213,22 @@ Global
 		{28F14272-0EC4-41BB-849F-182ADB81AF70}.ReleaseLTCG|x64.ActiveCfg = ReleaseLTCG|x64
 		{28F14272-0EC4-41BB-849F-182ADB81AF70}.ReleaseLTCG|x86.ActiveCfg = ReleaseLTCG|Win32
 		{28F14272-0EC4-41BB-849F-182ADB81AF70}.ReleaseLTCG|x86.Build.0 = ReleaseLTCG|Win32
+		{72F9423C-91EE-4487-AAC6-555ED6F61AA1}.Debug|x64.ActiveCfg = Debug|x64
+		{72F9423C-91EE-4487-AAC6-555ED6F61AA1}.Debug|x64.Build.0 = Debug|x64
+		{72F9423C-91EE-4487-AAC6-555ED6F61AA1}.Debug|x86.ActiveCfg = Debug|Win32
+		{72F9423C-91EE-4487-AAC6-555ED6F61AA1}.Debug|x86.Build.0 = Debug|Win32
+		{72F9423C-91EE-4487-AAC6-555ED6F61AA1}.DebugFast|x64.ActiveCfg = DebugFast|x64
+		{72F9423C-91EE-4487-AAC6-555ED6F61AA1}.DebugFast|x64.Build.0 = DebugFast|x64
+		{72F9423C-91EE-4487-AAC6-555ED6F61AA1}.DebugFast|x86.ActiveCfg = DebugFast|Win32
+		{72F9423C-91EE-4487-AAC6-555ED6F61AA1}.DebugFast|x86.Build.0 = DebugFast|Win32
+		{72F9423C-91EE-4487-AAC6-555ED6F61AA1}.Release|x64.ActiveCfg = Release|x64
+		{72F9423C-91EE-4487-AAC6-555ED6F61AA1}.Release|x64.Build.0 = Release|x64
+		{72F9423C-91EE-4487-AAC6-555ED6F61AA1}.Release|x86.ActiveCfg = Release|Win32
+		{72F9423C-91EE-4487-AAC6-555ED6F61AA1}.Release|x86.Build.0 = Release|Win32
+		{72F9423C-91EE-4487-AAC6-555ED6F61AA1}.ReleaseLTCG|x64.ActiveCfg = ReleaseLTCG|x64
+		{72F9423C-91EE-4487-AAC6-555ED6F61AA1}.ReleaseLTCG|x64.Build.0 = ReleaseLTCG|x64
+		{72F9423C-91EE-4487-AAC6-555ED6F61AA1}.ReleaseLTCG|x86.ActiveCfg = ReleaseLTCG|Win32
+		{72F9423C-91EE-4487-AAC6-555ED6F61AA1}.ReleaseLTCG|x86.Build.0 = ReleaseLTCG|Win32
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -223,6 +241,7 @@ Global
 		{ACE32F47-2960-4FB3-9F77-2C375625BF61} = {BA490C0E-497D-4634-A21E-E65012006385}
 		{3773F4CC-614E-4028-8595-22E08CA649E3} = {BA490C0E-497D-4634-A21E-E65012006385}
 		{933118A9-68C5-47B4-B151-B03C93961623} = {BA490C0E-497D-4634-A21E-E65012006385}
+		{72F9423C-91EE-4487-AAC6-555ED6F61AA1} = {BA490C0E-497D-4634-A21E-E65012006385}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {26E40B32-7C1D-48D0-95F4-1A500E054028}
diff --git a/src/common/audio_stream.h b/src/common/audio_stream.h
index 1160e6f36..ea8e73e0c 100644
--- a/src/common/audio_stream.h
+++ b/src/common/audio_stream.h
@@ -41,6 +41,10 @@ public:
   void WriteSamples(const SampleType* samples, u32 num_samples);
   void EndWrite(u32 num_samples);
 
+  static std::unique_ptr<AudioStream> CreateNullAudioStream();
+
+  static std::unique_ptr<AudioStream> CreateCubebAudioStream();
+
 protected:
   virtual bool OpenDevice() = 0;
   virtual void PauseDevice(bool paused) = 0;
diff --git a/src/common/common.vcxproj b/src/common/common.vcxproj
index 9dc19f2f8..f29ab73f5 100644
--- a/src/common/common.vcxproj
+++ b/src/common/common.vcxproj
@@ -42,6 +42,7 @@
     <ClInclude Include="byte_stream.h" />
     <ClInclude Include="cd_image.h" />
     <ClInclude Include="cpu_detect.h" />
+    <ClInclude Include="cubeb_audio_stream.h" />
     <ClInclude Include="d3d11\shader_compiler.h" />
     <ClInclude Include="d3d11\staging_texture.h" />
     <ClInclude Include="d3d11\stream_buffer.h" />
@@ -74,6 +75,7 @@
     <ClCompile Include="cd_image.cpp" />
     <ClCompile Include="cd_image_bin.cpp" />
     <ClCompile Include="cd_image_cue.cpp" />
+    <ClCompile Include="cubeb_audio_stream.cpp" />
     <ClCompile Include="d3d11\shader_compiler.cpp" />
     <ClCompile Include="d3d11\staging_texture.cpp" />
     <ClCompile Include="d3d11\stream_buffer.cpp" />
@@ -99,6 +101,9 @@
     <Natvis Include="bitfield.natvis" />
   </ItemGroup>
   <ItemGroup>
+    <ProjectReference Include="..\..\dep\cubeb\cubeb.vcxproj">
+      <Project>{72f9423c-91ee-4487-aac6-555ed6f61aa1}</Project>
+    </ProjectReference>
     <ProjectReference Include="..\..\dep\glad\glad.vcxproj">
       <Project>{43540154-9e1e-409c-834f-b84be5621388}</Project>
     </ProjectReference>
@@ -243,7 +248,7 @@
       <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <SDLCheck>true</SDLCheck>
       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
-      <AdditionalIncludeDirectories>$(SolutionDir)dep\glad\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>$(SolutionDir)dep\glad\include;$(SolutionDir)dep\cubeb\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>
       <LanguageStandard>stdcpp17</LanguageStandard>
       <ConformanceMode>true</ConformanceMode>
@@ -269,7 +274,7 @@
       <PreprocessorDefinitions>_ITERATOR_DEBUG_LEVEL=1;WIN32;_DEBUGFAST;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <SDLCheck>true</SDLCheck>
       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
-      <AdditionalIncludeDirectories>$(SolutionDir)dep\glad\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>$(SolutionDir)dep\glad\include;$(SolutionDir)dep\cubeb\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <BasicRuntimeChecks>Default</BasicRuntimeChecks>
       <SupportJustMyCode>false</SupportJustMyCode>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>
@@ -298,7 +303,7 @@
       <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <SDLCheck>true</SDLCheck>
       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
-      <AdditionalIncludeDirectories>$(SolutionDir)dep\glad\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>$(SolutionDir)dep\glad\include;$(SolutionDir)dep\cubeb\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>
       <LanguageStandard>stdcpp17</LanguageStandard>
       <ConformanceMode>true</ConformanceMode>
@@ -324,7 +329,7 @@
       <PreprocessorDefinitions>_ITERATOR_DEBUG_LEVEL=1;WIN32;_DEBUGFAST;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <SDLCheck>true</SDLCheck>
       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
-      <AdditionalIncludeDirectories>$(SolutionDir)dep\glad\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>$(SolutionDir)dep\glad\include;$(SolutionDir)dep\cubeb\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <BasicRuntimeChecks>Default</BasicRuntimeChecks>
       <SupportJustMyCode>false</SupportJustMyCode>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>
@@ -354,7 +359,7 @@
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <SDLCheck>true</SDLCheck>
-      <AdditionalIncludeDirectories>$(SolutionDir)dep\glad\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>$(SolutionDir)dep\glad\include;$(SolutionDir)dep\cubeb\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>
       <LanguageStandard>stdcpp17</LanguageStandard>
       <WholeProgramOptimization>false</WholeProgramOptimization>
@@ -384,7 +389,7 @@
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <SDLCheck>true</SDLCheck>
-      <AdditionalIncludeDirectories>$(SolutionDir)dep\glad\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>$(SolutionDir)dep\glad\include;$(SolutionDir)dep\cubeb\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <OmitFramePointers>true</OmitFramePointers>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>
       <LanguageStandard>stdcpp17</LanguageStandard>
@@ -414,7 +419,7 @@
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <SDLCheck>true</SDLCheck>
-      <AdditionalIncludeDirectories>$(SolutionDir)dep\glad\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>$(SolutionDir)dep\glad\include;$(SolutionDir)dep\cubeb\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>
       <LanguageStandard>stdcpp17</LanguageStandard>
       <WholeProgramOptimization>false</WholeProgramOptimization>
@@ -444,7 +449,7 @@
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <SDLCheck>true</SDLCheck>
-      <AdditionalIncludeDirectories>$(SolutionDir)dep\glad\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>$(SolutionDir)dep\glad\include;$(SolutionDir)dep\cubeb\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <OmitFramePointers>true</OmitFramePointers>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>
       <LanguageStandard>stdcpp17</LanguageStandard>
diff --git a/src/common/common.vcxproj.filters b/src/common/common.vcxproj.filters
index bb293b537..5e9a2e99b 100644
--- a/src/common/common.vcxproj.filters
+++ b/src/common/common.vcxproj.filters
@@ -46,6 +46,7 @@
     <ClInclude Include="string_util.h" />
     <ClInclude Include="md5_digest.h" />
     <ClInclude Include="cpu_detect.h" />
+    <ClInclude Include="cubeb_audio_stream.h" />
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="jit_code_buffer.cpp" />
@@ -88,6 +89,7 @@
     <ClCompile Include="file_system.cpp" />
     <ClCompile Include="string_util.cpp" />
     <ClCompile Include="md5_digest.cpp" />
+    <ClCompile Include="cubeb_audio_stream.cpp" />
   </ItemGroup>
   <ItemGroup>
     <Natvis Include="bitfield.natvis" />
diff --git a/src/common/cubeb_audio_stream.cpp b/src/common/cubeb_audio_stream.cpp
new file mode 100644
index 000000000..1a725ca53
--- /dev/null
+++ b/src/common/cubeb_audio_stream.cpp
@@ -0,0 +1,120 @@
+#include "cubeb_audio_stream.h"
+#include "common/assert.h"
+#include "common/log.h"
+Log_SetChannel(CubebAudioStream);
+
+CubebAudioStream::CubebAudioStream() = default;
+
+CubebAudioStream::~CubebAudioStream()
+{
+  if (IsOpen())
+    CubebAudioStream::CloseDevice();
+}
+
+bool CubebAudioStream::OpenDevice()
+{
+  Assert(!IsOpen());
+
+  int rv = cubeb_init(&m_cubeb_context, "DuckStation", nullptr);
+  if (rv != CUBEB_OK)
+  {
+    Log_ErrorPrintf("Could not initialize cubeb context: %d", rv);
+    return false;
+  }
+
+  cubeb_stream_params params = {};
+  params.format = CUBEB_SAMPLE_S16LE;
+  params.rate = m_output_sample_rate;
+  params.channels = m_channels;
+  params.layout = CUBEB_LAYOUT_UNDEFINED;
+  params.prefs = CUBEB_STREAM_PREF_NONE;
+
+  u32 latency_frames = 0;
+  rv = cubeb_get_min_latency(m_cubeb_context, &params, &latency_frames);
+  if (rv != CUBEB_OK)
+  {
+    Log_ErrorPrintf("Could not get minimum latency: %d", rv);
+    cubeb_destroy(m_cubeb_context);
+    m_cubeb_context = nullptr;
+    return false;
+  }
+
+  Log_InfoPrintf("Minimum latency in frames: %u", latency_frames);
+  if (latency_frames > m_buffer_size)
+    Log_WarningPrintf("Minimum latency is above buffer size: %u vs %u", latency_frames, m_buffer_size);
+  else
+    latency_frames = m_buffer_size;
+
+  char stream_name[32];
+  std::snprintf(stream_name, sizeof(stream_name), "AudioStream_%p", this);
+
+  rv = cubeb_stream_init(m_cubeb_context, &m_cubeb_stream, stream_name, nullptr, nullptr, nullptr, &params,
+                         latency_frames, DataCallback, StateCallback, this);
+  if (rv != CUBEB_OK)
+  {
+    Log_ErrorPrintf("Could not create stream: %d", rv);
+    cubeb_destroy(m_cubeb_context);
+    m_cubeb_context = nullptr;
+    return false;
+  }
+
+  return true;
+}
+
+void CubebAudioStream::PauseDevice(bool paused)
+{
+  if (paused == m_paused)
+    return;
+
+  int rv = paused ? cubeb_stream_stop(m_cubeb_stream) : cubeb_stream_start(m_cubeb_stream);
+  if (rv != CUBEB_OK)
+  {
+    Log_ErrorPrintf("cubeb_stream_%s failed: %d", paused ? "stop" : "start", rv);
+    return;
+  }
+}
+
+void CubebAudioStream::CloseDevice()
+{
+  Assert(IsOpen());
+
+  if (!m_paused)
+    cubeb_stream_stop(m_cubeb_stream);
+
+  cubeb_stream_destroy(m_cubeb_stream);
+  m_cubeb_stream = nullptr;
+
+  cubeb_destroy(m_cubeb_context);
+  m_cubeb_context = nullptr;
+}
+
+long CubebAudioStream::DataCallback(cubeb_stream* stm, void* user_ptr, const void* input_buffer, void* output_buffer,
+                                    long nframes)
+{
+  CubebAudioStream* const this_ptr = static_cast<CubebAudioStream*>(user_ptr);
+
+  const u32 read_frames =
+    this_ptr->ReadSamples(reinterpret_cast<SampleType*>(output_buffer), static_cast<u32>(nframes));
+  const u32 silence_frames = static_cast<u32>(nframes) - read_frames;
+  if (silence_frames > 0)
+  {
+    std::memset(reinterpret_cast<SampleType*>(output_buffer) + (read_frames * this_ptr->m_channels), 0,
+                silence_frames * this_ptr->m_channels * sizeof(SampleType));
+  }
+
+  return nframes;
+}
+
+void CubebAudioStream::StateCallback(cubeb_stream* stream, void* user_ptr, cubeb_state state)
+{
+  CubebAudioStream* const this_ptr = static_cast<CubebAudioStream*>(user_ptr);
+
+  this_ptr->m_paused = (state != CUBEB_STATE_STARTED);
+}
+
+void CubebAudioStream::BufferAvailable() {}
+
+std::unique_ptr<AudioStream> AudioStream::CreateCubebAudioStream()
+{
+  return std::make_unique<CubebAudioStream>();
+}
diff --git a/src/common/cubeb_audio_stream.h b/src/common/cubeb_audio_stream.h
new file mode 100644
index 000000000..a63c77be3
--- /dev/null
+++ b/src/common/cubeb_audio_stream.h
@@ -0,0 +1,27 @@
+#pragma once
+#include "common/audio_stream.h"
+#include "cubeb/cubeb.h"
+#include <cstdint>
+
+class CubebAudioStream final : public AudioStream
+{
+public:
+  CubebAudioStream();
+  ~CubebAudioStream();
+
+protected:
+  bool IsOpen() const { return m_cubeb_stream != nullptr; }
+
+  bool OpenDevice() override;
+  void PauseDevice(bool paused) override;
+  void CloseDevice() override;
+  void BufferAvailable() override;
+
+  static long DataCallback(cubeb_stream* stm, void* user_ptr, const void* input_buffer, void* output_buffer,
+                           long nframes);
+  static void StateCallback(cubeb_stream* stream, void* user_ptr, cubeb_state state);
+
+  cubeb* m_cubeb_context = nullptr;
+  cubeb_stream* m_cubeb_stream = nullptr;
+  bool m_paused = true;
+};
diff --git a/src/common/null_audio_stream.cpp b/src/common/null_audio_stream.cpp
index d6c5794b4..f8f4662f2 100644
--- a/src/common/null_audio_stream.cpp
+++ b/src/common/null_audio_stream.cpp
@@ -19,7 +19,7 @@ void NullAudioStream::BufferAvailable()
   DropBuffer();
 }
 
-std::unique_ptr<AudioStream> NullAudioStream::Create()
+std::unique_ptr<AudioStream> AudioStream::CreateNullAudioStream()
 {
-  return std::unique_ptr<AudioStream>(new NullAudioStream());
+  return std::make_unique<NullAudioStream>();
 }
diff --git a/src/common/null_audio_stream.h b/src/common/null_audio_stream.h
index d589ef697..40a6a243b 100644
--- a/src/common/null_audio_stream.h
+++ b/src/common/null_audio_stream.h
@@ -4,16 +4,12 @@
 class NullAudioStream final : public AudioStream
 {
 public:
+  NullAudioStream();
   ~NullAudioStream();
 
-  static std::unique_ptr<AudioStream> Create();
-
 protected:
   bool OpenDevice() override;
   void PauseDevice(bool paused) override;
   void CloseDevice() override;
   void BufferAvailable() override;
-
-private:
-  NullAudioStream();
 };
diff --git a/src/core/types.h b/src/core/types.h
index 1008d8a46..2d63e602d 100644
--- a/src/core/types.h
+++ b/src/core/types.h
@@ -53,6 +53,7 @@ enum class AudioBackend : u8
 {
   Null,
   Default,
+  Cubeb,
   Count
 };
 
diff --git a/src/duckstation-qt/qthostinterface.cpp b/src/duckstation-qt/qthostinterface.cpp
index 53d1da168..d2c33f819 100644
--- a/src/duckstation-qt/qthostinterface.cpp
+++ b/src/duckstation-qt/qthostinterface.cpp
@@ -2,7 +2,6 @@
 #include "common/assert.h"
 #include "common/byte_stream.h"
 #include "common/log.h"
-#include "common/null_audio_stream.h"
 #include "common/string_util.h"
 #include "core/controller.h"
 #include "core/game_list.h"
@@ -28,7 +27,6 @@ QtHostInterface::QtHostInterface(QObject* parent)
   checkSettings();
   createGameList();
   doUpdateInputMap();
-  createAudioStream();
   createThread();
 }
 
@@ -446,15 +444,26 @@ void QtHostInterface::doBootSystem(QString initial_filename, QString initial_sav
 
 void QtHostInterface::createAudioStream()
 {
-  // Qt at least on Windows seems to want a buffer size of at least 8KB.
-  // m_audio_stream = QtAudioStream::Create();
-  // if (!m_audio_stream->Reconfigure(AUDIO_SAMPLE_RATE, AUDIO_CHANNELS, AUDIO_BUFFER_SIZE, 4))
+  switch (m_settings.audio_backend)
+  {
+    case AudioBackend::Default:
+    case AudioBackend::Cubeb:
+      m_audio_stream = AudioStream::CreateCubebAudioStream();
+      break;
+
+    case AudioBackend::Null:
+    default:
+      m_audio_stream = AudioStream::CreateNullAudioStream();
+      break;
+  }
+
+  if (!m_audio_stream->Reconfigure(AUDIO_SAMPLE_RATE, AUDIO_CHANNELS, AUDIO_BUFFER_SIZE, 4))
   {
     qWarning() << "Failed to configure audio stream, falling back to null output";
 
     // fall back to null output
     m_audio_stream.reset();
-    m_audio_stream = NullAudioStream::Create();
+    m_audio_stream = AudioStream::CreateNullAudioStream();
     m_audio_stream->Reconfigure(AUDIO_SAMPLE_RATE, AUDIO_CHANNELS, AUDIO_BUFFER_SIZE, 4);
   }
 }
@@ -483,6 +492,10 @@ void QtHostInterface::doStopThread()
 void QtHostInterface::threadEntryPoint()
 {
   m_worker_thread_event_loop = new QEventLoop();
+
+  createAudioStream();
+
+  // TODO: Event which flags the thread as ready
   while (!m_shutdown_flag.load())
   {
     if (!m_system || m_paused)
@@ -525,6 +538,7 @@ void QtHostInterface::threadEntryPoint()
   }
 
   m_system.reset();
+  m_audio_stream.reset();
   delete m_worker_thread_event_loop;
   m_worker_thread_event_loop = nullptr;
 
diff --git a/src/duckstation/sdl_host_interface.cpp b/src/duckstation/sdl_host_interface.cpp
index 6bc50d263..59137e6f5 100644
--- a/src/duckstation/sdl_host_interface.cpp
+++ b/src/duckstation/sdl_host_interface.cpp
@@ -2,7 +2,6 @@
 #include "common/assert.h"
 #include "common/byte_stream.h"
 #include "common/log.h"
-#include "common/null_audio_stream.h"
 #include "common/string_util.h"
 #include "core/controller.h"
 #include "core/gpu.h"
@@ -126,7 +125,11 @@ void SDLHostInterface::CreateAudioStream()
   switch (m_settings.audio_backend)
   {
     case AudioBackend::Null:
-      m_audio_stream = NullAudioStream::Create();
+      m_audio_stream = AudioStream::CreateNullAudioStream();
+      break;
+
+    case AudioBackend::Cubeb:
+      m_audio_stream = AudioStream::CreateCubebAudioStream();
       break;
 
     case AudioBackend::Default:
@@ -139,7 +142,7 @@ void SDLHostInterface::CreateAudioStream()
   {
     ReportError("Failed to recreate audio stream, falling back to null");
     m_audio_stream.reset();
-    m_audio_stream = NullAudioStream::Create();
+    m_audio_stream = AudioStream::CreateNullAudioStream();
     if (!m_audio_stream->Reconfigure(AUDIO_SAMPLE_RATE, AUDIO_CHANNELS))
       Panic("Failed to reconfigure null audio stream");
   }