Bug 1635404 - mach vendor rust. r=achronop
authorPaul Adenot <paul@paul.cx>
Tue, 05 May 2020 13:39:15 +0000
changeset 595541 6a8b8e2b9d4fc9612d1d08c8f933f9b181da7453
parent 595540 2620eda6b4c908a7205488259a0071477f22efda
child 595542 67cba49a2ceb0d3018b42234ff64dfe9913aad8e
push id2357
push userffxbld-merge
push dateMon, 22 Jun 2020 15:16:15 +0000
treeherdermozilla-release@12dd23e8e944 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersachronop
bugs1635404
milestone78.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1635404 - mach vendor rust. r=achronop Depends on D73871 Differential Revision: https://phabricator.services.mozilla.com/D73872
.cargo/config.in
Cargo.lock
third_party/rust/cubeb-backend/.cargo-checksum.json
third_party/rust/cubeb-backend/Cargo.toml
third_party/rust/cubeb-backend/src/capi.rs
third_party/rust/cubeb-backend/src/log.rs
third_party/rust/cubeb-backend/src/ops.rs
third_party/rust/cubeb-backend/src/traits.rs
third_party/rust/cubeb-backend/tests/test_capi.rs
third_party/rust/cubeb-core/.cargo-checksum.json
third_party/rust/cubeb-core/Cargo.toml
third_party/rust/cubeb-core/src/channel.rs
third_party/rust/cubeb-core/src/device.rs
third_party/rust/cubeb-core/src/error.rs
third_party/rust/cubeb-core/src/stream.rs
third_party/rust/cubeb-core/src/try_call.rs
third_party/rust/cubeb-coreaudio/.cargo-checksum.json
third_party/rust/cubeb-coreaudio/Cargo.toml
third_party/rust/cubeb-coreaudio/src/backend/mod.rs
third_party/rust/cubeb-coreaudio/src/backend/tests/api.rs
third_party/rust/cubeb-pulse/.cargo-checksum.json
third_party/rust/cubeb-pulse/Cargo.toml
third_party/rust/cubeb-pulse/src/backend/stream.rs
third_party/rust/cubeb-sys/.cargo-checksum.json
third_party/rust/cubeb-sys/Cargo.toml
third_party/rust/cubeb-sys/build.rs
third_party/rust/cubeb-sys/libcubeb/CMakeLists.txt
third_party/rust/cubeb-sys/libcubeb/README.md
third_party/rust/cubeb-sys/libcubeb/TODO
third_party/rust/cubeb-sys/libcubeb/include/cubeb/cubeb.h
third_party/rust/cubeb-sys/libcubeb/src/android/cubeb-output-latency.h
third_party/rust/cubeb-sys/libcubeb/src/cubeb-internal.h
third_party/rust/cubeb-sys/libcubeb/src/cubeb.c
third_party/rust/cubeb-sys/libcubeb/src/cubeb_alsa.c
third_party/rust/cubeb-sys/libcubeb/src/cubeb_audiotrack.c
third_party/rust/cubeb-sys/libcubeb/src/cubeb_audiounit.cpp
third_party/rust/cubeb-sys/libcubeb/src/cubeb_jack.cpp
third_party/rust/cubeb-sys/libcubeb/src/cubeb_kai.c
third_party/rust/cubeb-sys/libcubeb/src/cubeb_log.h
third_party/rust/cubeb-sys/libcubeb/src/cubeb_opensl.c
third_party/rust/cubeb-sys/libcubeb/src/cubeb_pulse.c
third_party/rust/cubeb-sys/libcubeb/src/cubeb_resampler.cpp
third_party/rust/cubeb-sys/libcubeb/src/cubeb_sndio.c
third_party/rust/cubeb-sys/libcubeb/src/cubeb_sun.c
third_party/rust/cubeb-sys/libcubeb/src/cubeb_utils.cpp
third_party/rust/cubeb-sys/libcubeb/src/cubeb_wasapi.cpp
third_party/rust/cubeb-sys/libcubeb/src/cubeb_winmm.c
third_party/rust/cubeb-sys/libcubeb/test/test_device_changed_callback.cpp
third_party/rust/cubeb-sys/libcubeb/test/test_devices.cpp
third_party/rust/cubeb-sys/libcubeb/test/test_loopback.cpp
third_party/rust/cubeb-sys/libcubeb/test/test_resampler.cpp
third_party/rust/cubeb-sys/libcubeb/test/test_ring_buffer.cpp
third_party/rust/cubeb-sys/libcubeb/tools/cubeb-test.cpp
third_party/rust/cubeb-sys/src/device.rs
third_party/rust/cubeb-sys/src/stream.rs
third_party/rust/cubeb/.cargo-checksum.json
third_party/rust/cubeb/Cargo.lock
third_party/rust/cubeb/Cargo.toml
third_party/rust/cubeb/examples/devices.rs
third_party/rust/cubeb/src/context.rs
third_party/rust/cubeb/src/stream.rs
--- a/.cargo/config.in
+++ b/.cargo/config.in
@@ -40,17 +40,17 @@ tag = "v0.3.0"
 [source."https://github.com/hsivonen/packed_simd"]
 git = "https://github.com/hsivonen/packed_simd"
 replace-with = "vendored-sources"
 rev = "3541e3818fdc7c2a24f87e3459151a4ce955a67a"
 
 [source."https://github.com/djg/cubeb-pulse-rs"]
 git = "https://github.com/djg/cubeb-pulse-rs"
 replace-with = "vendored-sources"
-rev = "5eb38163103b0dae86de81cdaf46070bdeedc0d1"
+rev = "70431f444cf164177cb3c0f060698fc35f811be5"
 
 [source."https://github.com/bytecodealliance/wasmtime"]
 git = "https://github.com/bytecodealliance/wasmtime"
 replace-with = "vendored-sources"
 rev = "b7cfd39b531680217537cfcf5294a22077a0a58d"
 
 [source."https://github.com/badboy/failure"]
 git = "https://github.com/badboy/failure"
@@ -65,17 +65,17 @@ rev = "d510da5999a744c563b0acd18056069d1
 [source."https://github.com/PLSysSec/lucet_sandbox_compiler"]
 git = "https://github.com/PLSysSec/lucet_sandbox_compiler"
 replace-with = "vendored-sources"
 rev = "5e870faf6f95d79d11efc813e56370ad124bbed5"
 
 [source."https://github.com/ChunMinChang/cubeb-coreaudio-rs"]
 git = "https://github.com/ChunMinChang/cubeb-coreaudio-rs"
 replace-with = "vendored-sources"
-rev = "6156e941e5f46a641d2da10ca53a9ad318c10722"
+rev = "c5aacdc75618025e72f62f727a7a0d91606e6276"
 
 [source.crates-io]
 replace-with = "vendored-sources"
 
 # Take advantage of the fact that cargo will treat lines starting with #
 # as comments to add preprocessing directives. This file can thus by copied
 # as-is to $topsrcdir/.cargo/config with no preprocessing to be used there
 # (for e.g. independent tasks building rust code), or be preprocessed by
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -738,17 +738,17 @@ source = "registry+https://github.com/ru
 checksum = "e81f1c165c33ffab90a03077ac3b03462b34d5947145dfa48102e063d581502c"
 dependencies = [
  "bindgen",
 ]
 
 [[package]]
 name = "coreaudio-sys-utils"
 version = "0.1.0"
-source = "git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=6156e941e5f46a641d2da10ca53a9ad318c10722#6156e941e5f46a641d2da10ca53a9ad318c10722"
+source = "git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=c5aacdc75618025e72f62f727a7a0d91606e6276#c5aacdc75618025e72f62f727a7a0d91606e6276"
 dependencies = [
  "core-foundation-sys",
  "coreaudio-sys",
 ]
 
 [[package]]
 name = "cose"
 version = "0.1.4"
@@ -947,76 +947,76 @@ source = "registry+https://github.com/ru
 checksum = "cd670e5ff58768ef624207fb95709ce63b8d05573fb9a05165f0eef471ea6a3a"
 dependencies = [
  "procedural-masquerade",
  "syn",
 ]
 
 [[package]]
 name = "cubeb"
-version = "0.6.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3cbcdfde9ea319160af6eff068ffaa96aad3532e1b5c0ebc134614cfacacae24"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1116606d6045c9199f6a1e082f3cf63383ba6f9961339701faa6370dcf73135f"
 dependencies = [
  "cubeb-core",
 ]
 
 [[package]]
 name = "cubeb-backend"
-version = "0.6.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "228778b00abd85a2ccca9330bc0ee7e72795dd255039814baef02b99757ad12e"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc644425cb33d45994a5df4351958369c00e3f7bbf785b8cab270c7840b75774"
 dependencies = [
  "cubeb-core",
 ]
 
 [[package]]
 name = "cubeb-core"
-version = "0.6.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bfd9b2ea1cb6afed9419b0d18fc4093df552ccb2300eb57793629f8cd370b4c8"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7c55529b8f47926e4242e1fc01d31b08a5a4847967c5c250644e33fe237cfe5"
 dependencies = [
  "bitflags",
  "cubeb-sys",
 ]
 
 [[package]]
 name = "cubeb-coreaudio"
 version = "0.1.0"
-source = "git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=6156e941e5f46a641d2da10ca53a9ad318c10722#6156e941e5f46a641d2da10ca53a9ad318c10722"
+source = "git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=c5aacdc75618025e72f62f727a7a0d91606e6276#c5aacdc75618025e72f62f727a7a0d91606e6276"
 dependencies = [
  "atomic",
  "audio-mixer",
  "bitflags",
  "coreaudio-sys-utils",
  "cubeb-backend",
  "float-cmp",
  "lazy_static",
  "libc",
  "mach",
  "ringbuf",
 ]
 
 [[package]]
 name = "cubeb-pulse"
 version = "0.3.0"
-source = "git+https://github.com/djg/cubeb-pulse-rs?rev=5eb38163103b0dae86de81cdaf46070bdeedc0d1#5eb38163103b0dae86de81cdaf46070bdeedc0d1"
+source = "git+https://github.com/djg/cubeb-pulse-rs?rev=70431f444cf164177cb3c0f060698fc35f811be5#70431f444cf164177cb3c0f060698fc35f811be5"
 dependencies = [
  "cubeb-backend",
  "pulse",
  "pulse-ffi",
  "ringbuf",
  "semver",
 ]
 
 [[package]]
 name = "cubeb-sys"
-version = "0.6.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "309c5839c5fa03c08363bd308566cbe4654b25a9984342d7546a33d55b80a3d6"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcbc562eb6ccf62abacf9e3eebce992e5c36b230ca313ebd7c2d7d0e99deae90"
 dependencies = [
  "cmake",
  "pkg-config",
 ]
 
 [[package]]
 name = "d3d12"
 version = "0.3.0"
@@ -3589,26 +3589,26 @@ dependencies = [
  "rustc-demangle",
  "thin-vec",
  "uuid",
 ]
 
 [[package]]
 name = "pulse"
 version = "0.3.0"
-source = "git+https://github.com/djg/cubeb-pulse-rs?rev=5eb38163103b0dae86de81cdaf46070bdeedc0d1#5eb38163103b0dae86de81cdaf46070bdeedc0d1"
+source = "git+https://github.com/djg/cubeb-pulse-rs?rev=70431f444cf164177cb3c0f060698fc35f811be5#70431f444cf164177cb3c0f060698fc35f811be5"
 dependencies = [
  "bitflags",
  "pulse-ffi",
 ]
 
 [[package]]
 name = "pulse-ffi"
 version = "0.1.0"
-source = "git+https://github.com/djg/cubeb-pulse-rs?rev=5eb38163103b0dae86de81cdaf46070bdeedc0d1#5eb38163103b0dae86de81cdaf46070bdeedc0d1"
+source = "git+https://github.com/djg/cubeb-pulse-rs?rev=70431f444cf164177cb3c0f060698fc35f811be5#70431f444cf164177cb3c0f060698fc35f811be5"
 dependencies = [
  "libc",
 ]
 
 [[package]]
 name = "quick-error"
 version = "1.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
--- a/third_party/rust/cubeb-backend/.cargo-checksum.json
+++ b/third_party/rust/cubeb-backend/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{"Cargo.toml":"f14e28a47501dd6861a5f3de61bdf70a2e67d8516c78f0b3a73fcd790ef8c143","LICENSE":"8c044baa5d883274736eeece0b955249076c2697b826e576fce59496235b2cf5","src/capi.rs":"faec78d41430ae26ac24b46d5833c76f94ac6eebb4b2186383ebb5e9de32cf8f","src/lib.rs":"94b80747ae1037423a2281f2572fc6d15cd7702417974ae3730adccd71c7a300","src/log.rs":"4962e9c5fdaf44b660ea6813b26337283496ffae3e9beaf2cda86621e253ae8b","src/ops.rs":"d9465abd16c2ce7414aecfc8f983fc9c29dc9b4780c0ea2074974a9814653576","src/traits.rs":"fa8463bd030c1d7716b05401fd2425ff0a399ee0c4dacfeed22341d89ecad871","tests/test_capi.rs":"25860adff6e9e2bcb06371f1e7e52e5f8a20c61fd6b2efe61b7713f3bc4f1031"},"package":"228778b00abd85a2ccca9330bc0ee7e72795dd255039814baef02b99757ad12e"}
\ No newline at end of file
+{"files":{"Cargo.toml":"1e79db9b3f52dc7d3b4d8354cd92022d6d9540aa2b8eee1be6f653cf0f9b6f79","LICENSE":"8c044baa5d883274736eeece0b955249076c2697b826e576fce59496235b2cf5","src/capi.rs":"a772331992b8e058a11e0a96515092ca19182c8eb4c0766e459ccceeb434b2ad","src/lib.rs":"94b80747ae1037423a2281f2572fc6d15cd7702417974ae3730adccd71c7a300","src/log.rs":"cf8e3a778f6b72d4cd80c1c56963355aa2224f19fd4fdf07d03f6fb366000899","src/ops.rs":"12c808c465a8e40bfd19ef8449035924d795fe19bab25b2f13779dca68040010","src/traits.rs":"e8c268343f21799b806e85b0ab1c0f4350b109d83af3e565ac6ff1eb17413672","tests/test_capi.rs":"eea51e60d631d0dab2c8e7f5172467b4fbafa652487f8840e9c5c89da42bf598"},"package":"cc644425cb33d45994a5df4351958369c00e3f7bbf785b8cab270c7840b75774"}
\ No newline at end of file
--- a/third_party/rust/cubeb-backend/Cargo.toml
+++ b/third_party/rust/cubeb-backend/Cargo.toml
@@ -7,23 +7,23 @@
 #
 # If you believe there's an error in this file please file an
 # issue against the rust-lang/cargo repository. If you're
 # editing this file be aware that the upstream Cargo.toml
 # will likely look very different (and much more reasonable)
 
 [package]
 name = "cubeb-backend"
-version = "0.6.3"
+version = "0.7.0"
 authors = ["Dan Glastonbury <dglastonbury@mozilla.com>"]
 description = "Bindings to libcubeb internals to facilitate implementing cubeb backends in rust.\n"
 homepage = "https://github.com/djg/cubeb-rs"
 keywords = ["cubeb"]
 categories = ["api-bindings"]
 license = "ISC"
 repository = "https://github.com/djg/cubeb-rs"
 [dependencies.cubeb-core]
-version = "0.6.2"
+version = "0.7.0"
 
 [features]
 gecko-in-tree = ["cubeb-core/gecko-in-tree"]
 [badges.circle-ci]
 repository = "djg/cubeb-rs"
--- a/third_party/rust/cubeb-backend/src/capi.rs
+++ b/third_party/rust/cubeb-backend/src/capi.rs
@@ -44,16 +44,17 @@ macro_rules! capi_new(
             stream_init: Some($crate::capi::capi_stream_init::<$ctx>),
             stream_destroy: Some($crate::capi::capi_stream_destroy::<$stm>),
             stream_start: Some($crate::capi::capi_stream_start::<$stm>),
             stream_stop: Some($crate::capi::capi_stream_stop::<$stm>),
             stream_reset_default_device:
                 Some($crate::capi::capi_stream_reset_default_device::<$stm>),
             stream_get_position: Some($crate::capi::capi_stream_get_position::<$stm>),
             stream_get_latency: Some($crate::capi::capi_stream_get_latency::<$stm>),
+            stream_get_input_latency: Some($crate::capi::capi_stream_get_input_latency::<$stm>),
             stream_set_volume: Some($crate::capi::capi_stream_set_volume::<$stm>),
             stream_get_current_device: Some($crate::capi::capi_stream_get_current_device::<$stm>),
             stream_device_destroy: Some($crate::capi::capi_stream_device_destroy::<$stm>),
             stream_register_device_changed_callback:
                 Some($crate::capi::capi_stream_register_device_changed_callback::<$stm>),
             register_device_collection_changed:
                 Some($crate::capi::capi_register_device_collection_changed::<$ctx>)
         }));
@@ -211,16 +212,26 @@ pub unsafe extern "C" fn capi_stream_get
     latency: *mut u32,
 ) -> c_int {
     let stm = &mut *(s as *mut STM);
 
     *latency = _try!(stm.latency());
     ffi::CUBEB_OK
 }
 
+pub unsafe extern "C" fn capi_stream_get_input_latency<STM: StreamOps>(
+    s: *mut ffi::cubeb_stream,
+    latency: *mut u32,
+) -> c_int {
+    let stm = &mut *(s as *mut STM);
+
+    *latency = _try!(stm.input_latency());
+    ffi::CUBEB_OK
+}
+
 pub unsafe extern "C" fn capi_stream_set_volume<STM: StreamOps>(
     s: *mut ffi::cubeb_stream,
     volume: f32,
 ) -> c_int {
     let stm = &mut *(s as *mut STM);
 
     _try!(stm.set_volume(volume));
     ffi::CUBEB_OK
--- a/third_party/rust/cubeb-backend/src/log.rs
+++ b/third_party/rust/cubeb-backend/src/log.rs
@@ -32,17 +32,17 @@ macro_rules! cubeb_log_internal {
                 .to_str()
                 .unwrap();
             // 2 for ':', 1 for ' ', 1 for '\n', and 1 for converting `line!()` to number of digits
             let len = filename.len() + ((line!() as f32).log10().trunc() as usize) + $msg.len() + 5;
             debug_assert!(len < buf.len(), "log will be truncated");
             let _ = write!(&mut buf[..], "{}:{}: {}\n", filename, line!(), $msg);
             let last = std::cmp::min(len, buf.len() - 1);
             buf[last] = 0;
-            let cstr = unsafe { std::ffi::CStr::from_bytes_with_nul_unchecked(&buf[..last + 1]) };
+            let cstr = unsafe { std::ffi::CStr::from_bytes_with_nul_unchecked(&buf[..=last]) };
             log_callback(cstr.as_ptr());
         }
     }
 }
 
 #[macro_export]
 macro_rules! cubeb_logv {
     ($msg: expr) => (cubeb_log_internal!($crate::LogLevel::Verbose, $msg));
--- a/third_party/rust/cubeb-backend/src/ops.rs
+++ b/third_party/rust/cubeb-backend/src/ops.rs
@@ -58,16 +58,18 @@ pub struct Ops {
     pub stream_start: Option<unsafe extern "C" fn(stream: *mut ffi::cubeb_stream) -> c_int>,
     pub stream_stop: Option<unsafe extern "C" fn(stream: *mut ffi::cubeb_stream) -> c_int>,
     pub stream_reset_default_device:
         Option<unsafe extern "C" fn(stream: *mut ffi::cubeb_stream) -> c_int>,
     pub stream_get_position:
         Option<unsafe extern "C" fn(stream: *mut ffi::cubeb_stream, position: *mut u64) -> c_int>,
     pub stream_get_latency:
         Option<unsafe extern "C" fn(stream: *mut ffi::cubeb_stream, latency: *mut u32) -> c_int>,
+    pub stream_get_input_latency:
+        Option<unsafe extern "C" fn(stream: *mut ffi::cubeb_stream, latency: *mut u32) -> c_int>,
     pub stream_set_volume:
         Option<unsafe extern "C" fn(stream: *mut ffi::cubeb_stream, volumes: c_float) -> c_int>,
     pub stream_get_current_device: Option<
         unsafe extern "C" fn(stream: *mut ffi::cubeb_stream, device: *mut *mut ffi::cubeb_device)
             -> c_int,
     >,
     pub stream_device_destroy: Option<
         unsafe extern "C" fn(stream: *mut ffi::cubeb_stream, device: *mut ffi::cubeb_device)
--- a/third_party/rust/cubeb-backend/src/traits.rs
+++ b/third_party/rust/cubeb-backend/src/traits.rs
@@ -43,16 +43,17 @@ pub trait ContextOps {
 }
 
 pub trait StreamOps {
     fn start(&mut self) -> Result<()>;
     fn stop(&mut self) -> Result<()>;
     fn reset_default_device(&mut self) -> Result<()>;
     fn position(&mut self) -> Result<u64>;
     fn latency(&mut self) -> Result<u32>;
+    fn input_latency(&mut self) -> Result<u32>;
     fn set_volume(&mut self, volume: f32) -> Result<()>;
     fn current_device(&mut self) -> Result<&DeviceRef>;
     fn device_destroy(&mut self, device: &DeviceRef) -> Result<()>;
     fn register_device_changed_callback(
         &mut self,
         device_changed_callback: ffi::cubeb_device_changed_callback,
     ) -> Result<()>;
 }
--- a/third_party/rust/cubeb-backend/tests/test_capi.rs
+++ b/third_party/rust/cubeb-backend/tests/test_capi.rs
@@ -95,16 +95,19 @@ impl StreamOps for TestStream {
         Ok(())
     }
     fn position(&mut self) -> Result<u64> {
         Ok(0u64)
     }
     fn latency(&mut self) -> Result<u32> {
         Ok(0u32)
     }
+    fn input_latency(&mut self) -> Result<u32> {
+        Ok(0u32)
+    }
     fn set_volume(&mut self, volume: f32) -> Result<()> {
         assert_eq!(volume, 0.5);
         Ok(())
     }
     fn current_device(&mut self) -> Result<&DeviceRef> {
         Ok(unsafe { DeviceRef::from_ptr(0xDEAD_BEEF as *mut _) })
     }
     fn device_destroy(&mut self, device: &DeviceRef) -> Result<()> {
--- a/third_party/rust/cubeb-core/.cargo-checksum.json
+++ b/third_party/rust/cubeb-core/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{"Cargo.toml":"12fc57e44d5f3962acec8860191fe0e9aca6ebe654924f4736b35cf15450b0d1","LICENSE":"8c044baa5d883274736eeece0b955249076c2697b826e576fce59496235b2cf5","src/builders.rs":"ca97e3a3d1f3fc451c17851c8538964ec67f3964dfe29e902d904ee7445becca","src/channel.rs":"c8d5a76ef3ecdd96cd4de516e3d4d139bbb83c4690d1c3f5fd07fffc47be51f1","src/context.rs":"09625b75070ec88d566a907ab2e574e2d85df4c6df295f798b3372df2cdc8f7a","src/device.rs":"490d2e94ecae1e149476c2e8d9aa03c4163987c3efccc962b2d3123e4c09dedf","src/device_collection.rs":"f6d0c1628cc34b524f86b84a1e1c79971c3f64ebc4ac64eeb10a1330bbe8c238","src/error.rs":"855ff3d3597753f832ecea00e403c71129afd80db3d39456cf3e23cb9aeb91e7","src/ffi_types.rs":"d815d7a80895b5e86907e708dc0219fca4ac4668cde114afee434e7d702a145d","src/format.rs":"5513c537a72af1c222ee7c30b26d4de9d368a69772688b95d88b1a99f6892d5c","src/lib.rs":"6010a5e20b836b8e5c9fba382fde819e6f3c18c0ec2016e6e7e118eabedbcd51","src/log.rs":"c46bae3472043fd076df3229c3421d948a87fae8495c1524b41ab2d8608f612a","src/stream.rs":"56de9f3ad372c123932eca23659f76e7b65fc4502c8da5fe91f3ed35666c306d","src/try_call.rs":"231bfa3f3448f7531427bb228beb2bcd4fd711f0b13d2d8f412af013470f40c7","src/util.rs":"308cfbaacd615ff600e74415c52daeef007fff34a4a0648a73c0042f6067f84f"},"package":"bfd9b2ea1cb6afed9419b0d18fc4093df552ccb2300eb57793629f8cd370b4c8"}
\ No newline at end of file
+{"files":{"Cargo.toml":"acc28d182a00d78ed6e11c8773d40939016d3df2cd9f3593fae8983eb05db540","LICENSE":"8c044baa5d883274736eeece0b955249076c2697b826e576fce59496235b2cf5","src/builders.rs":"ca97e3a3d1f3fc451c17851c8538964ec67f3964dfe29e902d904ee7445becca","src/channel.rs":"971488fca24a11d49046f8fe9c396765560c070013e5d022afcbe27cfd51d941","src/context.rs":"09625b75070ec88d566a907ab2e574e2d85df4c6df295f798b3372df2cdc8f7a","src/device.rs":"c526d8f992c8ad6d137b0bda803c5182247c9c32797a6f86e0d86d4f6361eb4c","src/device_collection.rs":"f6d0c1628cc34b524f86b84a1e1c79971c3f64ebc4ac64eeb10a1330bbe8c238","src/error.rs":"60454b30bd496dffa94ed9c1bee5a39f29219f2411b76cff6a4f3074f3154701","src/ffi_types.rs":"d815d7a80895b5e86907e708dc0219fca4ac4668cde114afee434e7d702a145d","src/format.rs":"5513c537a72af1c222ee7c30b26d4de9d368a69772688b95d88b1a99f6892d5c","src/lib.rs":"6010a5e20b836b8e5c9fba382fde819e6f3c18c0ec2016e6e7e118eabedbcd51","src/log.rs":"c46bae3472043fd076df3229c3421d948a87fae8495c1524b41ab2d8608f612a","src/stream.rs":"c237f288e6c78597d5a8f53c30927b1aaa40d482f2edb4343991b4d9d0bb1230","src/try_call.rs":"99c59a13db90326613d8cf91909e8a7eaef80646984d10927cbc7cde2cb4b066","src/util.rs":"308cfbaacd615ff600e74415c52daeef007fff34a4a0648a73c0042f6067f84f"},"package":"f7c55529b8f47926e4242e1fc01d31b08a5a4847967c5c250644e33fe237cfe5"}
\ No newline at end of file
--- a/third_party/rust/cubeb-core/Cargo.toml
+++ b/third_party/rust/cubeb-core/Cargo.toml
@@ -7,26 +7,26 @@
 #
 # If you believe there's an error in this file please file an
 # issue against the rust-lang/cargo repository. If you're
 # editing this file be aware that the upstream Cargo.toml
 # will likely look very different (and much more reasonable)
 
 [package]
 name = "cubeb-core"
-version = "0.6.2"
+version = "0.7.0"
 authors = ["Dan Glastonbury <dglastonbury@mozilla.com>"]
 description = "Common types and definitions for cubeb rust and C bindings. Not intended for direct use.\n"
 homepage = "https://github.com/djg/cubeb-rs"
 keywords = ["cubeb"]
 categories = ["api-bindings"]
 license = "ISC"
 repository = "https://github.com/djg/cubeb-rs"
 [dependencies.bitflags]
-version = "1.0"
+version = "1.2.0"
 
 [dependencies.cubeb-sys]
-version = "0.6.2"
+version = "0.7.0"
 
 [features]
 gecko-in-tree = ["cubeb-sys/gecko-in-tree"]
 [badges.circle-ci]
 repository = "djg/cubeb-rs"
--- a/third_party/rust/cubeb-core/src/channel.rs
+++ b/third_party/rust/cubeb-core/src/channel.rs
@@ -83,17 +83,17 @@ impl ChannelLayout {
 #[cfg(test)]
 mod test {
     use ffi;
 
     #[test]
     fn channel_layout_from_raw() {
         macro_rules! check(
             ($($raw:ident => $real:ident),*) => (
-                $(let x = super::ChannelLayout::from(ffi::$raw);;
+                $(let x = super::ChannelLayout::from(ffi::$raw);
                   assert_eq!(x, super::ChannelLayout::$real);
                 )*
             ) );
 
         check!(CUBEB_LAYOUT_UNDEFINED => UNDEFINED,
                CUBEB_LAYOUT_MONO => MONO,
                CUBEB_LAYOUT_MONO_LFE => MONO_LFE,
                CUBEB_LAYOUT_STEREO => STEREO,
--- a/third_party/rust/cubeb-core/src/device.rs
+++ b/third_party/rust/cubeb-core/src/device.rs
@@ -13,55 +13,55 @@ pub enum DeviceState {
     /// The device has been disabled at the system level.
     Disabled,
     /// The device is enabled, but nothing is plugged into it.
     Unplugged,
     /// The device is enabled.
     Enabled,
 }
 
-/// Architecture specific sample type.
 bitflags! {
+    /// Architecture specific sample type.
     pub struct DeviceFormat: ffi::cubeb_device_fmt {
         const S16LE = ffi::CUBEB_DEVICE_FMT_S16LE;
         const S16BE = ffi::CUBEB_DEVICE_FMT_S16BE;
         const F32LE = ffi::CUBEB_DEVICE_FMT_F32LE;
         const F32BE = ffi::CUBEB_DEVICE_FMT_F32BE;
     }
 }
 
-/// Channel type for a `cubeb_stream`. Depending on the backend and platform
-/// used, this can control inter-stream interruption, ducking, and volume
-/// control.
 bitflags! {
+    /// Channel type for a `cubeb_stream`. Depending on the backend and platform
+    /// used, this can control inter-stream interruption, ducking, and volume
+    /// control.
     pub struct DevicePref: ffi::cubeb_device_pref {
         const NONE = ffi::CUBEB_DEVICE_PREF_NONE;
         const MULTIMEDIA = ffi::CUBEB_DEVICE_PREF_MULTIMEDIA;
         const VOICE = ffi::CUBEB_DEVICE_PREF_VOICE;
         const NOTIFICATION = ffi::CUBEB_DEVICE_PREF_NOTIFICATION;
         const ALL = ffi::CUBEB_DEVICE_PREF_ALL;
     }
 }
 
-/// Whether a particular device is an input device (e.g. a microphone), or an
-/// output device (e.g. headphones).
 bitflags! {
+    /// Whether a particular device is an input device (e.g. a microphone), or an
+    /// output device (e.g. headphones).
     pub struct DeviceType: ffi::cubeb_device_type {
         const UNKNOWN = ffi::CUBEB_DEVICE_TYPE_UNKNOWN as _;
         const INPUT = ffi::CUBEB_DEVICE_TYPE_INPUT as _;
         const OUTPUT = ffi::CUBEB_DEVICE_TYPE_OUTPUT as _;
     }
 }
 
 /// An opaque handle used to refer to a particular input or output device
 /// across calls.
 pub type DeviceId = ffi::cubeb_devid;
 
-/// Audio device description
 ffi_type_heap! {
+    /// Audio device description
     type CType = ffi::cubeb_device;
     #[derive(Debug)]
     pub struct Device;
     pub struct DeviceRef;
 }
 
 impl DeviceRef {
     fn get_ref(&self) -> &ffi::cubeb_device {
@@ -86,21 +86,21 @@ impl DeviceRef {
         self.input_name_bytes().map(|b| str::from_utf8(b).unwrap())
     }
 
     pub fn input_name_bytes(&self) -> Option<&[u8]> {
         unsafe { opt_bytes(self.get_ref().input_name) }
     }
 }
 
-/// This structure holds the characteristics of an input or output
-/// audio device. It is obtained using `enumerate_devices`, which
-/// returns these structures via `device_collection` and must be
-/// destroyed via `device_collection_destroy`.
 ffi_type_stack! {
+    /// This structure holds the characteristics of an input or output
+    /// audio device. It is obtained using `enumerate_devices`, which
+    /// returns these structures via `device_collection` and must be
+    /// destroyed via `device_collection_destroy`.
     type CType = ffi::cubeb_device_info;
     pub struct DeviceInfo;
     pub struct DeviceInfoRef;
 }
 
 impl DeviceInfoRef {
     fn get_ref(&self) -> &ffi::cubeb_device_info {
         unsafe { &*self.as_ptr() }
--- a/third_party/rust/cubeb-core/src/error.rs
+++ b/third_party/rust/cubeb-core/src/error.rs
@@ -95,18 +95,17 @@ impl error::Error for Error {
             ErrorCode::NotSupported => "Not supported",
             ErrorCode::DeviceUnavailable => "Device unavailable",
         }
     }
 }
 
 impl fmt::Display for Error {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        use std::error::Error;
-        write!(f, "{}", self.description())
+        write!(f, "{}", self)
     }
 }
 
 impl From<ErrorCode> for Error {
     fn from(code: ErrorCode) -> Error {
         Error { code: code }
     }
 }
--- a/third_party/rust/cubeb-core/src/stream.rs
+++ b/third_party/rust/cubeb-core/src/stream.rs
@@ -40,28 +40,28 @@ impl Into<ffi::cubeb_state> for State {
             Started => ffi::CUBEB_STATE_STARTED,
             Stopped => ffi::CUBEB_STATE_STOPPED,
             Drained => ffi::CUBEB_STATE_DRAINED,
             Error => ffi::CUBEB_STATE_ERROR,
         }
     }
 }
 
-/// Miscellaneous stream preferences.
 bitflags! {
+    /// Miscellaneous stream preferences.
     pub struct StreamPrefs: ffi::cubeb_stream_prefs {
         const NONE = ffi::CUBEB_STREAM_PREF_NONE;
         const LOOPBACK = ffi::CUBEB_STREAM_PREF_LOOPBACK;
         const DISABLE_DEVICE_SWITCHING = ffi::CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING;
         const VOICE = ffi::CUBEB_STREAM_PREF_VOICE;
     }
 }
 
-/// Stream format initialization parameters.
 ffi_type_stack!{
+    /// Stream format initialization parameters.
     type CType = ffi::cubeb_stream_params;
     #[derive(Debug)]
     pub struct StreamParams;
     pub struct StreamParamsRef;
 }
 
 impl StreamParamsRef {
     fn get_ref(&self) -> &ffi::cubeb_stream_params {
@@ -138,16 +138,27 @@ impl StreamRef {
     pub fn latency(&self) -> Result<u32> {
         let mut latency = 0u32;
         unsafe {
             let _ = try_call!(ffi::cubeb_stream_get_latency(self.as_ptr(), &mut latency));
         }
         Ok(latency)
     }
 
+    /// Get the input latency for this stream, in frames. This is the number of frames between the
+    /// time the audio input device records the audio, and the cubeb callback delivers it.
+    /// This returns an error if the stream is output-only.
+    pub fn input_latency(&self) -> Result<u32> {
+        let mut latency = 0u32;
+        unsafe {
+            let _ = try_call!(ffi::cubeb_stream_get_input_latency(self.as_ptr(), &mut latency));
+        }
+        Ok(latency)
+    }
+
     /// Set the volume for a stream.
     pub fn set_volume(&self, volume: f32) -> Result<()> {
         unsafe { call!(ffi::cubeb_stream_set_volume(self.as_ptr(), volume)) }
     }
 
     /// Get the current output device for this stream.
     pub fn current_device(&self) -> Result<&DeviceRef> {
         let mut device: *mut ffi::cubeb_device = ptr::null_mut();
--- a/third_party/rust/cubeb-core/src/try_call.rs
+++ b/third_party/rust/cubeb-core/src/try_call.rs
@@ -17,11 +17,11 @@ pub fn cvt_r(ret: c_int) -> Result<(), E
 macro_rules! call {
     (ffi::$p:ident ($($e:expr),*)) => ({
         ::try_call::cvt_r(ffi::$p($($e),*))
     })
 }
 
 macro_rules! try_call {
     (ffi::$p:ident ($($e:expr),*)) => ({
-        try!(::try_call::cvt_r(ffi::$p($($e),*)))
+        ::try_call::cvt_r(ffi::$p($($e),*))?
     })
 }
--- a/third_party/rust/cubeb-coreaudio/.cargo-checksum.json
+++ b/third_party/rust/cubeb-coreaudio/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{".editorconfig":"4e53b182bcc78b83d7e1b5c03efa14d22d4955c4ed2514d1ba4e99c1eb1a50ba",".githooks/pre-push":"8b8b26544cd56f54c0c33812551f786bb25cb08c86dbfeb6bf3daad881c826a1",".travis.yml":"dc07bac53f70f16c9bdf52264bdc58500ae6018c1b4c567bc7642f6b4ca3cc35","Cargo.toml":"333de4946bd1fbe250d68241d3f085a47d6990053a54999e2b16f3c46779d228","LICENSE":"6e6f56aff5bbf3cbc60747e152fb1a719bd0716aaf6d711c554f57d92e96297c","README.md":"ab0f168080dfdfc1512484aeb4a41e1625f6b4147e534899fab56b2cb6d9f32a","install_git_hook.sh":"d38c8e51e636f6b90b489621ac34ccd1d1b1f40dccce3d178ed1da1c5068f16d","install_rustfmt_clippy.sh":"4ae90d8dcb9757cb3ae4ae142ef80e5377c0dde61c63f4a3c32418646e80ca7b","run_device_tests.sh":"4b7d65eb638c1a278ffc8ecb6d30d47b3b8405392e976cae38c6f744e2bed532","run_sanitizers.sh":"2f0934ba01cbcd74485f19d50147f6b604cf9730bbd3a3d3f3d958e40d0f799f","run_tests.sh":"3dd76659f6dceeb0490dd92b355e113301ba0d0a8f034993a56f40e09edd25b2","src/backend/aggregate_device.rs":"ae21129aa6b3c7bd3376751b6a94d1ebe6c9f7afcd1db3107fb4d703d04da6b3","src/backend/auto_release.rs":"050fdcee74cf46b9a8a85a877e166d72a853d33220f59cf734cbb6ea09daa441","src/backend/buffer_manager.rs":"9c10a261792e32e75833b5f976b18547c338ca6beb2330eeab1ad203cc8c32bf","src/backend/device_property.rs":"d43642ea6e5f40e29c2a59ec7d81b42c154134685e417585045785359aa31686","src/backend/mixer.rs":"14e2156a8c1aeabcd4adb3336c3c9401b9c8526ec82a8c78942af7a79648f0f8","src/backend/mod.rs":"b566a4f853563109b74c84bc519323b42fb96d8af58e4030225aade7cd47d6c6","src/backend/resampler.rs":"fd1281d28a4db1659d2f75e43b8457651745e1b6eb5a53a77f04d752135f6dc7","src/backend/tests/aggregate_device.rs":"107f5c637844cd5ae43d2b42cec4ef3369bb702751586078c0a9d50f039161cd","src/backend/tests/api.rs":"b6692aa2cab751e8d52aee40c8177ce15912338cdbe3daa93d3a25ce9e92031b","src/backend/tests/backlog.rs":"3b189a7e036543c467cc242af0ed3332721179ee2b1c8847a6db563546f1ac52","src/backend/tests/device_change.rs":"8261f561f69dabd374ac47d69aa484812b65070a9e9581dfb2605e8404eaad6d","src/backend/tests/device_property.rs":"373f76d3bee83b263db3f02be3b94b408bdf852d84e4b5153273fda34b11a374","src/backend/tests/interfaces.rs":"14943e84a79976a7ef52882edeb9330350705d5190db6647f98b4ffa851a8396","src/backend/tests/manual.rs":"dc707836dab31f83d4b325afbc4dc4c8104ac8036e87f59ade3309ee83fe2d3f","src/backend/tests/mod.rs":"8dba770023d7f9c4228f0e11915347f0e07da5fd818e3ee4478c4b197af9aa2a","src/backend/tests/parallel.rs":"f9e1883660d6146b6e5075806561f5f689810e25c5e7764dfd28c9b939821a49","src/backend/tests/tone.rs":"16150438317ce501986734167b5fb97bfec567228acbcd8f3b4c4484c22f29e0","src/backend/tests/utils.rs":"1bb99ef71d3c18545bca49767e7b6bfffbe11896246994f41be7ed372772fd48","src/backend/utils.rs":"5ce1b753af0ffb654b6b2038d649aea88eebd27390a607a6d5988a9e40a4a717","src/capi.rs":"21b66b70545bf04ec719928004d1d9adb45b24ced51288f5b2993d79aaf78f5f","src/lib.rs":"5e586d45cd6b3722f0a6736d9252593299269817a153eef1930a5fb9bfbb56f5","todo.md":"7323051fa7f0c51eb2eb0d495dcd951502e4cc8ce0088e6e7b3b3a95180f43d4"},"package":null}
\ No newline at end of file
+{"files":{".editorconfig":"4e53b182bcc78b83d7e1b5c03efa14d22d4955c4ed2514d1ba4e99c1eb1a50ba",".githooks/pre-push":"8b8b26544cd56f54c0c33812551f786bb25cb08c86dbfeb6bf3daad881c826a1",".travis.yml":"dc07bac53f70f16c9bdf52264bdc58500ae6018c1b4c567bc7642f6b4ca3cc35","Cargo.toml":"93449267612163fe621c75c6195fcf5f961c2bddd975468d67fc2121d538f1c7","LICENSE":"6e6f56aff5bbf3cbc60747e152fb1a719bd0716aaf6d711c554f57d92e96297c","README.md":"ab0f168080dfdfc1512484aeb4a41e1625f6b4147e534899fab56b2cb6d9f32a","install_git_hook.sh":"d38c8e51e636f6b90b489621ac34ccd1d1b1f40dccce3d178ed1da1c5068f16d","install_rustfmt_clippy.sh":"4ae90d8dcb9757cb3ae4ae142ef80e5377c0dde61c63f4a3c32418646e80ca7b","run_device_tests.sh":"4b7d65eb638c1a278ffc8ecb6d30d47b3b8405392e976cae38c6f744e2bed532","run_sanitizers.sh":"2f0934ba01cbcd74485f19d50147f6b604cf9730bbd3a3d3f3d958e40d0f799f","run_tests.sh":"3dd76659f6dceeb0490dd92b355e113301ba0d0a8f034993a56f40e09edd25b2","src/backend/aggregate_device.rs":"ae21129aa6b3c7bd3376751b6a94d1ebe6c9f7afcd1db3107fb4d703d04da6b3","src/backend/auto_release.rs":"050fdcee74cf46b9a8a85a877e166d72a853d33220f59cf734cbb6ea09daa441","src/backend/buffer_manager.rs":"9c10a261792e32e75833b5f976b18547c338ca6beb2330eeab1ad203cc8c32bf","src/backend/device_property.rs":"d43642ea6e5f40e29c2a59ec7d81b42c154134685e417585045785359aa31686","src/backend/mixer.rs":"14e2156a8c1aeabcd4adb3336c3c9401b9c8526ec82a8c78942af7a79648f0f8","src/backend/mod.rs":"4c2967b7f581cebce875ea342658d32650695ebd57a026bd36e92ef3807866ae","src/backend/resampler.rs":"fd1281d28a4db1659d2f75e43b8457651745e1b6eb5a53a77f04d752135f6dc7","src/backend/tests/aggregate_device.rs":"107f5c637844cd5ae43d2b42cec4ef3369bb702751586078c0a9d50f039161cd","src/backend/tests/api.rs":"9ce44a867519d7b7a2b43c7f833327c35be38af7ba6fcc3d277ed1d7d8e7c8c2","src/backend/tests/backlog.rs":"3b189a7e036543c467cc242af0ed3332721179ee2b1c8847a6db563546f1ac52","src/backend/tests/device_change.rs":"8261f561f69dabd374ac47d69aa484812b65070a9e9581dfb2605e8404eaad6d","src/backend/tests/device_property.rs":"373f76d3bee83b263db3f02be3b94b408bdf852d84e4b5153273fda34b11a374","src/backend/tests/interfaces.rs":"14943e84a79976a7ef52882edeb9330350705d5190db6647f98b4ffa851a8396","src/backend/tests/manual.rs":"dc707836dab31f83d4b325afbc4dc4c8104ac8036e87f59ade3309ee83fe2d3f","src/backend/tests/mod.rs":"8dba770023d7f9c4228f0e11915347f0e07da5fd818e3ee4478c4b197af9aa2a","src/backend/tests/parallel.rs":"f9e1883660d6146b6e5075806561f5f689810e25c5e7764dfd28c9b939821a49","src/backend/tests/tone.rs":"16150438317ce501986734167b5fb97bfec567228acbcd8f3b4c4484c22f29e0","src/backend/tests/utils.rs":"1bb99ef71d3c18545bca49767e7b6bfffbe11896246994f41be7ed372772fd48","src/backend/utils.rs":"5ce1b753af0ffb654b6b2038d649aea88eebd27390a607a6d5988a9e40a4a717","src/capi.rs":"21b66b70545bf04ec719928004d1d9adb45b24ced51288f5b2993d79aaf78f5f","src/lib.rs":"5e586d45cd6b3722f0a6736d9252593299269817a153eef1930a5fb9bfbb56f5","todo.md":"7323051fa7f0c51eb2eb0d495dcd951502e4cc8ce0088e6e7b3b3a95180f43d4"},"package":null}
\ No newline at end of file
--- a/third_party/rust/cubeb-coreaudio/Cargo.toml
+++ b/third_party/rust/cubeb-coreaudio/Cargo.toml
@@ -6,15 +6,15 @@ license = "ISC"
 
 [lib]
 crate-type = ["staticlib", "rlib"]
 
 [dependencies]
 atomic = "0.4"
 bitflags = "1.0"
 coreaudio-sys-utils = { path = "coreaudio-sys-utils" }
-cubeb-backend = "0.6.3"
+cubeb-backend = "0.7"
 float-cmp = "0.6"
 libc = "0.2"
 lazy_static = "1.2"
 mach = "0.3"
 audio-mixer = "0.1"
 ringbuf = "0.2"
--- a/third_party/rust/cubeb-coreaudio/src/backend/mod.rs
+++ b/third_party/rust/cubeb-coreaudio/src/backend/mod.rs
@@ -339,16 +339,20 @@ extern "C" fn audiounit_input_callback(
     };
 
     assert!(input_frames > 0);
     assert_eq!(bus, AU_IN_BUS);
 
     assert!(!user_ptr.is_null());
     let stm = unsafe { &mut *(user_ptr as *mut AudioUnitStream) };
 
+    let input_latency_frames = compute_input_latency(&stm, unsafe { (*tstamp).mHostTime });
+    stm.total_input_latency_frames
+        .store(input_latency_frames, Ordering::SeqCst);
+
     if stm.shutdown.load(Ordering::SeqCst) {
         cubeb_log!("({:p}) input shutdown", stm as *const AudioUnitStream);
         return NO_ERR;
     }
 
     let handler = |stm: &mut AudioUnitStream,
                    flags: *mut AudioUnitRenderActionFlags,
                    tstamp: *const AudioTimeStamp,
@@ -496,17 +500,34 @@ fn compute_output_latency(stm: &AudioUni
         audio_output_time - now
     };
 
     const NS2S: u64 = 1_000_000_000;
     // The total output latency is the timestamp difference + the stream latency +
     // the hardware latency.
     let out_hw_rate = stm.core_stream_data.output_hw_rate as u64;
     (output_latency_ns * out_hw_rate / NS2S
-        + stm.current_latency_frames.load(Ordering::SeqCst) as u64) as u32
+        + stm.current_output_latency_frames.load(Ordering::SeqCst) as u64) as u32
+}
+
+fn compute_input_latency(stm: &AudioUnitStream, host_time: u64) -> u32 {
+    let now = host_time_to_ns(unsafe { mach_absolute_time() });
+    let audio_input_time = host_time_to_ns(host_time);
+    let input_latency_ns = if audio_input_time > now {
+        0
+    } else {
+        now - audio_input_time
+    };
+
+    const NS2S: u64 = 1_000_000_000;
+    // The total input latency is the timestamp difference + the stream latency +
+    // the hardware latency.
+    let input_hw_rate = stm.core_stream_data.input_hw_rate as u64;
+    (input_latency_ns * input_hw_rate / NS2S
+        + stm.current_input_latency_frames.load(Ordering::SeqCst) as u64) as u32
 }
 
 extern "C" fn audiounit_output_callback(
     user_ptr: *mut c_void,
     _: *mut AudioUnitRenderActionFlags,
     tstamp: *const AudioTimeStamp,
     bus: u32,
     output_frames: u32,
@@ -1332,17 +1353,17 @@ fn get_range_of_sample_rates(
         }
         if rate.mMinimum < min {
             min = rate.mMinimum;
         }
     }
     Ok((min, max))
 }
 
-fn get_presentation_latency(devid: AudioObjectID, devtype: DeviceType) -> u32 {
+fn get_fixed_latency(devid: AudioObjectID, devtype: DeviceType) -> u32 {
     let device_latency = match get_device_latency(devid, devtype) {
         Ok(latency) => latency,
         Err(e) => {
             cubeb_log!(
                 "Cannot get the device latency for device {} in {:?} scope. Error: {}",
                 devid,
                 devtype,
                 e
@@ -1582,17 +1603,17 @@ fn create_cubeb_device_info(
                 "Cannot get the range of sample rate for device {} in {:?} scope. Error: {}",
                 devid,
                 devtype,
                 e
             );
         }
     }
 
-    let latency = get_presentation_latency(devid, devtype);
+    let latency = get_fixed_latency(devid, devtype);
 
     let (latency_low, latency_high) = match get_device_buffer_frame_size_range(devid, devtype) {
         Ok(range) => (
             latency + range.mMinimum as u32,
             latency + range.mMaximum as u32,
         ),
         Err(e) => {
             cubeb_log!("Cannot get the buffer frame size for device {} in {:?} scope. Use default value instead. Error: {}", devid, devtype, e);
@@ -2808,42 +2829,47 @@ impl<'ctx> CoreStreamData<'ctx> {
         );
 
         if !self.input_unit.is_null() {
             let r = audio_unit_initialize(self.input_unit);
             if r != NO_ERR {
                 cubeb_log!("AudioUnitInitialize/input rv={}", r);
                 return Err(Error::error());
             }
+
+            stream.current_input_latency_frames.store(
+                get_fixed_latency(self.input_device.id, DeviceType::INPUT),
+                Ordering::SeqCst,
+            );
         }
 
         if !self.output_unit.is_null() {
             let r = audio_unit_initialize(self.output_unit);
             if r != NO_ERR {
                 cubeb_log!("AudioUnitInitialize/output rv={}", r);
                 return Err(Error::error());
             }
 
-            stream.current_latency_frames.store(
-                get_presentation_latency(self.output_device.id, DeviceType::OUTPUT),
+            stream.current_output_latency_frames.store(
+                get_fixed_latency(self.output_device.id, DeviceType::OUTPUT),
                 Ordering::SeqCst,
             );
 
             let mut unit_s: f64 = 0.0;
             let mut size = mem::size_of_val(&unit_s);
             if audio_unit_get_property(
                 self.output_unit,
                 kAudioUnitProperty_Latency,
                 kAudioUnitScope_Global,
                 0,
                 &mut unit_s,
                 &mut size,
             ) == NO_ERR
             {
-                stream.current_latency_frames.fetch_add(
+                stream.current_output_latency_frames.fetch_add(
                     (unit_s * self.output_desc.mSampleRate) as u32,
                     Ordering::SeqCst,
                 );
             }
         }
 
         if let Err(r) = self.install_system_changed_callback() {
             cubeb_log!(
@@ -3110,18 +3136,20 @@ struct AudioUnitStream<'ctx> {
     // How many frames got written to the output device since the stream started
     frames_written: AtomicUsize,
     shutdown: AtomicBool,
     draining: AtomicBool,
     reinit_pending: AtomicBool,
     destroy_pending: AtomicBool,
     // Latency requested by the user.
     latency_frames: u32,
-    current_latency_frames: AtomicU32,
+    current_output_latency_frames: AtomicU32,
+    current_input_latency_frames: AtomicU32,
     total_output_latency_frames: AtomicU32,
+    total_input_latency_frames: AtomicU32,
     // This is true if a device change callback is currently running.
     switching_device: AtomicBool,
     core_stream_data: CoreStreamData<'ctx>,
 }
 
 impl<'ctx> AudioUnitStream<'ctx> {
     fn new(
         context: &'ctx mut AudioUnitContext,
@@ -3141,18 +3169,20 @@ impl<'ctx> AudioUnitStream<'ctx> {
             frames_queued: 0,
             frames_read: AtomicUsize::new(0),
             frames_written: AtomicUsize::new(0),
             shutdown: AtomicBool::new(true),
             draining: AtomicBool::new(false),
             reinit_pending: AtomicBool::new(false),
             destroy_pending: AtomicBool::new(false),
             latency_frames,
-            current_latency_frames: AtomicU32::new(0),
+            current_output_latency_frames: AtomicU32::new(0),
+            current_input_latency_frames: AtomicU32::new(0),
             total_output_latency_frames: AtomicU32::new(0),
+            total_input_latency_frames: AtomicU32::new(0),
             switching_device: AtomicBool::new(false),
             core_stream_data: CoreStreamData::default(),
         }
     }
 
     fn add_device_listener(&self, listener: &device_property_listener) -> OSStatus {
         audio_object_add_property_listener(
             listener.device,
@@ -3423,33 +3453,49 @@ impl<'ctx> StreamOps for AudioUnitStream
             self as *const AudioUnitStream
         );
         Ok(())
     }
     fn reset_default_device(&mut self) -> Result<()> {
         Err(Error::not_supported())
     }
     fn position(&mut self) -> Result<u64> {
-        let current_latency_frames = u64::from(self.current_latency_frames.load(Ordering::SeqCst));
+        let current_output_latency_frames =
+            u64::from(self.current_output_latency_frames.load(Ordering::SeqCst));
         let frames_played = self.frames_played.load(Ordering::SeqCst);
-        let position = if current_latency_frames > frames_played {
+        let position = if current_output_latency_frames > frames_played {
             0
         } else {
-            frames_played - current_latency_frames
+            frames_played - current_output_latency_frames
         };
         Ok(position)
     }
     #[cfg(target_os = "ios")]
     fn latency(&mut self) -> Result<u32> {
         Err(not_supported())
     }
     #[cfg(not(target_os = "ios"))]
     fn latency(&mut self) -> Result<u32> {
         Ok(self.total_output_latency_frames.load(Ordering::SeqCst))
     }
+    #[cfg(target_os = "ios")]
+    fn input_latency(&mut self) -> Result<u32> {
+        Err(not_supported())
+    }
+    #[cfg(not(target_os = "ios"))]
+    fn input_latency(&mut self) -> Result<u32> {
+        let user_rate = self.core_stream_data.input_stream_params.rate();
+        let hw_rate = self.core_stream_data.input_hw_rate as u32;
+        let frames = self.total_input_latency_frames.load(Ordering::SeqCst);
+        if hw_rate == user_rate {
+            Ok(frames)
+        } else {
+            Ok(frames * (user_rate / hw_rate))
+        }
+    }
     fn set_volume(&mut self, volume: f32) -> Result<()> {
         set_volume(self.core_stream_data.output_unit, volume)
     }
     #[cfg(target_os = "ios")]
     fn current_device(&mut self) -> Result<&DeviceRef> {
         Err(not_supported())
     }
     #[cfg(not(target_os = "ios"))]
--- a/third_party/rust/cubeb-coreaudio/src/backend/tests/api.rs
+++ b/third_party/rust/cubeb-coreaudio/src/backend/tests/api.rs
@@ -1195,17 +1195,17 @@ fn test_get_range_of_sample_rates() {
 #[test]
 fn test_get_device_presentation_latency() {
     test_get_device_presentation_latencies_in_scope(Scope::Input);
     test_get_device_presentation_latencies_in_scope(Scope::Output);
 
     fn test_get_device_presentation_latencies_in_scope(scope: Scope) {
         if let Some(device) = test_get_default_device(scope.clone()) {
             // TODO: The latencies very from devices to devices. Check nothing here.
-            let latency = get_presentation_latency(device, scope.clone().into());
+            let latency = get_fixed_latency(device, scope.clone().into());
             println!(
                 "present latency on the device {} in scope {:?}: {}",
                 device, scope, latency
             );
         } else {
             println!("No device for {:?}.", scope);
         }
     }
--- a/third_party/rust/cubeb-pulse/.cargo-checksum.json
+++ b/third_party/rust/cubeb-pulse/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{".editorconfig":"bf047bd1da10cabb99eea666d1e57c321eba4716dccb3e4ed0e2c5fe3ca53858",".travis.yml":"0394e2adb041175457685cde5ee05ff04bdab8885fd8a62551f2ff43d9e48872","AUTHORS":"0e0ac930a68ce2f6b876126b195add177f0d3886facb9260f4d9b69f1988f0cc","Cargo.toml":"297f0144bbebcbc2f3b5478fed750f39adb3b60f9d6c273dfa72d9736818423d","LICENSE":"44c6b5ae5ec3fe2fbc608b00e6f4896f4d2d5c7e525fcbaa3eaa3cf2f3d5a983","README.md":"e6a98ee5630b9ce1a096a2907d095454f2770e298a5b0976ab552cc53ca96cfc","src/backend/context.rs":"33d9fdf1504fe1ae43d301e288daf6eaeabeb47aa0ef86efa135c6d984425fc4","src/backend/cork_state.rs":"4a0f1afc7d9f333dac89218cc56d7d32fbffb487cd48c1c9a4e03d79cb3b5e28","src/backend/intern.rs":"374a9a3bd79fddc47739dda1dbfc5929aea5a91946794fe65fba3c8d130fbda9","src/backend/mod.rs":"06ce9250865abf0ea461f215b128470636d072a6776821efef3caf5a7b992fb9","src/backend/stream.rs":"8ff67b76b8663a952fe2d2f7d370a05271f156601c929d19caa56fc892a4a4ab","src/capi.rs":"b2c1be8128cadd36caa65c80950440f9d6f2aa0c24cc7bae6a9eaf6347ac454d","src/lib.rs":"7282560d84b134b09acfd8d6282600982e42fb3557f72454c535637cc26c7bf6"},"package":null}
\ No newline at end of file
+{"files":{".editorconfig":"bf047bd1da10cabb99eea666d1e57c321eba4716dccb3e4ed0e2c5fe3ca53858",".travis.yml":"0394e2adb041175457685cde5ee05ff04bdab8885fd8a62551f2ff43d9e48872","AUTHORS":"0e0ac930a68ce2f6b876126b195add177f0d3886facb9260f4d9b69f1988f0cc","Cargo.toml":"603fb672fb83699707559d13a651963321991b9bec6ea208723d3ee37c40694e","LICENSE":"44c6b5ae5ec3fe2fbc608b00e6f4896f4d2d5c7e525fcbaa3eaa3cf2f3d5a983","README.md":"e6a98ee5630b9ce1a096a2907d095454f2770e298a5b0976ab552cc53ca96cfc","src/backend/context.rs":"33d9fdf1504fe1ae43d301e288daf6eaeabeb47aa0ef86efa135c6d984425fc4","src/backend/cork_state.rs":"4a0f1afc7d9f333dac89218cc56d7d32fbffb487cd48c1c9a4e03d79cb3b5e28","src/backend/intern.rs":"374a9a3bd79fddc47739dda1dbfc5929aea5a91946794fe65fba3c8d130fbda9","src/backend/mod.rs":"06ce9250865abf0ea461f215b128470636d072a6776821efef3caf5a7b992fb9","src/backend/stream.rs":"ce537c89dc5329f19d287e547b6c556a836ef42439cffce9d2cf3eaa18a65a0b","src/capi.rs":"b2c1be8128cadd36caa65c80950440f9d6f2aa0c24cc7bae6a9eaf6347ac454d","src/lib.rs":"7282560d84b134b09acfd8d6282600982e42fb3557f72454c535637cc26c7bf6"},"package":null}
\ No newline at end of file
--- a/third_party/rust/cubeb-pulse/Cargo.toml
+++ b/third_party/rust/cubeb-pulse/Cargo.toml
@@ -7,13 +7,13 @@ license = "ISC"
 
 [features]
 pulse-dlopen = ["pulse-ffi/dlopen"]
 
 [lib]
 crate-type = ["staticlib", "rlib"]
 
 [dependencies]
-cubeb-backend = "0.6"
+cubeb-backend = "0.7"
 pulse-ffi = { path = "pulse-ffi" }
 pulse = { path = "pulse-rs" }
 semver = "^0.9"
 ringbuf = "0.2"
--- a/third_party/rust/cubeb-pulse/src/backend/stream.rs
+++ b/third_party/rust/cubeb-pulse/src/backend/stream.rs
@@ -637,16 +637,20 @@ impl<'ctx> StreamOps for PulseStream<'ct
                 Ok(_) => {
                     panic!("Can not handle negative latency values.");
                 }
                 Err(_) => Err(Error::error()),
             },
         }
     }
 
+    fn input_latency(&mut self) -> Result<u32> {
+        Err(Error::error())
+    }
+
     fn set_volume(&mut self, volume: f32) -> Result<()> {
         match self.output_stream {
             None => Err(Error::error()),
             Some(ref stm) => {
                 if let Some(ref context) = self.context.context {
                     self.context.mainloop.lock();
 
                     let mut cvol: pa_cvolume = Default::default();
--- a/third_party/rust/cubeb-sys/.cargo-checksum.json
+++ b/third_party/rust/cubeb-sys/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{"Cargo.toml":"36f12dbd2d01bbf8ac6ff5ee25b5b28807860f7e77e830f483f31eb0cfa9b2bd","LICENSE":"8c044baa5d883274736eeece0b955249076c2697b826e576fce59496235b2cf5","build.rs":"94f2df211c2ecff4f5be54a7aa12849fe973b555b0daa2a64493976db5be7dad","libcubeb/AUTHORS":"829e45d138c7c8827799f302806fa4be8cd8bd4bad70a2fe26c3a27a5cf36948","libcubeb/CMakeLists.txt":"1e66380904470ff3aa469a3a83ed21db4b68dd2414f66300a5956e972a8e68ca","libcubeb/Config.cmake.in":"88019286c96ef3d5d3a673b183c8655dfc97ceede07d3eb6c18f0c51bb896388","libcubeb/INSTALL.md":"7a84cdfbe86e7d3180e2203603b88571df61b369421fa97ee86740ffd4d4db8e","libcubeb/LICENSE":"44c6b5ae5ec3fe2fbc608b00e6f4896f4d2d5c7e525fcbaa3eaa3cf2f3d5a983","libcubeb/README.md":"aa417156dc65069264901b75fc3c792c73021ec61c909de04e109bd4184c07ab","libcubeb/TODO":"6f8065136e005d2becee810e3d8697a94f2c755f8c79687adfac7136ad165e80","libcubeb/appveyor.yml":"9a87fdc18b76fca1167289ecb2ec3a210b76984c1d7e92268b2cd36f1e00f541","libcubeb/cmake/sanitizers-cmake/CMakeLists.txt":"89b282c19b3897ff666f7847d5e679ae928ca7e76ffd0d23f7c58c9464048b61","libcubeb/cmake/sanitizers-cmake/LICENSE":"4b67e7ae8c91e68e1a929eb1cbaa4c773c6d19aa91aaa12c390cf9560d1c9799","libcubeb/cmake/sanitizers-cmake/README.md":"f3b85a594e1601ae65bb09393c56fbc8a98e3f3f49234e56f86bd78803c3544e","libcubeb/cmake/sanitizers-cmake/cmake/FindASan.cmake":"bd30b1c3cb0682b8a2b0b599f6c51fbf99ef4908b00eba8826f0d484c623b01b","libcubeb/cmake/sanitizers-cmake/cmake/FindMSan.cmake":"55e40f0994b47f8e779622af2007c69252d9456be99f4aa4fba7dd49c23d5580","libcubeb/cmake/sanitizers-cmake/cmake/FindSanitizers.cmake":"2ffd9717a49e3e9de848ec0d91097bdbab3f75a7866211d902f9659f59271ffc","libcubeb/cmake/sanitizers-cmake/cmake/FindTSan.cmake":"ceace234abd5a463ad1b8134ad06c4c8004895b2e8fc9edb67e64620ea97ca47","libcubeb/cmake/sanitizers-cmake/cmake/FindUBSan.cmake":"4720ec96ea42a2a9c07d887c1fc5562ed3e8c222f5778a18b78c46678b23f156","libcubeb/cmake/sanitizers-cmake/cmake/asan-wrapper":"4e543936e6374e24b80a0f92135c07c2e2101c0d110e51bddaf0e70ae8ec391e","libcubeb/cmake/sanitizers-cmake/cmake/sanitize-helpers.cmake":"c0c225f22bebd27eb68b807b2a61962d28afc61609735fe652c2fde6aa6a005b","libcubeb/cmake/sanitizers-cmake/tests/CMakeLists.txt":"64b7aea469a043f27414e3d652aaa89a03a11843d152c4cd42f30561e6ef51bc","libcubeb/cmake/sanitizers-cmake/tests/asan_test.cpp":"a25de95282aaee22d977d0b474468b946c1422b80b15424c04266dba038eba2e","libcubeb/cmake/sanitizers-cmake/tests/shortest.ext.test.cpp":"a25de95282aaee22d977d0b474468b946c1422b80b15424c04266dba038eba2e","libcubeb/cmake/toolchain-cross-mingw.cmake":"1c26749465364061ddf37aaa00f363eaf057c4b6a70181086e13b0a4d09bd376","libcubeb/cubeb.supp":"19f33e59f8dc91a327c923e44c2c3f9af0a043ce1d6a8cac275ba094b4bfe0da","libcubeb/docs/Doxyfile.in":"0815f19789cedd310652a133bab260c27b57e95f7a65458df2dfca38ea4f1041","libcubeb/googletest/CHANGES":"72c8a289bfe4dd9160074a3a2238c8067a5bc7ca49fd87f70a134c3f373932a4","libcubeb/googletest/CMakeLists.txt":"cdf938ce79ea066866dc614de3925bfaa48d9e19e04049db2ec8c5443abaaa9b","libcubeb/googletest/CONTRIBUTORS":"4d911cd5e6d71f3f4bbcb2788b2f916df4b0ce1e088752a159457a1d936ae0ce","libcubeb/googletest/COPYING":"9702de7e4117a8e2b20dafab11ffda58c198aede066406496bef670d40a22138","libcubeb/googletest/Makefile.am":"a795e5a18e82ba06fd97509d92d773b7fafd0dd7086db8a1211fbd151b503bac","libcubeb/googletest/README":"484b19654362942fac9734c8fab0ed3d99249b81c69027bdb7de46054abada6b","libcubeb/googletest/cmake/internal_utils.cmake":"6f4670a5825cf8ae0415be9dd43d82a7f30316d75cab20a4c60afb6d9db2a01d","libcubeb/googletest/codegear/gtest.cbproj":"9fa07a66b8c01773256e508187775407c465ed9055651e93d390426d1888721a","libcubeb/googletest/codegear/gtest.groupproj":"76c001cb2ee0070e26d1570fb6db5250f2585179c707496c5ef6d12e582cf205","libcubeb/googletest/codegear/gtest_all.cc":"c8750dc2b7b3612edb60e8d23fc2f60ae264451916c4b15f81cbf60ea30eb75d","libcubeb/googletest/codegear/gtest_link.cc":"e5e47c086f932471e0ca748a62b3b1597e5e731f4d34fb8cb8b2ca2db7ba3ed0","libcubeb/googletest/codegear/gtest_main.cbproj":"056448ba7f41fae3f704b1001922d9f6e419cf8cbcf3a55bf0ecc59ece1fa9a1","libcubeb/googletest/codegear/gtest_unittest.cbproj":"2c48be90b0dbc3224a8bd6c4dc21f78dd1b08c81685e6bd4952bb6df6ee38701","libcubeb/googletest/configure.ac":"fadebffdaeaccebb60fbe4a501c1138c296e01348a5da45fabf5e4233248baa8","libcubeb/googletest/include/gtest/gtest-death-test.h":"fdd087f700cd04a3ce4bdd36f35769de52a44bfc0c5bae2dc9681d4cbcd3c44a","libcubeb/googletest/include/gtest/gtest-message.h":"eaf44df1284d94333507c47091c84eaaf43814e6a02a1b1c0061ca7b363e74d6","libcubeb/googletest/include/gtest/gtest-param-test.h":"f226f0a24c04cddbceaaa45d8a5e575ce18c3392349f9b8ba73317e37e62368d","libcubeb/googletest/include/gtest/gtest-param-test.h.pump":"17c65fd5cc5218279044a61f3873c9c952b0924a7ba5147d4999d400b122207f","libcubeb/googletest/include/gtest/gtest-printers.h":"7046f611398d63ee0f1c37bdb4fd08d9931979b2fedf13b781e6d85d4b3b5d60","libcubeb/googletest/include/gtest/gtest-spi.h":"560407dd45e8e57fa6927504c9e4a4cfdecf30f8cada975c1ffddce765e6a88a","libcubeb/googletest/include/gtest/gtest-test-part.h":"c4d6d840284728740c284646075e8ffc85b63b9f74f3ed488b18ef3c2c2b9005","libcubeb/googletest/include/gtest/gtest-typed-test.h":"1ec858bbb9ed8a8bb553232df016437c080b2670f00453b4de297c286eb78c21","libcubeb/googletest/include/gtest/gtest.h":"47433ca72a43bda7a380c34fe6d4ff451797c687e9bbd74d1f366bcdfa0cb013","libcubeb/googletest/include/gtest/gtest_pred_impl.h":"f03a3f77e3c231889c0ace6f63b2c4e410e4a9330287ea09443b23b9a3cf9092","libcubeb/googletest/include/gtest/gtest_prod.h":"4a99a3d986a45b4d6d9b3af54809f015c54aa98274793a4ae173f5010d0ad33c","libcubeb/googletest/include/gtest/internal/gtest-death-test-internal.h":"0b3abead866363f3e6b4201acc8d2763072e033826b22ae5ebffd790e1415235","libcubeb/googletest/include/gtest/internal/gtest-filepath.h":"638d2bb6c06a894513b03311a8e931ac835fc00afc4bd21fab3afc05732c23a0","libcubeb/googletest/include/gtest/internal/gtest-internal.h":"12c2c83df0a9dc5b46697ccd8271dfa34ee5f3d1972dcb56585bc1459d9583c9","libcubeb/googletest/include/gtest/internal/gtest-linked_ptr.h":"9bd319548dd073630dfd349c06a440c6a582feec47d4ff14f348ec32f8b4c1f3","libcubeb/googletest/include/gtest/internal/gtest-param-util-generated.h":"10db93fa7e98820192bae6f560664469dd33b265036fca64253c89b6801f96cb","libcubeb/googletest/include/gtest/internal/gtest-param-util-generated.h.pump":"abb72365d94d2811b34c195dc520fbf41c7dcb42aae5a1cfa0502cf619b21e70","libcubeb/googletest/include/gtest/internal/gtest-param-util.h":"7f9311f033ef6916217d87cef53b1db6c4e8733be930e4b48fe7e11d21b33da0","libcubeb/googletest/include/gtest/internal/gtest-port.h":"612932c2930a7cf2c3514d89a8d6b51a2c0030d251309b71765ed1c9954e20c5","libcubeb/googletest/include/gtest/internal/gtest-string.h":"a46aa36165c400d1e926f942be03fe04cd7ccb1e59f7a2a03b919c4eea05b997","libcubeb/googletest/include/gtest/internal/gtest-tuple.h":"43e7e3c92f8e4258cf3927a9204b214d4d03e6c796f88f3ad4e66b1ac20aa938","libcubeb/googletest/include/gtest/internal/gtest-tuple.h.pump":"16fa027ed3c5940699e0ac906815e66620993bcf75b0acaf826d4f09348d4b83","libcubeb/googletest/include/gtest/internal/gtest-type-util.h":"6d177af46a9b1e14b969972a8b886667f95e69037aba411433a44fb9b92e7037","libcubeb/googletest/include/gtest/internal/gtest-type-util.h.pump":"22092f44127af91651f57ce222e20914d5d32ae02f1c0964f6d5d7bc019af339","libcubeb/googletest/m4/acx_pthread.m4":"3326e3746b6b351d1671fe31f798269cda8de92c365b8a8305404ec0fa6c6b32","libcubeb/googletest/m4/gtest.m4":"d3c37ebd1aa792c967d4357755cc670bc6deb30091d9e6db498871d90a30ea4c","libcubeb/googletest/make/Makefile":"9b86e2a112dd55c6bf6a2b39e6f4078faf60cfecb8282ebf9b025167ed233420","libcubeb/googletest/msvc/gtest-md.sln":"0beab679b42cf0c431eaf4fe143bbf3b01fc064e20c74d9e33e7e437a70487d4","libcubeb/googletest/msvc/gtest-md.vcproj":"52e873e964daf9d5409b4e9bb471ddf2827be04985cd96c40620f9275d17a256","libcubeb/googletest/msvc/gtest.sln":"be21c2340650ec2259a2fbaeb8608ae6d3e982a0626a0f91128a771dc88e6bea","libcubeb/googletest/msvc/gtest.vcproj":"0064616c7d88a284c1b7c05baab038f239134ea9c6c563628f286f9600b3f921","libcubeb/googletest/msvc/gtest_main-md.vcproj":"f83a294a92b616bf34ccae6743ff916297bdba61d6125a9637a813d467a30372","libcubeb/googletest/msvc/gtest_main.vcproj":"9f03270a00896eab0c7015c6fb1a73818d024e462d3944ba1d3ceb313a051649","libcubeb/googletest/msvc/gtest_prod_test-md.vcproj":"7caa108733e2d5f140da004d2133e04a9a105811909c0e2d4ec06e2971983592","libcubeb/googletest/msvc/gtest_prod_test.vcproj":"cf5bfb7f3de9a59a0eba5535067845d12c33c3fd8fecc3d03aa702665db29578","libcubeb/googletest/msvc/gtest_unittest-md.vcproj":"e7949b21cf0418f2a7afe8aa94616e2c40e3ba0801c2f0826f3a3a3d2e6f48b0","libcubeb/googletest/msvc/gtest_unittest.vcproj":"5b097d596fbbc1c4090fd518008a0961b29661194e1c02d8a2d3daaa557e626f","libcubeb/googletest/samples/prime_tables.h":"2903df1d1e6643a5f624fe3ea3f931c3410eb1858ac347c5df278273c6c91ca4","libcubeb/googletest/samples/sample1.cc":"dc106c5940d87bb4bbef3d77815eab642ee173a3340b2b9c532b5c711c4c2d0e","libcubeb/googletest/samples/sample1.h":"7a7bf9a0fbd2401e8b2cb554bfcb9bd0ed228212f3b970675c1b1d38d4e188bb","libcubeb/googletest/samples/sample10_unittest.cc":"ccebb6393a5a8468399f7e511219b667a2233f82312ce59834a4bb0997d8700e","libcubeb/googletest/samples/sample1_unittest.cc":"904be0d4a095e74393515195392bd10e1e916bb2ca61f3f94b1bd6aebea29cb6","libcubeb/googletest/samples/sample2.cc":"f14b8a1e69d52eef1a70053fb256818c7eca64e8eda08de43cf46e896e57fcc2","libcubeb/googletest/samples/sample2.h":"df956ba520dafca068dbc1e28f36567db3cba36293e06762318af8cda6a12bd4","libcubeb/googletest/samples/sample2_unittest.cc":"abe7e0b253d328cb82ae67623fbe3c89eb94699102510c64a0b568eaca101e05","libcubeb/googletest/samples/sample3-inl.h":"3fe482bbd4f725f5820f5d6beab4d0d4a524be8addf4b344a9a470ec5aabc451","libcubeb/googletest/samples/sample3_unittest.cc":"252c06b4531dc35213ebdd7311700b9b4057bc1bdeeba0cd767b2bc86c456639","libcubeb/googletest/samples/sample4.cc":"b4260f5fa35d78ac114a9abb59fce12403faf0273df41f57e83c341ae7979222","libcubeb/googletest/samples/sample4.h":"604905cae7e5587805c3b884a36eda7a2bebdfedb53b24b0fd9a220eec0ef1a9","libcubeb/googletest/samples/sample4_unittest.cc":"6cfb4821d8cb1c77fbb5af4f8aec569948762d8ea314827e3ead967b5b6a223e","libcubeb/googletest/samples/sample5_unittest.cc":"73646d9038873a68bb2e56b12495d7f7b65b5c23901109701da446af454ba2ec","libcubeb/googletest/samples/sample6_unittest.cc":"833fee399954f908cf0f3b789832e505329787f4cf73607a7b31ca0f62f368d7","libcubeb/googletest/samples/sample7_unittest.cc":"8013ee68d61c181e4e936cdae3a9a635646274f8512033ef11bff7214e03e4a6","libcubeb/googletest/samples/sample8_unittest.cc":"7b7510fadf4955d2f934d23d652dbd35add832e50bdfcc98421fb9be4588d808","libcubeb/googletest/samples/sample9_unittest.cc":"8b827040dea37b460cbcaea0b255b98974a9840f6ef7bd82aaa7d4ad2c724335","libcubeb/googletest/scripts/fuse_gtest_files.py":"adecf64c6bab65b31740c321e568cf174f753d5617745aa5762d842339d68b53","libcubeb/googletest/scripts/gen_gtest_pred_impl.py":"78fb7e20a014c251d723186eb58040e4eb32405b73c9288d787ea0a0e4ff5183","libcubeb/googletest/scripts/gtest-config.in":"9a43978eeee88e188845d737c17f4d024d4e74feae09da7997e8fbe4ea6cc176","libcubeb/googletest/scripts/pump.py":"3856a3d7be37f78e0f214ee7d4f29d05f1ca14218b67539d67c9a16e992f670c","libcubeb/googletest/scripts/test/Makefile":"3576b257594a2d8e843b9e4de8c83353d837833bb86431fb1b4198022b1bcddc","libcubeb/googletest/scripts/upload.py":"f75d0712e3b13bebd8daa0a15e4eb32c9e3034a933f4fcccf65b1e999a7ae066","libcubeb/googletest/scripts/upload_gtest.py":"6e76fc0a7a3831c01cfffd18c220d44438073a66338d91ca61fc84b924021e61","libcubeb/googletest/src/gtest-all.cc":"568ac119f5e6418f1fbcfbdf185d724657d7f3539b47822da229ac5d015626b2","libcubeb/googletest/src/gtest-death-test.cc":"eec1b3c8252670c76acbbaf63483946897ce625139b53a566406b6313f023896","libcubeb/googletest/src/gtest-filepath.cc":"31b7fcda5d11346f8a487597c6a70ff057f1192e0cb11f27eb7841a9f3aa8b86","libcubeb/googletest/src/gtest-internal-inl.h":"c9d428a6b5990ace091e40c4ce8b7bf6c50c186a8314b1c4a4cdc988ca0ac1a4","libcubeb/googletest/src/gtest-port.cc":"95bcf473622d1b901c734e5c2aeb8efb058555ec924212a61bb04f049bb5a069","libcubeb/googletest/src/gtest-printers.cc":"6f191a7fc7f5a0a967fd11964057f2e2d2eaf2f37ccece16bd816531f52b3154","libcubeb/googletest/src/gtest-test-part.cc":"e489868b4cdc66f4fc33bc2326ac86bc1acc5808ab58bbb288c9dcfc330faddc","libcubeb/googletest/src/gtest-typed-test.cc":"ca9e819df728c25a6a1fc072806c22f3494e1dffe4bd0d48284f38dbdd3a0dd5","libcubeb/googletest/src/gtest.cc":"5cf9a3e897892c9f0e5c887f91d3c8c8c5665bd7348560441fc0b946c254873c","libcubeb/googletest/src/gtest_main.cc":"22fa1f77542b882d1798d7f696045c5895942a626e26200a175fa4382e1fa5b5","libcubeb/googletest/test/gtest-death-test_ex_test.cc":"613ccf50a3ff8f84c975a13e86ea01ea4e36933072388a3738b4acf9ed3ed7cf","libcubeb/googletest/test/gtest-death-test_test.cc":"df8384a847bdf889233c3d45d171f784991def7a9b6a08442138569fbae32b9d","libcubeb/googletest/test/gtest-filepath_test.cc":"49760f91723845b113bb60bb9b1a1426ed1da1f4ebfef2462128980ea5692cc9","libcubeb/googletest/test/gtest-linked_ptr_test.cc":"1b9cb4ff67475900db9de34ae9749b94193048a1f7a741091ba5a2dd7fc7a79b","libcubeb/googletest/test/gtest-listener_test.cc":"acf78f2c9a730525ea5adc93e9196a42de8fbfe488db1dfd02656bdbd477b2c0","libcubeb/googletest/test/gtest-message_test.cc":"b1fc68f8b75ce25fbd79b3f7d3c9c793381ef07b3203e1a2d9b610cb597542be","libcubeb/googletest/test/gtest-options_test.cc":"74e3ae0c310edb3139b0032266219d3ce7f386ded6feafa57fef03f4493ed7fa","libcubeb/googletest/test/gtest-param-test2_test.cc":"a0f1efbcab3f7e49df639383157626931f64756f7e738be081760f93f7308332","libcubeb/googletest/test/gtest-param-test_test.cc":"ef8bd344e959053f562b0c9e0d15e2fb6c1e534772a67aaf3f90bd6bad0bf99f","libcubeb/googletest/test/gtest-param-test_test.h":"9d7f47b79d54df7cc050aa6038b0464aa684dfca669a847bf70ea16e4a000628","libcubeb/googletest/test/gtest-port_test.cc":"1600f78ef0860a0f5b5525e5e5041ff32a216cc6ae948b1ea61fe04ec603f67d","libcubeb/googletest/test/gtest-printers_test.cc":"7898e4b4163ee0821fed248e1c75d9f4a0a511a2b4bbfad1ef2f4a11a099f6e7","libcubeb/googletest/test/gtest-test-part_test.cc":"62c8906bb0d12ea84d60217b3773cd8e1768db4aab934880db2316df7026cab8","libcubeb/googletest/test/gtest-tuple_test.cc":"2850dc1f73a3f8020d8a4d80688a28d9b736eae6d677222c3f871d8d33b25501","libcubeb/googletest/test/gtest-typed-test2_test.cc":"c52b65e7181610d6e577631cd50177399884913ff28d08aedfedc92f05185044","libcubeb/googletest/test/gtest-typed-test_test.cc":"c7daff5211028da79b3ca0473dca18ada9197f38e710f72d0493ad3332ce3ec9","libcubeb/googletest/test/gtest-typed-test_test.h":"3145698534d8869beb624c9c8ed114f75bead046b2eeb92ada5a724993ee7786","libcubeb/googletest/test/gtest-unittest-api_test.cc":"e3f54c28ef2849e8b12af666ed46aace50c3e047845072ee6f974ce4528bd297","libcubeb/googletest/test/gtest_all_test.cc":"db0c3c42b385570b1d517e3ee927671b8fad4e206247fca738ec477222ac3d97","libcubeb/googletest/test/gtest_break_on_failure_unittest.py":"11c91bc1c68cfdb913e2affb01261b55fb3b0c18773a45875e9c25cb330a4dcd","libcubeb/googletest/test/gtest_break_on_failure_unittest_.cc":"1da12e4bdda2a0bc7b59d4638fe34b2d3798134224fd9237eeebdd09c3326011","libcubeb/googletest/test/gtest_catch_exceptions_test.py":"305cef45c6dc034bdf72fd91aba1e89e1c6b5d222c3d6baffff5acdfd9b3873e","libcubeb/googletest/test/gtest_catch_exceptions_test_.cc":"b297a4f4d5bc0285ea9eb8869741631658305e49d4513bca904842aacb82128b","libcubeb/googletest/test/gtest_color_test.py":"c4cb006682a40f2d88759a4bcabf0d4be623720b135c71447f1788d17ea23d0f","libcubeb/googletest/test/gtest_color_test_.cc":"f263ba349afe58a558bf0fee98a98bb9207a648e7cd4f908a87799bd13d001ea","libcubeb/googletest/test/gtest_env_var_test.py":"79819598cd1e366eaa8f2a4fee2d638b6ef0686e490402fae792ccce58d876c0","libcubeb/googletest/test/gtest_env_var_test_.cc":"0eee5dfbb2a2598f4e76626346b921928ec1e052e38f254cc97c60d05611ab46","libcubeb/googletest/test/gtest_environment_test.cc":"a52a21ea29c2203b03fa93922733546d171f98d3b2fcd42972269e98fd124715","libcubeb/googletest/test/gtest_filter_unittest.py":"edc7d278803bba41626eacd050d91d7247f1c5999f9dceb99a8877e238bc73d6","libcubeb/googletest/test/gtest_filter_unittest_.cc":"996ac528ad75c293d8201ce28cf6acccee266286bd369b4cf43f05b8d67a4559","libcubeb/googletest/test/gtest_help_test.py":"b43ab690c08e4bffd84a47b361167496298697f9511bdf4a745bf305b5cfbdfc","libcubeb/googletest/test/gtest_help_test_.cc":"ff4b121098f0fe7cb4abf11fdd31f2fe7a477286ec9175482138bc038d61c807","libcubeb/googletest/test/gtest_list_tests_unittest.py":"7caebc175b44b3c727fc50420ada1a6a9500f3e4ce9e2839f69205437aa85e7a","libcubeb/googletest/test/gtest_list_tests_unittest_.cc":"d82d8b72914897232c2ff9fd091a7b0add68b7cf75f3f210d3a487ebeea84cfe","libcubeb/googletest/test/gtest_main_unittest.cc":"0f66f318809c88f0fbe034a340a75331720c4e33be5378022baffaf588ef1202","libcubeb/googletest/test/gtest_no_test_unittest.cc":"7cf487e07c3d27376c2cb8af33d02239b7966623875d37b7aa0259e927a9c2f6","libcubeb/googletest/test/gtest_output_test.py":"cf0dc1979572d94450a5e611b44f3fdb88d9cd980d669a723f0ed63057b5e2c4","libcubeb/googletest/test/gtest_output_test_.cc":"f69569374c2b3d06aa04a38ebc4f92ddc303e6af503f8b533cd8e6bf9f104899","libcubeb/googletest/test/gtest_output_test_golden_lin.txt":"4f3e49c10a524a99437cdcb5294e3335a7f3c07ea8462e65730f703a5fe4fec3","libcubeb/googletest/test/gtest_pred_impl_unittest.cc":"e406eccf75b6b58746a95d1c7ea7bc8e80ff974e438ef7c83074a46d4e62db9a","libcubeb/googletest/test/gtest_prod_test.cc":"b42ca1a6d0a1e43bc576b4ff7776c6d2c37234f6dc2a76f2735f261b4a47a526","libcubeb/googletest/test/gtest_repeat_test.cc":"e10abbb71595920aa3bb415029eed74106335fc9ea3d58c417ccfc7cba6a4cdb","libcubeb/googletest/test/gtest_shuffle_test.py":"12dd94eb5f30260ba37059fa74658bda57dffa821f3ba6a2a8b52ff14b1ad029","libcubeb/googletest/test/gtest_shuffle_test_.cc":"af1b2b01ae275f1a9fee7e7940c0f88f39ded063008994d585aad87e3ffadb39","libcubeb/googletest/test/gtest_sole_header_test.cc":"538414c27a593ab8dc34c37b5c877eb3a022d75d1b481ef14ceca00914929754","libcubeb/googletest/test/gtest_stress_test.cc":"0b02fc12f87944226915a999bdcc8a3eaafb34a1ea5bb0df128774abf8667f09","libcubeb/googletest/test/gtest_test_utils.py":"d4a76930aee6658ad8734981ca0c4ea14f34dbe8fdd31d5afe41f6d98f9779ee","libcubeb/googletest/test/gtest_throw_on_failure_ex_test.cc":"11ae790028da20bc7b7af1572eff3cfe8499be43ab64c110e18e1892612a183f","libcubeb/googletest/test/gtest_throw_on_failure_test.py":"ebe18ca0b07f90c53b9b3f9a54ed02df94facf8995cfa90dd41c6f5474537c13","libcubeb/googletest/test/gtest_throw_on_failure_test_.cc":"f8cbf75d8bf9e9ae068a17ff968434c3aec7b7f1137c994d8f14af1a84361aa9","libcubeb/googletest/test/gtest_uninitialized_test.py":"da8e6ce34930753e36fc1dfa2c3e20e48d02bda2a27d3d03a07364312c5f3bd9","libcubeb/googletest/test/gtest_uninitialized_test_.cc":"0b6a9d4983480f87352ca4da946089264b401f7a4a3b1282253fd6cc861bf483","libcubeb/googletest/test/gtest_unittest.cc":"c0c7d1f691ce1e10c3d1647ed5f7a66510725808ad58bb6da4bc03a7a08fb2fc","libcubeb/googletest/test/gtest_xml_outfile1_test_.cc":"29341d777a9c9d25f360d13ed966b30f0cbef1fc88aefe2f01bb88b82cf1ed93","libcubeb/googletest/test/gtest_xml_outfile2_test_.cc":"da7ab3cf0e9b2a293eceed7c5691233d6b61afb557e3c1176dfb75390f85be46","libcubeb/googletest/test/gtest_xml_outfiles_test.py":"b07927b43f44afbfd61761c2cc69f1b68c4fbdeddb992db03ff0c73052518cd4","libcubeb/googletest/test/gtest_xml_output_unittest.py":"b5ff0c0207238d01cada961b8f4656f2ec30a3e1e5bf9d22efdf1745af423031","libcubeb/googletest/test/gtest_xml_output_unittest_.cc":"ad0b9ebe63a146e386df3c5c51916869f6d4647b9832ceacc912fb1272d15f82","libcubeb/googletest/test/gtest_xml_test_utils.py":"ad89a39a6cd5b08e87082341f3e7800dbf1150ea0f1386e0b8cd374aa6832f00","libcubeb/googletest/test/production.cc":"56fef77c3a8e62073ec11653d740a8e534008a0d57925ab0877b843f4fdd6be8","libcubeb/googletest/test/production.h":"a36d10545d12ead5e93a3b0fdca6ff73405f29091cfe38164415e9038888ba8d","libcubeb/googletest/xcode/Config/DebugProject.xcconfig":"fb42302df29bd8e8b5237194c0c04941f0e578527037930d88469baeb7a7f62b","libcubeb/googletest/xcode/Config/FrameworkTarget.xcconfig":"9935ddabe221f074d2f3b969a137d12b0dc0f845a460f58b63232987cb0f37ff","libcubeb/googletest/xcode/Config/General.xcconfig":"0fb768924daba1048f8db28b3a1fbf915b6f788d49d9d37e85979aa4ee92e02d","libcubeb/googletest/xcode/Config/ReleaseProject.xcconfig":"a4878ddd1ed78fb411906623cb51bc0ab4aea1cc3feb5379d2ae2862d8bf3bf5","libcubeb/googletest/xcode/Config/StaticLibraryTarget.xcconfig":"5886291788b3e9d5aadcb979ff055fd26a2413be81016e7afffb813b627d177c","libcubeb/googletest/xcode/Config/TestTarget.xcconfig":"f074e6c2516a6063b253ec6b842d74f5c2abefc7bcf8d8da54097a7bfe016480","libcubeb/googletest/xcode/Resources/Info.plist":"5b7f78a6d5810ce5490649793175c8982d41e6b49af06bc0705bc061567cc9aa","libcubeb/googletest/xcode/Samples/FrameworkSample/Info.plist":"1c13d83e5eed45689d7fe4bf4681030366474bc40608e39e1623c1350513a0cd","libcubeb/googletest/xcode/Samples/FrameworkSample/WidgetFramework.xcodeproj/project.pbxproj":"1cf0e1b1abf84414372faf9c8bf634e01fe5750bb3ca769b1eb25fc530b21358","libcubeb/googletest/xcode/Samples/FrameworkSample/runtests.sh":"a587e5b00a8353dee0aca5a4c59b28301ccf7648dee8c79b62a9223f9fc3c8cf","libcubeb/googletest/xcode/Samples/FrameworkSample/widget.cc":"562a2bb615e93186012823c9b41761769638a11e38b54498ad5f699038c8fd32","libcubeb/googletest/xcode/Samples/FrameworkSample/widget.h":"0c7915e45cf7cb8d67db24e49cd0b277f23f967578f917f8e859a6adc4b156f9","libcubeb/googletest/xcode/Samples/FrameworkSample/widget_test.cc":"6a1a49d64912d9829ef3d991faf5a3f0e1e081126a1d8d387cdfa84fab70dc77","libcubeb/googletest/xcode/Scripts/runtests.sh":"1a0672a4151b16f3c797478ba26c534e82b2faa603f90b9aa14e785805f7683a","libcubeb/googletest/xcode/Scripts/versiongenerate.py":"4b9d5c0f4e1b556084109311d156bee6d781968dc5b1dfdc8702364508f1dd43","libcubeb/googletest/xcode/gtest.xcodeproj/project.pbxproj":"a1224decff058bfed01b8eefaee13cab0129492615548c6d0d878003a154f7ff","libcubeb/include/cubeb/cubeb.h":"6dca4f874a188e6bd7e476c22418a7e011d6fefd77cb9cce5d061c9ef527f29a","libcubeb/scan-build-install.sh":"1ecf22aca367a4d02f810c4cb78db8636e08a12787e94e30c441ce439cf4a265","libcubeb/src/android/audiotrack_definitions.h":"0d5ec772f9ebf61333bc16e61a081a7e3b4cc02342ec4f8f417e220d032fbbc6","libcubeb/src/android/cubeb-output-latency.h":"000fb7bec38105f54b9d40fc436440c637d63390b219775d335216d29d767226","libcubeb/src/android/cubeb_media_library.h":"f67965fb2ea38f0023eb4c76024341ba9563ac1a135507b6f12a9aabd85e30a9","libcubeb/src/android/sles_definitions.h":"66d10b157e463ee106236736b0338d1ec512d66d577ecafda56427bf14658443","libcubeb/src/cubeb-internal.h":"e58ce40e1402a8fbbbc048b453f01868174aac191b6cc8b66be7ceb0141cc2c4","libcubeb/src/cubeb-jni-instances.h":"8195554372bf60dea569873c9e5fb1106a2cf5dedc66a13d2bc967da0ff48a12","libcubeb/src/cubeb-jni.cpp":"81f001720c41c69b5927e32bd19b9e8e7176d7c33d63c2a58bd0d695dace4fd2","libcubeb/src/cubeb-jni.h":"73f810a32087a6062fd49ba89542655a7e19cecac6f40f8411e1d77ce42a45d1","libcubeb/src/cubeb-sles.h":"dc84a01ba4de9db1ff108609930b36ba442020ccc3e3d6f16528f429426c430c","libcubeb/src/cubeb-speex-resampler.h":"dbf543eee4cc6e40ba3531a39d327e2cec884c19e26c570aa7eae0647e5f7420","libcubeb/src/cubeb.c":"b8f8e5ffd028aa2d50745f26ee69b51e476576724c0d67534029370310ab9caf","libcubeb/src/cubeb_alsa.c":"4ae98dfc610a5f0814aa8dd9eac3c05eca72fba59fb2025239f4f336847c9e63","libcubeb/src/cubeb_array_queue.h":"5264ae02799c540ff73e8eb8efa55986772b22562a025ae227c2603b6b8d1036","libcubeb/src/cubeb_assert.h":"ab8ed4fe7070a3aed9419c5f9695fce2318c4dafd91213ae61cac476e64adaa7","libcubeb/src/cubeb_audiotrack.c":"04bb993fc2a8dfaaca6f7930370111fdc1b072f054b1e4d7c34658cabd107fd8","libcubeb/src/cubeb_audiounit.cpp":"9ad755c93b2a4893e59547d7089ebe95207dd8e4dd8a2741150dc5759bd0146b","libcubeb/src/cubeb_jack.cpp":"1e4676767720df3599f424e6973660e11011b79d89faa4bfdd5910f30a65ceb0","libcubeb/src/cubeb_kai.c":"068d48462f3263013aa59aff589039a334c5cd4eab4254f8011e4bb60afeadbd","libcubeb/src/cubeb_log.cpp":"74bdc0d92a31d6b4ce3c376c6d310db583b869d673e40e6bd0ea795a1e2f937a","libcubeb/src/cubeb_log.h":"ee05fd22ed9820bed79247b8603cdc64f67bcb145be47e78bf0a0e99fb6c0806","libcubeb/src/cubeb_mixer.cpp":"061427a06516792ead3936a643224c3d275913410f67765cc24e1b8699e8d99d","libcubeb/src/cubeb_mixer.h":"e94205c1b98859e41eeab2cad53f635f34f57a2ae95aa23a36461a2619abf81b","libcubeb/src/cubeb_opensl.c":"cfd2e5a53a8e10f54ab4e7259ebc9ee5034bf5958642a28527d7397aa5bb4b2f","libcubeb/src/cubeb_osx_run_loop.cpp":"13c029f7ea04808597b8841b13f248c4476fb7425762f01a59bc6c944cab3a78","libcubeb/src/cubeb_osx_run_loop.h":"ffe80df60b8ea3d0341715b72cc60aae2e0df45141887c01117df543260a0ef8","libcubeb/src/cubeb_pulse.c":"dec7a5a31942f343ab47b90974aed66bb7bdcc17a1d256edcf403aefaec8b01c","libcubeb/src/cubeb_resampler.cpp":"f58deac4d17f004e8b31773335bb0cafbde08494cbda8453fa39a36617e47337","libcubeb/src/cubeb_resampler.h":"ad9800a40c0272fb2a451c02367cc5a516a3067d4acf963d35eb31be367f9224","libcubeb/src/cubeb_resampler_internal.h":"4b4eb6aee343b9c1f73cf17b201329d3714ba44b06ecb1cebf950fdf93a9dfe6","libcubeb/src/cubeb_ring_array.h":"db8dec7a6d778a424045e5ac61c2bc3a3cec8c4fe8e4780f383db4b6f1b2438c","libcubeb/src/cubeb_ringbuffer.h":"2296255ca7835a3ace2fe1852054f43428b677f432b4174bd832ad0224a409eb","libcubeb/src/cubeb_sndio.c":"0037a0bb173ad9b0302e3cc24edc5062d6a37dc4d2505d035282043d99e61c5b","libcubeb/src/cubeb_strings.c":"60f2b8c03a5a01925f9777eec0ab84b0e704b40c03e0d6b9c0e7a16246c81fde","libcubeb/src/cubeb_strings.h":"00e5dc97cf16e28cfb53aaae50ac0e3c0ae8a82aad0720ab6842ce5d9785c39f","libcubeb/src/cubeb_sun.c":"3af2394c4d3575ef2c7862549789eda29a61a4a00646e18299edaa1c72259daf","libcubeb/src/cubeb_utils.cpp":"5bd7de8bbf7bbdc3a275525edd92bad590fb90fb4d35e77017fdd5d25a5769e6","libcubeb/src/cubeb_utils.h":"27baa42747771bf9232741382b83722f5c731e5dcd4dc2e9b595aca91c5647a5","libcubeb/src/cubeb_utils_unix.h":"2a48bd2aefa1b4e4e4968121512bcaaa6be3bca6597ea390b8203c338f5c49b5","libcubeb/src/cubeb_utils_win.h":"74658b6266a8a223c681b0fd06fcc9c891c7b0b95377635c6f1763b240c2ab27","libcubeb/src/cubeb_wasapi.cpp":"bccff541f927f6cf436806a3b04c4fc97d001dc2119279ed62bf27c18f6d8bcd","libcubeb/src/cubeb_winmm.c":"7416b044d333892e79489a2b3d787ef50bf9ea65db99cbe03885b04967df9b7c","libcubeb/src/speex/arch.h":"2300bce68c588270cdc684dc7f01377e5e251529f4545d93771e111c56d51b0f","libcubeb/src/speex/fixed_generic.h":"306ee7453677fa6067f16c79d358c6c90a9d3d008850b493cdaa59c07e6375c1","libcubeb/src/speex/resample.c":"9c3a1c64ecf3750af82c980d01ea73d3682f73c332a580465d1e787e5c54cd00","libcubeb/src/speex/resample_neon.h":"7d3fd7af9a1ddde22518b9c7b4419073b72b2dfa5be4c3bc8796992bc87b3da0","libcubeb/src/speex/resample_sse.h":"5a196d8e2d8ab5c956f5252f09f5ddc55aee1f99b1341af3fa54a1f4c2157924","libcubeb/src/speex/speex_config_types.h":"24e3ffbf29e5519611a48e5acb959645b01d166dcb4369380d5f776c3f53d4cd","libcubeb/src/speex/speex_resampler.h":"7e439ec0dd30c32216b3ced17135f8992e5aaf53389d3f5996a7d900c453e65f","libcubeb/src/speex/stack_alloc.h":"e8a2fc0874942d2c7177475fcc141fdd0c0156200b8a4e7656d4a20313e2e569","libcubeb/test/README.md":"1c11b038f87daf10ea78abc17bdbdd157940c241df548c24d5872d142a98c1af","libcubeb/test/common.h":"968144634f3ce627d7f6cb91d48ddc6f1fa89f481ae63f80a7cbe743989e449c","libcubeb/test/test_audio.cpp":"4dfa5e686293101541a300b04712f0cd5a32904fc1f1a71f73ca872d56050e57","libcubeb/test/test_callback_ret.cpp":"f2d190ec14b2da927bb4cd759f775cd3b7f38df3cd2984862b346f7c37eb9ca8","libcubeb/test/test_deadlock.cpp":"0e28e59f360e27241536879d9e02e4e05291db252f66fd601577aaf723e06543","libcubeb/test/test_device_changed_callback.cpp":"c30bcd2ec4348f56c520952cef18a47c430f121cad59d64d648498c14e79f080","libcubeb/test/test_devices.cpp":"8c61e4319af2af5a59103fccd216c666424d42b859b8719166513e26eebd39c3","libcubeb/test/test_duplex.cpp":"fa9944a3d7975b21ad0b61690f2ab40c4ed16a4c3f2758d7b6ad0df60cbb0768","libcubeb/test/test_latency.cpp":"dc5af071df0ff0c4b2f6cf7b37438ac8ed9f18cc0fafffb2c685b1e82201c9f5","libcubeb/test/test_loopback.cpp":"f67c2828e2e238f07a0d1371d42ae916b9d7decfc4233fcf4d632346ef98871d","libcubeb/test/test_overload_callback.cpp":"f3ffa014a0e6f7871a023b46733f93d992ddc9b41e0974cc22a365979b6d989e","libcubeb/test/test_record.cpp":"89a884872b780005b2fb29d106b6f33ddd76b2fcae381395e17d3413153b3b89","libcubeb/test/test_resampler.cpp":"ca4bc0493ce0c3a5f02f389ac6e37bc2cdb9a752faad2b6fbc5af0a2223ef645","libcubeb/test/test_ring_array.cpp":"27836d716616abf8b020a710659c10ac5e1d4b64149d22cb89bd7a7a5a5669d1","libcubeb/test/test_ring_buffer.cpp":"4ca684e38f1d34bb1f625ce2a2a730e5b24e09d1107f108e8b80731e55315861","libcubeb/test/test_sanity.cpp":"19e2b22e50fef3120859bfe9e9c5fcf5720b47e694eb02c9cd388c3345706ea2","libcubeb/test/test_tone.cpp":"02de19023d203e2d538c4f8ae23574153a4df6ada1d06fb83af2d51603b61669","libcubeb/test/test_utils.cpp":"e8d7a02a9096dbc0fd15d71f9849415d52958d2ed82cbc7b8507d3228e08d382","libcubeb/tools/cubeb-test.cpp":"112ecc87060761e31c4fa181de3c17fd91f0d0a135f6e1085f0f6a0331933fdb","src/callbacks.rs":"b4b4eb3f370475488d7c77b338b0bceee94ef59ede8ff41fa797a3d35ea98b5d","src/channel.rs":"555bc8d5257496cf6e5b4c786df84755327d88a9dc07202746e35f3b7b7e4e0b","src/context.rs":"96bf3442b1dd40262d3088dbb6c473f5451fd91320cef93c522b40f284599803","src/device.rs":"5b65e1c1fdd53fa5208f20bfb809475cbb3c434e02db7d0e4771d13c86f8e369","src/error.rs":"406e3b843ed2d263fe677c4b34fb96a6d780a68bcb56a6f85f041d20d70227bd","src/format.rs":"d4d27790c20eab0b16592f60d5e487425a45a268cf4c74cf843c10ac91bbff4c","src/internal.rs":"c3af5f53dc7957860bf3bc0cd9737d094fb8ac000e7b40c569304cfa76a43145","src/lib.rs":"cdc36b11f18e274d10df592d216f142e05e1d22db5aac4c7e346893d6b1510db","src/log.rs":"6694178177775f2ce5449435d00609e9361c836e23119c07d04892000587dc55","src/macros.rs":"caef13f5d23f7a3ec1a54ec3ca2390ac4ad89d521893f1d0864daf70d57a20aa","src/mixer.rs":"e72e92855614da187da6419ed0a115062a05670cb73443b2ea4313d8f5108a68","src/resampler.rs":"762070f8afde2256715b8764380cdfa1204a1a39d98a9da0b2efe88699792f2c","src/stream.rs":"c4e102bf8ca9c63144346342ab2668b6f554969eb0f6bae96a58eb0420534adf"},"package":"309c5839c5fa03c08363bd308566cbe4654b25a9984342d7546a33d55b80a3d6"}
\ No newline at end of file
+{"files":{"Cargo.toml":"fd97db61bb65f2fed82030616ad06d17d28bd4bbe12389b3d0f4367da1fbd2d4","LICENSE":"8c044baa5d883274736eeece0b955249076c2697b826e576fce59496235b2cf5","build.rs":"fcfbe7f9f2f3981f451cb3e6746ab09415ded80eb9cffb0abb152a3898765d41","libcubeb/AUTHORS":"829e45d138c7c8827799f302806fa4be8cd8bd4bad70a2fe26c3a27a5cf36948","libcubeb/CMakeLists.txt":"2c4c4d0bfc88494b2e834c12b2a1a49e02f7726292a19962495d27a69da6851b","libcubeb/Config.cmake.in":"88019286c96ef3d5d3a673b183c8655dfc97ceede07d3eb6c18f0c51bb896388","libcubeb/INSTALL.md":"7a84cdfbe86e7d3180e2203603b88571df61b369421fa97ee86740ffd4d4db8e","libcubeb/LICENSE":"44c6b5ae5ec3fe2fbc608b00e6f4896f4d2d5c7e525fcbaa3eaa3cf2f3d5a983","libcubeb/README.md":"54736bf33ee10535d12b9656e8b37043e9b86d97fb2f8fb8133adfdf0af1d9f5","libcubeb/appveyor.yml":"9a87fdc18b76fca1167289ecb2ec3a210b76984c1d7e92268b2cd36f1e00f541","libcubeb/cmake/sanitizers-cmake/CMakeLists.txt":"89b282c19b3897ff666f7847d5e679ae928ca7e76ffd0d23f7c58c9464048b61","libcubeb/cmake/sanitizers-cmake/LICENSE":"4b67e7ae8c91e68e1a929eb1cbaa4c773c6d19aa91aaa12c390cf9560d1c9799","libcubeb/cmake/sanitizers-cmake/README.md":"f3b85a594e1601ae65bb09393c56fbc8a98e3f3f49234e56f86bd78803c3544e","libcubeb/cmake/sanitizers-cmake/cmake/FindASan.cmake":"bd30b1c3cb0682b8a2b0b599f6c51fbf99ef4908b00eba8826f0d484c623b01b","libcubeb/cmake/sanitizers-cmake/cmake/FindMSan.cmake":"55e40f0994b47f8e779622af2007c69252d9456be99f4aa4fba7dd49c23d5580","libcubeb/cmake/sanitizers-cmake/cmake/FindSanitizers.cmake":"2ffd9717a49e3e9de848ec0d91097bdbab3f75a7866211d902f9659f59271ffc","libcubeb/cmake/sanitizers-cmake/cmake/FindTSan.cmake":"ceace234abd5a463ad1b8134ad06c4c8004895b2e8fc9edb67e64620ea97ca47","libcubeb/cmake/sanitizers-cmake/cmake/FindUBSan.cmake":"4720ec96ea42a2a9c07d887c1fc5562ed3e8c222f5778a18b78c46678b23f156","libcubeb/cmake/sanitizers-cmake/cmake/asan-wrapper":"4e543936e6374e24b80a0f92135c07c2e2101c0d110e51bddaf0e70ae8ec391e","libcubeb/cmake/sanitizers-cmake/cmake/sanitize-helpers.cmake":"c0c225f22bebd27eb68b807b2a61962d28afc61609735fe652c2fde6aa6a005b","libcubeb/cmake/sanitizers-cmake/tests/CMakeLists.txt":"64b7aea469a043f27414e3d652aaa89a03a11843d152c4cd42f30561e6ef51bc","libcubeb/cmake/sanitizers-cmake/tests/asan_test.cpp":"a25de95282aaee22d977d0b474468b946c1422b80b15424c04266dba038eba2e","libcubeb/cmake/sanitizers-cmake/tests/shortest.ext.test.cpp":"a25de95282aaee22d977d0b474468b946c1422b80b15424c04266dba038eba2e","libcubeb/cmake/toolchain-cross-mingw.cmake":"1c26749465364061ddf37aaa00f363eaf057c4b6a70181086e13b0a4d09bd376","libcubeb/cubeb.supp":"19f33e59f8dc91a327c923e44c2c3f9af0a043ce1d6a8cac275ba094b4bfe0da","libcubeb/docs/Doxyfile.in":"0815f19789cedd310652a133bab260c27b57e95f7a65458df2dfca38ea4f1041","libcubeb/googletest/CHANGES":"72c8a289bfe4dd9160074a3a2238c8067a5bc7ca49fd87f70a134c3f373932a4","libcubeb/googletest/CMakeLists.txt":"cdf938ce79ea066866dc614de3925bfaa48d9e19e04049db2ec8c5443abaaa9b","libcubeb/googletest/CONTRIBUTORS":"4d911cd5e6d71f3f4bbcb2788b2f916df4b0ce1e088752a159457a1d936ae0ce","libcubeb/googletest/COPYING":"9702de7e4117a8e2b20dafab11ffda58c198aede066406496bef670d40a22138","libcubeb/googletest/Makefile.am":"a795e5a18e82ba06fd97509d92d773b7fafd0dd7086db8a1211fbd151b503bac","libcubeb/googletest/README":"484b19654362942fac9734c8fab0ed3d99249b81c69027bdb7de46054abada6b","libcubeb/googletest/cmake/internal_utils.cmake":"6f4670a5825cf8ae0415be9dd43d82a7f30316d75cab20a4c60afb6d9db2a01d","libcubeb/googletest/codegear/gtest.cbproj":"9fa07a66b8c01773256e508187775407c465ed9055651e93d390426d1888721a","libcubeb/googletest/codegear/gtest.groupproj":"76c001cb2ee0070e26d1570fb6db5250f2585179c707496c5ef6d12e582cf205","libcubeb/googletest/codegear/gtest_all.cc":"c8750dc2b7b3612edb60e8d23fc2f60ae264451916c4b15f81cbf60ea30eb75d","libcubeb/googletest/codegear/gtest_link.cc":"e5e47c086f932471e0ca748a62b3b1597e5e731f4d34fb8cb8b2ca2db7ba3ed0","libcubeb/googletest/codegear/gtest_main.cbproj":"056448ba7f41fae3f704b1001922d9f6e419cf8cbcf3a55bf0ecc59ece1fa9a1","libcubeb/googletest/codegear/gtest_unittest.cbproj":"2c48be90b0dbc3224a8bd6c4dc21f78dd1b08c81685e6bd4952bb6df6ee38701","libcubeb/googletest/configure.ac":"fadebffdaeaccebb60fbe4a501c1138c296e01348a5da45fabf5e4233248baa8","libcubeb/googletest/include/gtest/gtest-death-test.h":"fdd087f700cd04a3ce4bdd36f35769de52a44bfc0c5bae2dc9681d4cbcd3c44a","libcubeb/googletest/include/gtest/gtest-message.h":"eaf44df1284d94333507c47091c84eaaf43814e6a02a1b1c0061ca7b363e74d6","libcubeb/googletest/include/gtest/gtest-param-test.h":"f226f0a24c04cddbceaaa45d8a5e575ce18c3392349f9b8ba73317e37e62368d","libcubeb/googletest/include/gtest/gtest-param-test.h.pump":"17c65fd5cc5218279044a61f3873c9c952b0924a7ba5147d4999d400b122207f","libcubeb/googletest/include/gtest/gtest-printers.h":"7046f611398d63ee0f1c37bdb4fd08d9931979b2fedf13b781e6d85d4b3b5d60","libcubeb/googletest/include/gtest/gtest-spi.h":"560407dd45e8e57fa6927504c9e4a4cfdecf30f8cada975c1ffddce765e6a88a","libcubeb/googletest/include/gtest/gtest-test-part.h":"c4d6d840284728740c284646075e8ffc85b63b9f74f3ed488b18ef3c2c2b9005","libcubeb/googletest/include/gtest/gtest-typed-test.h":"1ec858bbb9ed8a8bb553232df016437c080b2670f00453b4de297c286eb78c21","libcubeb/googletest/include/gtest/gtest.h":"47433ca72a43bda7a380c34fe6d4ff451797c687e9bbd74d1f366bcdfa0cb013","libcubeb/googletest/include/gtest/gtest_pred_impl.h":"f03a3f77e3c231889c0ace6f63b2c4e410e4a9330287ea09443b23b9a3cf9092","libcubeb/googletest/include/gtest/gtest_prod.h":"4a99a3d986a45b4d6d9b3af54809f015c54aa98274793a4ae173f5010d0ad33c","libcubeb/googletest/include/gtest/internal/gtest-death-test-internal.h":"0b3abead866363f3e6b4201acc8d2763072e033826b22ae5ebffd790e1415235","libcubeb/googletest/include/gtest/internal/gtest-filepath.h":"638d2bb6c06a894513b03311a8e931ac835fc00afc4bd21fab3afc05732c23a0","libcubeb/googletest/include/gtest/internal/gtest-internal.h":"12c2c83df0a9dc5b46697ccd8271dfa34ee5f3d1972dcb56585bc1459d9583c9","libcubeb/googletest/include/gtest/internal/gtest-linked_ptr.h":"9bd319548dd073630dfd349c06a440c6a582feec47d4ff14f348ec32f8b4c1f3","libcubeb/googletest/include/gtest/internal/gtest-param-util-generated.h":"10db93fa7e98820192bae6f560664469dd33b265036fca64253c89b6801f96cb","libcubeb/googletest/include/gtest/internal/gtest-param-util-generated.h.pump":"abb72365d94d2811b34c195dc520fbf41c7dcb42aae5a1cfa0502cf619b21e70","libcubeb/googletest/include/gtest/internal/gtest-param-util.h":"7f9311f033ef6916217d87cef53b1db6c4e8733be930e4b48fe7e11d21b33da0","libcubeb/googletest/include/gtest/internal/gtest-port.h":"612932c2930a7cf2c3514d89a8d6b51a2c0030d251309b71765ed1c9954e20c5","libcubeb/googletest/include/gtest/internal/gtest-string.h":"a46aa36165c400d1e926f942be03fe04cd7ccb1e59f7a2a03b919c4eea05b997","libcubeb/googletest/include/gtest/internal/gtest-tuple.h":"43e7e3c92f8e4258cf3927a9204b214d4d03e6c796f88f3ad4e66b1ac20aa938","libcubeb/googletest/include/gtest/internal/gtest-tuple.h.pump":"16fa027ed3c5940699e0ac906815e66620993bcf75b0acaf826d4f09348d4b83","libcubeb/googletest/include/gtest/internal/gtest-type-util.h":"6d177af46a9b1e14b969972a8b886667f95e69037aba411433a44fb9b92e7037","libcubeb/googletest/include/gtest/internal/gtest-type-util.h.pump":"22092f44127af91651f57ce222e20914d5d32ae02f1c0964f6d5d7bc019af339","libcubeb/googletest/m4/acx_pthread.m4":"3326e3746b6b351d1671fe31f798269cda8de92c365b8a8305404ec0fa6c6b32","libcubeb/googletest/m4/gtest.m4":"d3c37ebd1aa792c967d4357755cc670bc6deb30091d9e6db498871d90a30ea4c","libcubeb/googletest/make/Makefile":"9b86e2a112dd55c6bf6a2b39e6f4078faf60cfecb8282ebf9b025167ed233420","libcubeb/googletest/msvc/gtest-md.sln":"0beab679b42cf0c431eaf4fe143bbf3b01fc064e20c74d9e33e7e437a70487d4","libcubeb/googletest/msvc/gtest-md.vcproj":"52e873e964daf9d5409b4e9bb471ddf2827be04985cd96c40620f9275d17a256","libcubeb/googletest/msvc/gtest.sln":"be21c2340650ec2259a2fbaeb8608ae6d3e982a0626a0f91128a771dc88e6bea","libcubeb/googletest/msvc/gtest.vcproj":"0064616c7d88a284c1b7c05baab038f239134ea9c6c563628f286f9600b3f921","libcubeb/googletest/msvc/gtest_main-md.vcproj":"f83a294a92b616bf34ccae6743ff916297bdba61d6125a9637a813d467a30372","libcubeb/googletest/msvc/gtest_main.vcproj":"9f03270a00896eab0c7015c6fb1a73818d024e462d3944ba1d3ceb313a051649","libcubeb/googletest/msvc/gtest_prod_test-md.vcproj":"7caa108733e2d5f140da004d2133e04a9a105811909c0e2d4ec06e2971983592","libcubeb/googletest/msvc/gtest_prod_test.vcproj":"cf5bfb7f3de9a59a0eba5535067845d12c33c3fd8fecc3d03aa702665db29578","libcubeb/googletest/msvc/gtest_unittest-md.vcproj":"e7949b21cf0418f2a7afe8aa94616e2c40e3ba0801c2f0826f3a3a3d2e6f48b0","libcubeb/googletest/msvc/gtest_unittest.vcproj":"5b097d596fbbc1c4090fd518008a0961b29661194e1c02d8a2d3daaa557e626f","libcubeb/googletest/samples/prime_tables.h":"2903df1d1e6643a5f624fe3ea3f931c3410eb1858ac347c5df278273c6c91ca4","libcubeb/googletest/samples/sample1.cc":"dc106c5940d87bb4bbef3d77815eab642ee173a3340b2b9c532b5c711c4c2d0e","libcubeb/googletest/samples/sample1.h":"7a7bf9a0fbd2401e8b2cb554bfcb9bd0ed228212f3b970675c1b1d38d4e188bb","libcubeb/googletest/samples/sample10_unittest.cc":"ccebb6393a5a8468399f7e511219b667a2233f82312ce59834a4bb0997d8700e","libcubeb/googletest/samples/sample1_unittest.cc":"904be0d4a095e74393515195392bd10e1e916bb2ca61f3f94b1bd6aebea29cb6","libcubeb/googletest/samples/sample2.cc":"f14b8a1e69d52eef1a70053fb256818c7eca64e8eda08de43cf46e896e57fcc2","libcubeb/googletest/samples/sample2.h":"df956ba520dafca068dbc1e28f36567db3cba36293e06762318af8cda6a12bd4","libcubeb/googletest/samples/sample2_unittest.cc":"abe7e0b253d328cb82ae67623fbe3c89eb94699102510c64a0b568eaca101e05","libcubeb/googletest/samples/sample3-inl.h":"3fe482bbd4f725f5820f5d6beab4d0d4a524be8addf4b344a9a470ec5aabc451","libcubeb/googletest/samples/sample3_unittest.cc":"252c06b4531dc35213ebdd7311700b9b4057bc1bdeeba0cd767b2bc86c456639","libcubeb/googletest/samples/sample4.cc":"b4260f5fa35d78ac114a9abb59fce12403faf0273df41f57e83c341ae7979222","libcubeb/googletest/samples/sample4.h":"604905cae7e5587805c3b884a36eda7a2bebdfedb53b24b0fd9a220eec0ef1a9","libcubeb/googletest/samples/sample4_unittest.cc":"6cfb4821d8cb1c77fbb5af4f8aec569948762d8ea314827e3ead967b5b6a223e","libcubeb/googletest/samples/sample5_unittest.cc":"73646d9038873a68bb2e56b12495d7f7b65b5c23901109701da446af454ba2ec","libcubeb/googletest/samples/sample6_unittest.cc":"833fee399954f908cf0f3b789832e505329787f4cf73607a7b31ca0f62f368d7","libcubeb/googletest/samples/sample7_unittest.cc":"8013ee68d61c181e4e936cdae3a9a635646274f8512033ef11bff7214e03e4a6","libcubeb/googletest/samples/sample8_unittest.cc":"7b7510fadf4955d2f934d23d652dbd35add832e50bdfcc98421fb9be4588d808","libcubeb/googletest/samples/sample9_unittest.cc":"8b827040dea37b460cbcaea0b255b98974a9840f6ef7bd82aaa7d4ad2c724335","libcubeb/googletest/scripts/fuse_gtest_files.py":"adecf64c6bab65b31740c321e568cf174f753d5617745aa5762d842339d68b53","libcubeb/googletest/scripts/gen_gtest_pred_impl.py":"78fb7e20a014c251d723186eb58040e4eb32405b73c9288d787ea0a0e4ff5183","libcubeb/googletest/scripts/gtest-config.in":"9a43978eeee88e188845d737c17f4d024d4e74feae09da7997e8fbe4ea6cc176","libcubeb/googletest/scripts/pump.py":"3856a3d7be37f78e0f214ee7d4f29d05f1ca14218b67539d67c9a16e992f670c","libcubeb/googletest/scripts/test/Makefile":"3576b257594a2d8e843b9e4de8c83353d837833bb86431fb1b4198022b1bcddc","libcubeb/googletest/scripts/upload.py":"f75d0712e3b13bebd8daa0a15e4eb32c9e3034a933f4fcccf65b1e999a7ae066","libcubeb/googletest/scripts/upload_gtest.py":"6e76fc0a7a3831c01cfffd18c220d44438073a66338d91ca61fc84b924021e61","libcubeb/googletest/src/gtest-all.cc":"568ac119f5e6418f1fbcfbdf185d724657d7f3539b47822da229ac5d015626b2","libcubeb/googletest/src/gtest-death-test.cc":"eec1b3c8252670c76acbbaf63483946897ce625139b53a566406b6313f023896","libcubeb/googletest/src/gtest-filepath.cc":"31b7fcda5d11346f8a487597c6a70ff057f1192e0cb11f27eb7841a9f3aa8b86","libcubeb/googletest/src/gtest-internal-inl.h":"c9d428a6b5990ace091e40c4ce8b7bf6c50c186a8314b1c4a4cdc988ca0ac1a4","libcubeb/googletest/src/gtest-port.cc":"95bcf473622d1b901c734e5c2aeb8efb058555ec924212a61bb04f049bb5a069","libcubeb/googletest/src/gtest-printers.cc":"6f191a7fc7f5a0a967fd11964057f2e2d2eaf2f37ccece16bd816531f52b3154","libcubeb/googletest/src/gtest-test-part.cc":"e489868b4cdc66f4fc33bc2326ac86bc1acc5808ab58bbb288c9dcfc330faddc","libcubeb/googletest/src/gtest-typed-test.cc":"ca9e819df728c25a6a1fc072806c22f3494e1dffe4bd0d48284f38dbdd3a0dd5","libcubeb/googletest/src/gtest.cc":"5cf9a3e897892c9f0e5c887f91d3c8c8c5665bd7348560441fc0b946c254873c","libcubeb/googletest/src/gtest_main.cc":"22fa1f77542b882d1798d7f696045c5895942a626e26200a175fa4382e1fa5b5","libcubeb/googletest/test/gtest-death-test_ex_test.cc":"613ccf50a3ff8f84c975a13e86ea01ea4e36933072388a3738b4acf9ed3ed7cf","libcubeb/googletest/test/gtest-death-test_test.cc":"df8384a847bdf889233c3d45d171f784991def7a9b6a08442138569fbae32b9d","libcubeb/googletest/test/gtest-filepath_test.cc":"49760f91723845b113bb60bb9b1a1426ed1da1f4ebfef2462128980ea5692cc9","libcubeb/googletest/test/gtest-linked_ptr_test.cc":"1b9cb4ff67475900db9de34ae9749b94193048a1f7a741091ba5a2dd7fc7a79b","libcubeb/googletest/test/gtest-listener_test.cc":"acf78f2c9a730525ea5adc93e9196a42de8fbfe488db1dfd02656bdbd477b2c0","libcubeb/googletest/test/gtest-message_test.cc":"b1fc68f8b75ce25fbd79b3f7d3c9c793381ef07b3203e1a2d9b610cb597542be","libcubeb/googletest/test/gtest-options_test.cc":"74e3ae0c310edb3139b0032266219d3ce7f386ded6feafa57fef03f4493ed7fa","libcubeb/googletest/test/gtest-param-test2_test.cc":"a0f1efbcab3f7e49df639383157626931f64756f7e738be081760f93f7308332","libcubeb/googletest/test/gtest-param-test_test.cc":"ef8bd344e959053f562b0c9e0d15e2fb6c1e534772a67aaf3f90bd6bad0bf99f","libcubeb/googletest/test/gtest-param-test_test.h":"9d7f47b79d54df7cc050aa6038b0464aa684dfca669a847bf70ea16e4a000628","libcubeb/googletest/test/gtest-port_test.cc":"1600f78ef0860a0f5b5525e5e5041ff32a216cc6ae948b1ea61fe04ec603f67d","libcubeb/googletest/test/gtest-printers_test.cc":"7898e4b4163ee0821fed248e1c75d9f4a0a511a2b4bbfad1ef2f4a11a099f6e7","libcubeb/googletest/test/gtest-test-part_test.cc":"62c8906bb0d12ea84d60217b3773cd8e1768db4aab934880db2316df7026cab8","libcubeb/googletest/test/gtest-tuple_test.cc":"2850dc1f73a3f8020d8a4d80688a28d9b736eae6d677222c3f871d8d33b25501","libcubeb/googletest/test/gtest-typed-test2_test.cc":"c52b65e7181610d6e577631cd50177399884913ff28d08aedfedc92f05185044","libcubeb/googletest/test/gtest-typed-test_test.cc":"c7daff5211028da79b3ca0473dca18ada9197f38e710f72d0493ad3332ce3ec9","libcubeb/googletest/test/gtest-typed-test_test.h":"3145698534d8869beb624c9c8ed114f75bead046b2eeb92ada5a724993ee7786","libcubeb/googletest/test/gtest-unittest-api_test.cc":"e3f54c28ef2849e8b12af666ed46aace50c3e047845072ee6f974ce4528bd297","libcubeb/googletest/test/gtest_all_test.cc":"db0c3c42b385570b1d517e3ee927671b8fad4e206247fca738ec477222ac3d97","libcubeb/googletest/test/gtest_break_on_failure_unittest.py":"11c91bc1c68cfdb913e2affb01261b55fb3b0c18773a45875e9c25cb330a4dcd","libcubeb/googletest/test/gtest_break_on_failure_unittest_.cc":"1da12e4bdda2a0bc7b59d4638fe34b2d3798134224fd9237eeebdd09c3326011","libcubeb/googletest/test/gtest_catch_exceptions_test.py":"305cef45c6dc034bdf72fd91aba1e89e1c6b5d222c3d6baffff5acdfd9b3873e","libcubeb/googletest/test/gtest_catch_exceptions_test_.cc":"b297a4f4d5bc0285ea9eb8869741631658305e49d4513bca904842aacb82128b","libcubeb/googletest/test/gtest_color_test.py":"c4cb006682a40f2d88759a4bcabf0d4be623720b135c71447f1788d17ea23d0f","libcubeb/googletest/test/gtest_color_test_.cc":"f263ba349afe58a558bf0fee98a98bb9207a648e7cd4f908a87799bd13d001ea","libcubeb/googletest/test/gtest_env_var_test.py":"79819598cd1e366eaa8f2a4fee2d638b6ef0686e490402fae792ccce58d876c0","libcubeb/googletest/test/gtest_env_var_test_.cc":"0eee5dfbb2a2598f4e76626346b921928ec1e052e38f254cc97c60d05611ab46","libcubeb/googletest/test/gtest_environment_test.cc":"a52a21ea29c2203b03fa93922733546d171f98d3b2fcd42972269e98fd124715","libcubeb/googletest/test/gtest_filter_unittest.py":"edc7d278803bba41626eacd050d91d7247f1c5999f9dceb99a8877e238bc73d6","libcubeb/googletest/test/gtest_filter_unittest_.cc":"996ac528ad75c293d8201ce28cf6acccee266286bd369b4cf43f05b8d67a4559","libcubeb/googletest/test/gtest_help_test.py":"b43ab690c08e4bffd84a47b361167496298697f9511bdf4a745bf305b5cfbdfc","libcubeb/googletest/test/gtest_help_test_.cc":"ff4b121098f0fe7cb4abf11fdd31f2fe7a477286ec9175482138bc038d61c807","libcubeb/googletest/test/gtest_list_tests_unittest.py":"7caebc175b44b3c727fc50420ada1a6a9500f3e4ce9e2839f69205437aa85e7a","libcubeb/googletest/test/gtest_list_tests_unittest_.cc":"d82d8b72914897232c2ff9fd091a7b0add68b7cf75f3f210d3a487ebeea84cfe","libcubeb/googletest/test/gtest_main_unittest.cc":"0f66f318809c88f0fbe034a340a75331720c4e33be5378022baffaf588ef1202","libcubeb/googletest/test/gtest_no_test_unittest.cc":"7cf487e07c3d27376c2cb8af33d02239b7966623875d37b7aa0259e927a9c2f6","libcubeb/googletest/test/gtest_output_test.py":"cf0dc1979572d94450a5e611b44f3fdb88d9cd980d669a723f0ed63057b5e2c4","libcubeb/googletest/test/gtest_output_test_.cc":"f69569374c2b3d06aa04a38ebc4f92ddc303e6af503f8b533cd8e6bf9f104899","libcubeb/googletest/test/gtest_output_test_golden_lin.txt":"4f3e49c10a524a99437cdcb5294e3335a7f3c07ea8462e65730f703a5fe4fec3","libcubeb/googletest/test/gtest_pred_impl_unittest.cc":"e406eccf75b6b58746a95d1c7ea7bc8e80ff974e438ef7c83074a46d4e62db9a","libcubeb/googletest/test/gtest_prod_test.cc":"b42ca1a6d0a1e43bc576b4ff7776c6d2c37234f6dc2a76f2735f261b4a47a526","libcubeb/googletest/test/gtest_repeat_test.cc":"e10abbb71595920aa3bb415029eed74106335fc9ea3d58c417ccfc7cba6a4cdb","libcubeb/googletest/test/gtest_shuffle_test.py":"12dd94eb5f30260ba37059fa74658bda57dffa821f3ba6a2a8b52ff14b1ad029","libcubeb/googletest/test/gtest_shuffle_test_.cc":"af1b2b01ae275f1a9fee7e7940c0f88f39ded063008994d585aad87e3ffadb39","libcubeb/googletest/test/gtest_sole_header_test.cc":"538414c27a593ab8dc34c37b5c877eb3a022d75d1b481ef14ceca00914929754","libcubeb/googletest/test/gtest_stress_test.cc":"0b02fc12f87944226915a999bdcc8a3eaafb34a1ea5bb0df128774abf8667f09","libcubeb/googletest/test/gtest_test_utils.py":"d4a76930aee6658ad8734981ca0c4ea14f34dbe8fdd31d5afe41f6d98f9779ee","libcubeb/googletest/test/gtest_throw_on_failure_ex_test.cc":"11ae790028da20bc7b7af1572eff3cfe8499be43ab64c110e18e1892612a183f","libcubeb/googletest/test/gtest_throw_on_failure_test.py":"ebe18ca0b07f90c53b9b3f9a54ed02df94facf8995cfa90dd41c6f5474537c13","libcubeb/googletest/test/gtest_throw_on_failure_test_.cc":"f8cbf75d8bf9e9ae068a17ff968434c3aec7b7f1137c994d8f14af1a84361aa9","libcubeb/googletest/test/gtest_uninitialized_test.py":"da8e6ce34930753e36fc1dfa2c3e20e48d02bda2a27d3d03a07364312c5f3bd9","libcubeb/googletest/test/gtest_uninitialized_test_.cc":"0b6a9d4983480f87352ca4da946089264b401f7a4a3b1282253fd6cc861bf483","libcubeb/googletest/test/gtest_unittest.cc":"c0c7d1f691ce1e10c3d1647ed5f7a66510725808ad58bb6da4bc03a7a08fb2fc","libcubeb/googletest/test/gtest_xml_outfile1_test_.cc":"29341d777a9c9d25f360d13ed966b30f0cbef1fc88aefe2f01bb88b82cf1ed93","libcubeb/googletest/test/gtest_xml_outfile2_test_.cc":"da7ab3cf0e9b2a293eceed7c5691233d6b61afb557e3c1176dfb75390f85be46","libcubeb/googletest/test/gtest_xml_outfiles_test.py":"b07927b43f44afbfd61761c2cc69f1b68c4fbdeddb992db03ff0c73052518cd4","libcubeb/googletest/test/gtest_xml_output_unittest.py":"b5ff0c0207238d01cada961b8f4656f2ec30a3e1e5bf9d22efdf1745af423031","libcubeb/googletest/test/gtest_xml_output_unittest_.cc":"ad0b9ebe63a146e386df3c5c51916869f6d4647b9832ceacc912fb1272d15f82","libcubeb/googletest/test/gtest_xml_test_utils.py":"ad89a39a6cd5b08e87082341f3e7800dbf1150ea0f1386e0b8cd374aa6832f00","libcubeb/googletest/test/production.cc":"56fef77c3a8e62073ec11653d740a8e534008a0d57925ab0877b843f4fdd6be8","libcubeb/googletest/test/production.h":"a36d10545d12ead5e93a3b0fdca6ff73405f29091cfe38164415e9038888ba8d","libcubeb/googletest/xcode/Config/DebugProject.xcconfig":"fb42302df29bd8e8b5237194c0c04941f0e578527037930d88469baeb7a7f62b","libcubeb/googletest/xcode/Config/FrameworkTarget.xcconfig":"9935ddabe221f074d2f3b969a137d12b0dc0f845a460f58b63232987cb0f37ff","libcubeb/googletest/xcode/Config/General.xcconfig":"0fb768924daba1048f8db28b3a1fbf915b6f788d49d9d37e85979aa4ee92e02d","libcubeb/googletest/xcode/Config/ReleaseProject.xcconfig":"a4878ddd1ed78fb411906623cb51bc0ab4aea1cc3feb5379d2ae2862d8bf3bf5","libcubeb/googletest/xcode/Config/StaticLibraryTarget.xcconfig":"5886291788b3e9d5aadcb979ff055fd26a2413be81016e7afffb813b627d177c","libcubeb/googletest/xcode/Config/TestTarget.xcconfig":"f074e6c2516a6063b253ec6b842d74f5c2abefc7bcf8d8da54097a7bfe016480","libcubeb/googletest/xcode/Resources/Info.plist":"5b7f78a6d5810ce5490649793175c8982d41e6b49af06bc0705bc061567cc9aa","libcubeb/googletest/xcode/Samples/FrameworkSample/Info.plist":"1c13d83e5eed45689d7fe4bf4681030366474bc40608e39e1623c1350513a0cd","libcubeb/googletest/xcode/Samples/FrameworkSample/WidgetFramework.xcodeproj/project.pbxproj":"1cf0e1b1abf84414372faf9c8bf634e01fe5750bb3ca769b1eb25fc530b21358","libcubeb/googletest/xcode/Samples/FrameworkSample/runtests.sh":"a587e5b00a8353dee0aca5a4c59b28301ccf7648dee8c79b62a9223f9fc3c8cf","libcubeb/googletest/xcode/Samples/FrameworkSample/widget.cc":"562a2bb615e93186012823c9b41761769638a11e38b54498ad5f699038c8fd32","libcubeb/googletest/xcode/Samples/FrameworkSample/widget.h":"0c7915e45cf7cb8d67db24e49cd0b277f23f967578f917f8e859a6adc4b156f9","libcubeb/googletest/xcode/Samples/FrameworkSample/widget_test.cc":"6a1a49d64912d9829ef3d991faf5a3f0e1e081126a1d8d387cdfa84fab70dc77","libcubeb/googletest/xcode/Scripts/runtests.sh":"1a0672a4151b16f3c797478ba26c534e82b2faa603f90b9aa14e785805f7683a","libcubeb/googletest/xcode/Scripts/versiongenerate.py":"4b9d5c0f4e1b556084109311d156bee6d781968dc5b1dfdc8702364508f1dd43","libcubeb/googletest/xcode/gtest.xcodeproj/project.pbxproj":"a1224decff058bfed01b8eefaee13cab0129492615548c6d0d878003a154f7ff","libcubeb/include/cubeb/cubeb.h":"578057761519e21c96587c6d80d16dd713c6dc723357f8e10c3de25088e5fc83","libcubeb/scan-build-install.sh":"1ecf22aca367a4d02f810c4cb78db8636e08a12787e94e30c441ce439cf4a265","libcubeb/src/android/audiotrack_definitions.h":"0d5ec772f9ebf61333bc16e61a081a7e3b4cc02342ec4f8f417e220d032fbbc6","libcubeb/src/android/cubeb-output-latency.h":"017f1d92a70d3dda3a9adb2cb666660d083598f631149022724a5ad5450394d9","libcubeb/src/android/cubeb_media_library.h":"f67965fb2ea38f0023eb4c76024341ba9563ac1a135507b6f12a9aabd85e30a9","libcubeb/src/android/sles_definitions.h":"66d10b157e463ee106236736b0338d1ec512d66d577ecafda56427bf14658443","libcubeb/src/cubeb-internal.h":"4e66ae5817643db363feb8415e9afc3fe35ec1a3a1df50b656efde62092d30d7","libcubeb/src/cubeb-jni-instances.h":"8195554372bf60dea569873c9e5fb1106a2cf5dedc66a13d2bc967da0ff48a12","libcubeb/src/cubeb-jni.cpp":"81f001720c41c69b5927e32bd19b9e8e7176d7c33d63c2a58bd0d695dace4fd2","libcubeb/src/cubeb-jni.h":"73f810a32087a6062fd49ba89542655a7e19cecac6f40f8411e1d77ce42a45d1","libcubeb/src/cubeb-sles.h":"dc84a01ba4de9db1ff108609930b36ba442020ccc3e3d6f16528f429426c430c","libcubeb/src/cubeb-speex-resampler.h":"dbf543eee4cc6e40ba3531a39d327e2cec884c19e26c570aa7eae0647e5f7420","libcubeb/src/cubeb.c":"7deee12d406054849030b0aab5732ce3226347ee6e93d24fa0781a8edca77b2f","libcubeb/src/cubeb_alsa.c":"fd6e92c9e4f69a4f00b28ffdf1477995440ed0a61d2ed4de43dcd07793ff8c3a","libcubeb/src/cubeb_array_queue.h":"5264ae02799c540ff73e8eb8efa55986772b22562a025ae227c2603b6b8d1036","libcubeb/src/cubeb_assert.h":"ab8ed4fe7070a3aed9419c5f9695fce2318c4dafd91213ae61cac476e64adaa7","libcubeb/src/cubeb_audiotrack.c":"ecaf326b9a073c48fa6e1d30cba67e31fcea21f3bc952389a92ca94b99cac619","libcubeb/src/cubeb_audiounit.cpp":"51e3fb264afd6b58d51d8b08e5f3aecef57ac9a0d55320b150280d3110b0e6a2","libcubeb/src/cubeb_jack.cpp":"e94686de234b96d33382157f593732206ff1aa1e4ee63db04d8fbbfa74ebe641","libcubeb/src/cubeb_kai.c":"09819383df514c8353517836e1f93692e934617eedc4bd2a7a4c4d3e21ad4f09","libcubeb/src/cubeb_log.cpp":"74bdc0d92a31d6b4ce3c376c6d310db583b869d673e40e6bd0ea795a1e2f937a","libcubeb/src/cubeb_log.h":"9b0b5d02092882226394611a29a1dae2c78adbac61e0fe95fafb7b25724f32ca","libcubeb/src/cubeb_mixer.cpp":"061427a06516792ead3936a643224c3d275913410f67765cc24e1b8699e8d99d","libcubeb/src/cubeb_mixer.h":"e94205c1b98859e41eeab2cad53f635f34f57a2ae95aa23a36461a2619abf81b","libcubeb/src/cubeb_opensl.c":"40079c87c85b6cd3a7808edb62fd8ff5502aef6f08179dc11abf2523d9a26d7e","libcubeb/src/cubeb_osx_run_loop.cpp":"13c029f7ea04808597b8841b13f248c4476fb7425762f01a59bc6c944cab3a78","libcubeb/src/cubeb_osx_run_loop.h":"ffe80df60b8ea3d0341715b72cc60aae2e0df45141887c01117df543260a0ef8","libcubeb/src/cubeb_pulse.c":"ef7c87e4eddb24db6dc0ee8809f4d1246c44a5e43d1357304b199e43cfe51a9c","libcubeb/src/cubeb_resampler.cpp":"9a2e5ead61ab88a9c038e2fcce624471982cb7b507b0a1ff44c0192ad73bddc5","libcubeb/src/cubeb_resampler.h":"ad9800a40c0272fb2a451c02367cc5a516a3067d4acf963d35eb31be367f9224","libcubeb/src/cubeb_resampler_internal.h":"4b4eb6aee343b9c1f73cf17b201329d3714ba44b06ecb1cebf950fdf93a9dfe6","libcubeb/src/cubeb_ring_array.h":"db8dec7a6d778a424045e5ac61c2bc3a3cec8c4fe8e4780f383db4b6f1b2438c","libcubeb/src/cubeb_ringbuffer.h":"2296255ca7835a3ace2fe1852054f43428b677f432b4174bd832ad0224a409eb","libcubeb/src/cubeb_sndio.c":"7a878e9e8428debcfabe3652a0161592e74e6b06a73fc71f7792f365280ae125","libcubeb/src/cubeb_strings.c":"60f2b8c03a5a01925f9777eec0ab84b0e704b40c03e0d6b9c0e7a16246c81fde","libcubeb/src/cubeb_strings.h":"00e5dc97cf16e28cfb53aaae50ac0e3c0ae8a82aad0720ab6842ce5d9785c39f","libcubeb/src/cubeb_sun.c":"b9b64b8581cf40b81a1b3b4665d69476996d1220381b0745378d034373886abd","libcubeb/src/cubeb_utils.cpp":"1846d43050966da43ed533e9fd924460206fbda3919f72f9e6be5668fe9a0eb9","libcubeb/src/cubeb_utils.h":"27baa42747771bf9232741382b83722f5c731e5dcd4dc2e9b595aca91c5647a5","libcubeb/src/cubeb_utils_unix.h":"2a48bd2aefa1b4e4e4968121512bcaaa6be3bca6597ea390b8203c338f5c49b5","libcubeb/src/cubeb_utils_win.h":"74658b6266a8a223c681b0fd06fcc9c891c7b0b95377635c6f1763b240c2ab27","libcubeb/src/cubeb_wasapi.cpp":"ee462f35903166c235e08291595d3c2b5a738275322f7eb1e04a9719dd6eb201","libcubeb/src/cubeb_winmm.c":"7acb05d43fd0f7b928e171e0d7f64b3f171f40ba20b4855176dee786e0f2b7d0","libcubeb/src/speex/arch.h":"2300bce68c588270cdc684dc7f01377e5e251529f4545d93771e111c56d51b0f","libcubeb/src/speex/fixed_generic.h":"306ee7453677fa6067f16c79d358c6c90a9d3d008850b493cdaa59c07e6375c1","libcubeb/src/speex/resample.c":"9c3a1c64ecf3750af82c980d01ea73d3682f73c332a580465d1e787e5c54cd00","libcubeb/src/speex/resample_neon.h":"7d3fd7af9a1ddde22518b9c7b4419073b72b2dfa5be4c3bc8796992bc87b3da0","libcubeb/src/speex/resample_sse.h":"5a196d8e2d8ab5c956f5252f09f5ddc55aee1f99b1341af3fa54a1f4c2157924","libcubeb/src/speex/speex_config_types.h":"24e3ffbf29e5519611a48e5acb959645b01d166dcb4369380d5f776c3f53d4cd","libcubeb/src/speex/speex_resampler.h":"7e439ec0dd30c32216b3ced17135f8992e5aaf53389d3f5996a7d900c453e65f","libcubeb/src/speex/stack_alloc.h":"e8a2fc0874942d2c7177475fcc141fdd0c0156200b8a4e7656d4a20313e2e569","libcubeb/test/README.md":"1c11b038f87daf10ea78abc17bdbdd157940c241df548c24d5872d142a98c1af","libcubeb/test/common.h":"968144634f3ce627d7f6cb91d48ddc6f1fa89f481ae63f80a7cbe743989e449c","libcubeb/test/test_audio.cpp":"4dfa5e686293101541a300b04712f0cd5a32904fc1f1a71f73ca872d56050e57","libcubeb/test/test_callback_ret.cpp":"f2d190ec14b2da927bb4cd759f775cd3b7f38df3cd2984862b346f7c37eb9ca8","libcubeb/test/test_deadlock.cpp":"0e28e59f360e27241536879d9e02e4e05291db252f66fd601577aaf723e06543","libcubeb/test/test_device_changed_callback.cpp":"d804d1da5611ef5b12abb68b17840cb19a1fa4207678dbb2fc9bdd834aed1fc2","libcubeb/test/test_devices.cpp":"0fbffbd4097898220b5e60f3bee5008dbf005f77bce33c6aa4934a5562b8d092","libcubeb/test/test_duplex.cpp":"fa9944a3d7975b21ad0b61690f2ab40c4ed16a4c3f2758d7b6ad0df60cbb0768","libcubeb/test/test_latency.cpp":"dc5af071df0ff0c4b2f6cf7b37438ac8ed9f18cc0fafffb2c685b1e82201c9f5","libcubeb/test/test_loopback.cpp":"00f1250c2012a8bd458a38dfbd80f7e93b764494595c6cd5f3bc2ea518cf1ad0","libcubeb/test/test_overload_callback.cpp":"f3ffa014a0e6f7871a023b46733f93d992ddc9b41e0974cc22a365979b6d989e","libcubeb/test/test_record.cpp":"89a884872b780005b2fb29d106b6f33ddd76b2fcae381395e17d3413153b3b89","libcubeb/test/test_resampler.cpp":"765481618db48c013e02312179a1e32526994eacc0051c2075433b3181fc31e0","libcubeb/test/test_ring_array.cpp":"27836d716616abf8b020a710659c10ac5e1d4b64149d22cb89bd7a7a5a5669d1","libcubeb/test/test_ring_buffer.cpp":"0ac28f2de88fb4f052b8c212be680002f447a87d72695bb1fdca508145aade7f","libcubeb/test/test_sanity.cpp":"19e2b22e50fef3120859bfe9e9c5fcf5720b47e694eb02c9cd388c3345706ea2","libcubeb/test/test_tone.cpp":"02de19023d203e2d538c4f8ae23574153a4df6ada1d06fb83af2d51603b61669","libcubeb/test/test_utils.cpp":"e8d7a02a9096dbc0fd15d71f9849415d52958d2ed82cbc7b8507d3228e08d382","libcubeb/tools/cubeb-test.cpp":"36387bcc015eb406a32869d05fb50c4d5fe0c80d2d4525e80c42387573d4276e","src/callbacks.rs":"b4b4eb3f370475488d7c77b338b0bceee94ef59ede8ff41fa797a3d35ea98b5d","src/channel.rs":"555bc8d5257496cf6e5b4c786df84755327d88a9dc07202746e35f3b7b7e4e0b","src/context.rs":"96bf3442b1dd40262d3088dbb6c473f5451fd91320cef93c522b40f284599803","src/device.rs":"dd61883843c177a6387527a51698061316b47272c2e11156dbb74c07dda1b5c0","src/error.rs":"406e3b843ed2d263fe677c4b34fb96a6d780a68bcb56a6f85f041d20d70227bd","src/format.rs":"d4d27790c20eab0b16592f60d5e487425a45a268cf4c74cf843c10ac91bbff4c","src/internal.rs":"c3af5f53dc7957860bf3bc0cd9737d094fb8ac000e7b40c569304cfa76a43145","src/lib.rs":"cdc36b11f18e274d10df592d216f142e05e1d22db5aac4c7e346893d6b1510db","src/log.rs":"6694178177775f2ce5449435d00609e9361c836e23119c07d04892000587dc55","src/macros.rs":"caef13f5d23f7a3ec1a54ec3ca2390ac4ad89d521893f1d0864daf70d57a20aa","src/mixer.rs":"e72e92855614da187da6419ed0a115062a05670cb73443b2ea4313d8f5108a68","src/resampler.rs":"762070f8afde2256715b8764380cdfa1204a1a39d98a9da0b2efe88699792f2c","src/stream.rs":"65e3d2f26be37d742506c3d71710fe106e5d532cbbdfea896a002b95789ab8aa"},"package":"dcbc562eb6ccf62abacf9e3eebce992e5c36b230ca313ebd7c2d7d0e99deae90"}
\ No newline at end of file
--- a/third_party/rust/cubeb-sys/Cargo.toml
+++ b/third_party/rust/cubeb-sys/Cargo.toml
@@ -7,17 +7,17 @@
 #
 # If you believe there's an error in this file please file an
 # issue against the rust-lang/cargo repository. If you're
 # editing this file be aware that the upstream Cargo.toml
 # will likely look very different (and much more reasonable)
 
 [package]
 name = "cubeb-sys"
-version = "0.6.2"
+version = "0.7.0"
 authors = ["Dan Glastonbury <dglastonbury@mozilla.com>"]
 build = "build.rs"
 links = "cubeb"
 description = "Native bindings to the cubeb library"
 license = "ISC"
 repository = "https://github.com/djg/cubeb-rs"
 [build-dependencies.cmake]
 version = "0.1.2"
--- a/third_party/rust/cubeb-sys/build.rs
+++ b/third_party/rust/cubeb-sys/build.rs
@@ -35,43 +35,47 @@ fn main() {
             .args(&["submodule", "update", "--init", "--recursive"])
             .status();
     }
 
     let target = env::var("TARGET").unwrap();
     //    let host = env::var("HOST").unwrap();
     let windows = target.contains("windows");
     let darwin = target.contains("darwin");
+    let freebsd = target.contains("freebsd");
     let mut cfg = cmake::Config::new("libcubeb");
 
     let _ = fs::remove_dir_all(env::var("OUT_DIR").unwrap());
     t!(fs::create_dir_all(env::var("OUT_DIR").unwrap()));
 
     env::remove_var("DESTDIR");
     let dst = cfg.define("BUILD_SHARED_LIBS", "OFF")
         .define("BUILD_TESTS", "OFF")
+        .define("BUILD_TOOLS", "OFF")
         .build();
 
+    println!("cargo:rustc-link-lib=static=cubeb");
     if windows {
-        println!("cargo:rustc-link-lib=static=cubeb");
         println!("cargo:rustc-link-lib=dylib=avrt");
         println!("cargo:rustc-link-lib=dylib=ole32");
         println!("cargo:rustc-link-lib=dylib=user32");
         println!("cargo:rustc-link-lib=dylib=winmm");
         println!("cargo:rustc-link-search=native={}/lib", dst.display());
     } else if darwin {
-        println!("cargo:rustc-link-lib=static=cubeb");
         println!("cargo:rustc-link-lib=framework=AudioUnit");
         println!("cargo:rustc-link-lib=framework=CoreAudio");
         println!("cargo:rustc-link-lib=framework=CoreServices");
         println!("cargo:rustc-link-lib=dylib=c++");
         println!("cargo:rustc-link-search=native={}/lib", dst.display());
     } else {
-        println!("cargo:rustc-link-lib=static=cubeb");
-        println!("cargo:rustc-link-lib=dylib=stdc++");
+        if freebsd {
+            println!("cargo:rustc-link-lib=dylib=c++");
+        } else {
+            println!("cargo:rustc-link-lib=dylib=stdc++");
+        }
         println!("cargo:rustc-link-search=native={}/lib", dst.display());
         println!("cargo:rustc-link-search=native={}/lib64", dst.display());
 
         // Ignore the result of find_library. We don't care if the
         // libraries are missing.
         let _ = pkg_config::find_library("alsa");
         let _ = pkg_config::find_library("libpulse");
         let _ = pkg_config::find_library("jack");
--- a/third_party/rust/cubeb-sys/libcubeb/CMakeLists.txt
+++ b/third_party/rust/cubeb-sys/libcubeb/CMakeLists.txt
@@ -150,42 +150,33 @@ if(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 pulse pthread)
-  if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
-    target_link_libraries(cubeb PRIVATE dl)
-  endif()
+  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 asound pthread)
-  if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
-    target_link_libraries(cubeb PRIVATE dl)
-  endif()
+  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 jack pthread)
-  if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
-    target_link_libraries(cubeb PRIVATE dl)
-  endif()
+  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)
@@ -216,17 +207,17 @@ if(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 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)
@@ -243,16 +234,17 @@ 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
+    BUILD_ALWAYS ON
     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)
@@ -261,16 +253,17 @@ 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
+    BUILD_ALWAYS ON
     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")
--- a/third_party/rust/cubeb-sys/libcubeb/README.md
+++ b/third_party/rust/cubeb-sys/libcubeb/README.md
@@ -1,6 +1,8 @@
 [![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.
 
+See [Backend Support](https://github.com/kinetiknz/cubeb/wiki/Backend-Support) in the wiki for the support level of each backend.
+
 Licensed under an ISC-style license.  See LICENSE for details.
deleted file mode 100644
--- a/third_party/rust/cubeb-sys/libcubeb/TODO
+++ /dev/null
@@ -1,41 +0,0 @@
-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
--- a/third_party/rust/cubeb-sys/libcubeb/include/cubeb/cubeb.h
+++ b/third_party/rust/cubeb-sys/libcubeb/include/cubeb/cubeb.h
@@ -26,41 +26,41 @@ extern "C" {
     @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");
+    cubeb_init(&app_ctx, "Example Application", NULL);
     int rv;
     uint32_t rate;
     uint32_t latency_frames;
     uint64_t ts;
 
-    rv = cubeb_get_min_latency(app_ctx, &output_params, &latency_frames);
-    if (rv != CUBEB_OK) {
-      fprintf(stderr, "Could not get minimum latency");
-      return rv;
-    }
-
-    rv = cubeb_get_preferred_sample_rate(app_ctx, output_params, &rate);
+    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;
@@ -92,24 +92,24 @@ extern "C" {
     }
 
     cubeb_stream_destroy(stm);
     cubeb_destroy(app_ctx);
     @endcode
 
     @code
     long data_cb(cubeb_stream * stm, void * user,
-                 void * input_buffer, void * output_buffer, long nframes)
+                 const void * input_buffer, void * output_buffer, long nframes)
     {
-      float * in  = input_buffer;
+      const float * in  = input_buffer;
       float * out = output_buffer;
 
-      for (i = 0; i < nframes; ++i) {
-        for (c = 0; c < 2; ++c) {
-          out[i][c] = in[i];
+      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)
@@ -554,16 +554,26 @@ CUBEB_EXPORT int cubeb_stream_get_positi
     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);
 
+/** Get the input latency for this stream, in frames. This is the number of
+    frames between the time the audio input devices records the data, and they
+    are available in the data callback.
+    This returns CUBEB_ERROR when the stream is output-only.
+    @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_input_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);
--- a/third_party/rust/cubeb-sys/libcubeb/src/android/cubeb-output-latency.h
+++ b/third_party/rust/cubeb-sys/libcubeb/src/android/cubeb-output-latency.h
@@ -30,17 +30,17 @@ cubeb_output_latency_load_method(int ver
 
   ol->from_lib = cubeb_load_media_library();
   return ol;
 }
 
 bool
 cubeb_output_latency_method_is_loaded(output_latency_function * ol)
 {
-  assert(ol && (ol->from_jni || ol->from_lib));
+  assert(ol);
   if (ol->version > ANDROID_JELLY_BEAN_MR1_4_2){
     return !!ol->from_jni;
   }
 
   return !!ol->from_lib;
 }
 
 void
--- a/third_party/rust/cubeb-sys/libcubeb/src/cubeb-internal.h
+++ b/third_party/rust/cubeb-sys/libcubeb/src/cubeb-internal.h
@@ -58,16 +58,17 @@ struct cubeb_ops {
                       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_get_input_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,
--- a/third_party/rust/cubeb-sys/libcubeb/src/cubeb.c
+++ b/third_party/rust/cubeb-sys/libcubeb/src/cubeb.c
@@ -75,17 +75,17 @@ validate_stream_params(cubeb_stream_para
   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) {
+        input_stream_params->channels < 1 || input_stream_params->channels > UINT8_MAX) {
       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) {
@@ -189,34 +189,34 @@ cubeb_init(cubeb ** context, char const 
     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_SNDIO)
-    sndio_init,
-#endif
 #if defined(USE_SUN)
     sun_init,
 #endif
 #if defined(USE_OPENSL)
     opensl_init,
 #endif
 #if defined(USE_AUDIOTRACK)
     audiotrack_init,
@@ -416,16 +416,30 @@ cubeb_stream_get_latency(cubeb_stream * 
   if (!stream->context->ops->stream_get_latency) {
     return CUBEB_ERROR_NOT_SUPPORTED;
   }
 
   return stream->context->ops->stream_get_latency(stream, latency);
 }
 
 int
+cubeb_stream_get_input_latency(cubeb_stream * stream, uint32_t * latency)
+{
+  if (!stream || !latency) {
+    return CUBEB_ERROR_INVALID_PARAMETER;
+  }
+
+  if (!stream->context->ops->stream_get_input_latency) {
+    return CUBEB_ERROR_NOT_SUPPORTED;
+  }
+
+  return stream->context->ops->stream_get_input_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;
--- a/third_party/rust/cubeb-sys/libcubeb/src/cubeb_alsa.c
+++ b/third_party/rust/cubeb-sys/libcubeb/src/cubeb_alsa.c
@@ -9,20 +9,68 @@
 #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
@@ -31,16 +79,17 @@
    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];
@@ -240,18 +289,18 @@ set_timeout(struct timeval * timeout, un
   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 + snd_pcm_frames_to_bytes(stm->pcm, count);
-  memmove(stm->buffer, bufremains, snd_pcm_frames_to_bytes(stm->pcm, stm->bufframes - 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;
@@ -273,19 +322,19 @@ alsa_process_stream(cubeb_stream * stm)
 
   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. */
-  snd_pcm_poll_descriptors_revents(stm->pcm, stm->fds, stm->nfds, &revents);
+  WRAP(snd_pcm_poll_descriptors_revents)(stm->pcm, stm->fds, stm->nfds, &revents);
 
-  avail = snd_pcm_avail_update(stm->pcm);
+  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. */
@@ -298,17 +347,17 @@ alsa_process_stream(cubeb_stream * stm)
     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 = snd_pcm_readi(stm->pcm, stm->buffer+stm->bufframes, avail);
+    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);
@@ -342,17 +391,17 @@ alsa_process_stream(cubeb_stream * stm)
     }
   }
 
   /* 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 + snd_pcm_frames_to_bytes(stm->pcm, stm->bufframes);
+    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);
@@ -369,18 +418,18 @@ alsa_process_stream(cubeb_stream * stm)
     }
   }
 
   /* 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 + snd_pcm_frames_to_bytes(stm->pcm, stm->bufframes);
-    memset(buftail, 0, snd_pcm_frames_to_bytes(stm->pcm, drain_frames));
+    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;
     }
@@ -397,36 +446,36 @@ alsa_process_stream(cubeb_stream * stm)
       }
     } else {
       short * b = (short *) stm->buffer;
       for (uint32_t i = 0; i < avail * stm->params.channels; i++) {
         b[i] *= stm->volume;
       }
     }
 
-    wrote = snd_pcm_writei(stm->pcm, stm->buffer, avail);
+    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 = snd_pcm_recover(stm->pcm, 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 &&
-        snd_pcm_state(stm->pcm) == SND_PCM_STATE_PREPARED) {
-      avail = snd_pcm_start(stm->pcm);
+        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;
@@ -532,54 +581,54 @@ get_slave_pcm_node(snd_config_t * lconf,
   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 = snd_config_search(root_pcm, "slave", &slave_pcm);
+  r = WRAP(snd_config_search)(root_pcm, "slave", &slave_pcm);
   if (r < 0) {
     return NULL;
   }
 
-  r = snd_config_get_string(slave_pcm, &string);
+  r = WRAP(snd_config_get_string)(slave_pcm, &string);
   if (r >= 0) {
-    r = snd_config_search_definition(lconf, "pcm_slave", string, &slave_def);
+    r = WRAP(snd_config_search_definition)(lconf, "pcm_slave", string, &slave_def);
     if (r < 0) {
       return NULL;
     }
   }
 
   do {
-    r = snd_config_search(slave_def ? slave_def : slave_pcm, "pcm", &pcm);
+    r = WRAP(snd_config_search)(slave_def ? slave_def : slave_pcm, "pcm", &pcm);
     if (r < 0) {
       break;
     }
 
-    r = snd_config_get_string(slave_def ? slave_def : slave_pcm, &string);
+    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 = snd_config_search(lconf, node_name, &pcm);
+    r = WRAP(snd_config_search)(lconf, node_name, &pcm);
     if (r < 0) {
       break;
     }
 
     return pcm;
   } while (0);
 
   if (slave_def) {
-    snd_config_delete(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
@@ -592,114 +641,114 @@ init_local_config_with_workaround(char c
   snd_config_t * lconf;
   snd_config_t * pcm_node;
   snd_config_t * node;
   char const * string;
   char node_name[64];
 
   lconf = NULL;
 
-  if (snd_config == NULL) {
+  if (*WRAP(snd_config) == NULL) {
     return NULL;
   }
 
-  r = snd_config_copy(&lconf, snd_config);
+  r = WRAP(snd_config_copy)(&lconf, *WRAP(snd_config));
   if (r < 0) {
     return NULL;
   }
 
   do {
-    r = snd_config_search_definition(lconf, "pcm", pcm_name, &pcm_node);
+    r = WRAP(snd_config_search_definition)(lconf, "pcm", pcm_name, &pcm_node);
     if (r < 0) {
       break;
     }
 
-    r = snd_config_get_id(pcm_node, &string);
+    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 = snd_config_search(lconf, node_name, &pcm_node);
+    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 = snd_config_search(pcm_node, "type", &node);
+    r = WRAP(snd_config_search)(pcm_node, "type", &node);
     if (r < 0) {
       break;
     }
 
-    r = snd_config_get_string(node, &string);
+    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 = snd_config_search(pcm_node, "handle_underrun", &node);
+    r = WRAP(snd_config_search)(pcm_node, "handle_underrun", &node);
     if (r != -ENOENT) {
       break;
     }
 
     /* Disable pcm_pulse's asynchronous underrun handling. */
-    r = snd_config_imake_integer(&node, "handle_underrun", 0);
+    r = WRAP(snd_config_imake_integer)(&node, "handle_underrun", 0);
     if (r < 0) {
       break;
     }
 
-    r = snd_config_add(pcm_node, node);
+    r = WRAP(snd_config_add)(pcm_node, node);
     if (r < 0) {
       break;
     }
 
     return lconf;
   } while (0);
 
-  snd_config_delete(lconf);
+  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 = snd_pcm_open_lconf(pcm, pcm_name, stream, SND_PCM_NONBLOCK, local_config);
+    r = WRAP(snd_pcm_open_lconf)(pcm, pcm_name, stream, SND_PCM_NONBLOCK, local_config);
   } else {
-    r = snd_pcm_open(pcm, pcm_name, stream, SND_PCM_NONBLOCK);
+    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 = snd_pcm_close(pcm);
+  r = WRAP(snd_pcm_close)(pcm);
   pthread_mutex_unlock(&cubeb_alsa_mutex);
 
   return r;
 }
 
 static int
 alsa_register_stream(cubeb * ctx, cubeb_stream * stm)
 {
@@ -745,37 +794,60 @@ silent_error_handler(char const * file, 
   (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) {
-    snd_lib_error_set_handler(silent_error_handler);
+    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) {
@@ -814,17 +886,17 @@ alsa_init(cubeb ** context, char const *
   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);
-      snd_config_delete(ctx->local_config);
+      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;
@@ -856,20 +928,24 @@ alsa_destroy(cubeb * ctx)
 
   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);
-    snd_config_delete(ctx->local_config);
+    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,
@@ -943,51 +1019,51 @@ alsa_stream_init_single(cubeb * ctx, cub
   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 = snd_pcm_nonblock(stm->pcm, 1);
+  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 = snd_pcm_set_params(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED,
+  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 = snd_pcm_get_params(stm->pcm, &stm->buffer_size, &period_size);
+  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, snd_pcm_frames_to_bytes(stm->pcm, stm->buffer_size));
+  stm->buffer = calloc(1, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->buffer_size));
   assert(stm->buffer);
 
-  stm->nfds = snd_pcm_poll_descriptors_count(stm->pcm);
+  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 = snd_pcm_poll_descriptors(stm->pcm, stm->saved_fds, stm->nfds);
+  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;
@@ -1049,17 +1125,17 @@ alsa_stream_destroy(cubeb_stream * stm)
   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) {
-      snd_pcm_drain(stm->pcm);
+      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);
 
@@ -1095,22 +1171,22 @@ alsa_get_max_channel_count(cubeb * 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 = snd_pcm_hw_params_any(stm->pcm, hw_params);
+  r = WRAP(snd_pcm_hw_params_any)(stm->pcm, hw_params);
   if (r < 0) {
     return CUBEB_ERROR;
   }
 
-  r = snd_pcm_hw_params_get_channels_max(hw_params, max_channels);
+  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;
 }
@@ -1121,44 +1197,44 @@ alsa_get_preferred_sample_rate(cubeb * c
   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 = snd_pcm_open(&pcm, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK, SND_PCM_NO_AUTO_RESAMPLE);
+  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 = snd_pcm_hw_params_any(pcm, hw_params);
+  r = WRAP(snd_pcm_hw_params_any)(pcm, hw_params);
   if (r < 0) {
-    snd_pcm_close(pcm);
+    WRAP(snd_pcm_close)(pcm);
     return CUBEB_ERROR;
   }
 
-  r = snd_pcm_hw_params_get_rate(hw_params, rate, &dir);
+  r = WRAP(snd_pcm_hw_params_get_rate)(hw_params, rate, &dir);
   if (r >= 0) {
     /* There is a default rate: use it. */
-    snd_pcm_close(pcm);
+    WRAP(snd_pcm_close)(pcm);
     return CUBEB_OK;
   }
 
   /* Use a common rate, alsa may adjust it based on hw/etc. capabilities. */
   *rate = 44100;
 
-  r = snd_pcm_hw_params_set_rate_near(pcm, hw_params, rate, NULL);
+  r = WRAP(snd_pcm_hw_params_set_rate_near)(pcm, hw_params, rate, NULL);
   if (r < 0) {
-    snd_pcm_close(pcm);
+    WRAP(snd_pcm_close)(pcm);
     return CUBEB_ERROR;
   }
 
-  snd_pcm_close(pcm);
+  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;
@@ -1181,20 +1257,20 @@ alsa_stream_start(cubeb_stream * stm)
     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 &&
-      snd_pcm_state(stm->pcm) == SND_PCM_STATE_PREPARED) {
-    snd_pcm_start(stm->pcm);
+      WRAP(snd_pcm_state)(stm->pcm) == SND_PCM_STATE_PREPARED) {
+    WRAP(snd_pcm_start)(stm->pcm);
   }
-  snd_pcm_pause(stm->pcm, 0);
+  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;
   }
@@ -1224,34 +1300,34 @@ alsa_stream_stop(cubeb_stream * stm)
     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);
-  snd_pcm_pause(stm->pcm, 1);
+  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 (snd_pcm_state(stm->pcm) != SND_PCM_STATE_RUNNING ||
-      snd_pcm_delay(stm->pcm, &delay) != 0) {
+  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;
@@ -1266,17 +1342,17 @@ alsa_stream_get_position(cubeb_stream * 
 }
 
 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 (snd_pcm_delay(stm->pcm, &delay)) {
+  if (WRAP(snd_pcm_delay)(stm->pcm, &delay)) {
     return CUBEB_ERROR;
   }
 
   *latency = delay;
 
   return CUBEB_OK;
 }
 
@@ -1363,14 +1439,15 @@ static struct cubeb_ops const alsa_ops =
   .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_get_input_latency = NULL,
   .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
 };
--- a/third_party/rust/cubeb-sys/libcubeb/src/cubeb_audiotrack.c
+++ b/third_party/rust/cubeb-sys/libcubeb/src/cubeb_audiotrack.c
@@ -428,14 +428,15 @@ static struct cubeb_ops const audiotrack
   .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_get_input_latency = NULL,
   .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
 };
--- a/third_party/rust/cubeb-sys/libcubeb/src/cubeb_audiounit.cpp
+++ b/third_party/rust/cubeb-sys/libcubeb/src/cubeb_audiounit.cpp
@@ -496,16 +496,27 @@ audiounit_input_callback(void * 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;
@@ -515,22 +526,17 @@ audiounit_input_callback(void * user_ptr
      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);
-  if (outframes < total_input_frames) {
-    OSStatus r = AudioOutputUnitStop(stm->input_unit);
-    assert(r == 0);
-    stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
-    return noErr;
-  }
+  stm->draining = outframes < total_input_frames;
 
   // Reset input buffer
   stm->input_linear_buffer->clear();
 
   return noErr;
 }
 
 static void
@@ -607,20 +613,16 @@ audiounit_output_callback(void * user_pt
     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);
-    if (stm->input_unit) {
-      r = AudioOutputUnitStop(stm->input_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
@@ -687,19 +689,24 @@ audiounit_output_callback(void * user_pt
   }
 
   stm->draining = (UInt32) outframes < output_frames;
   stm->frames_played = stm->frames_queued;
   stm->frames_queued += outframes;
 
   /* Post process output samples. */
   if (stm->draining) {
-    size_t outbpf = cubeb_sample_size(stm->output_stream_params.format);
     /* Clear missing frames (silence) */
-    memset((uint8_t*)output_buffer + outframes * outbpf, 0, (output_frames - outframes) * outbpf);
+    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,
@@ -2859,16 +2866,25 @@ audiounit_stream_destroy_internal(cubeb_
   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)
 {
+  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);
+  }
+
   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
@@ -3600,14 +3616,15 @@ cubeb_ops const audiounit_ops = {
   /*.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_get_input_latency =*/ NULL,
   /*.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
 };
--- a/third_party/rust/cubeb-sys/libcubeb/src/cubeb_jack.cpp
+++ b/third_party/rust/cubeb-sys/libcubeb/src/cubeb_jack.cpp
@@ -45,16 +45,19 @@
   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,
@@ -124,16 +127,17 @@ static struct cubeb_ops const cbjack_ops
   .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_get_input_latency = NULL,
   .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 {
@@ -206,16 +210,19 @@ load_jack_lib(cubeb * context)
 #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);  \
@@ -735,18 +742,20 @@ cbjack_stream_init(cubeb * context, cube
 
   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 || output_device)
+  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;
@@ -959,18 +968,18 @@ cbjack_stream_set_volume(cubeb_stream * 
 
 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 capture";
-  const char * j_out = "JACK playback";
+  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);
@@ -989,19 +998,16 @@ cbjack_stream_device_destroy(cubeb_strea
   if (device->input_name)
     free(device->input_name);
   if (device->output_name)
     free(device->output_name);
   free(device);
   return CUBEB_OK;
 }
 
-#define JACK_DEFAULT_IN "JACK capture"
-#define JACK_DEFAULT_OUT "JACK playback"
-
 static int
 cbjack_enumerate_devices(cubeb * context, cubeb_device_type type,
                          cubeb_device_collection * collection)
 {
   if (!context)
     return CUBEB_ERROR;
 
   uint32_t rate;
--- a/third_party/rust/cubeb-sys/libcubeb/src/cubeb_kai.c
+++ b/third_party/rust/cubeb-sys/libcubeb/src/cubeb_kai.c
@@ -356,14 +356,15 @@ static struct cubeb_ops const kai_ops = 
   /*.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_get_input_latency = */ NULL,
   /*.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
 };
--- a/third_party/rust/cubeb-sys/libcubeb/src/cubeb_log.h
+++ b/third_party/rust/cubeb-sys/libcubeb/src/cubeb_log.h
@@ -11,36 +11,43 @@
 #include "cubeb/cubeb.h"
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 #if defined(__GNUC__) || defined(__clang__)
 #define PRINTF_FORMAT(fmt, args) __attribute__((format(printf, fmt, args)))
+#if defined(__FILE_NAME__)
+#define __FILENAME__ __FILE_NAME__
+#else
+#define __FILENAME__ (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : __FILE__)
+#endif
 #else
 #define PRINTF_FORMAT(fmt, args)
+#include <string.h>
+#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
 #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__); \
-    }                                                                        \
+#define LOG_INTERNAL(level, fmt, ...) do {                                              \
+    if (g_cubeb_log_callback && level <= g_cubeb_log_level) {                           \
+      g_cubeb_log_callback("%s:%d: " fmt "\n",  __FILENAME__, __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)
 
--- a/third_party/rust/cubeb-sys/libcubeb/src/cubeb_opensl.c
+++ b/third_party/rust/cubeb-sys/libcubeb/src/cubeb_opensl.c
@@ -747,17 +747,17 @@ opensl_init(cubeb ** context, char const
 
   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 (!ctx->p_output_latency_function) {
+  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;
 }
@@ -789,16 +789,63 @@ opensl_destroy(cubeb * ctx)
   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;
@@ -1015,33 +1062,54 @@ opensl_configure_capture(cubeb_stream * 
 }
 
 static int
 opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
   assert(stm);
   assert(params);
 
   stm->user_output_rate = params->rate;
-  stm->framesize = params->channels * sizeof(int16_t);
+  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;
 
-  SLDataFormat_PCM format;
-  int r = opensl_set_format(&format, params);
-  if (r != CUBEB_OK) {
-    return CUBEB_ERROR_INVALID_FORMAT;
+  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;
+  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;
 
@@ -1067,17 +1135,17 @@ opensl_configure_playback(cubeb_stream *
                                                   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.samplesPerSec = preferred_sampling_rate * 1000;
+    *format_sample_rate = preferred_sampling_rate * 1000;
     res = (*stm->context->eng)->CreateAudioPlayer(stm->context->eng,
                                                   &stm->playerObj,
                                                   &source,
                                                   &sink,
                                                   NELEMS(ids),
                                                   ids,
                                                   req);
   }
@@ -1620,16 +1688,28 @@ opensl_stream_get_position(cubeb_stream 
     *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);
@@ -1666,15 +1746,16 @@ static struct cubeb_ops const opensl_ops
   .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 = NULL,
+  .stream_get_latency = opensl_stream_get_latency,
+  .stream_get_input_latency = NULL,
   .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
 };
--- a/third_party/rust/cubeb-sys/libcubeb/src/cubeb_pulse.c
+++ b/third_party/rust/cubeb-sys/libcubeb/src/cubeb_pulse.c
@@ -571,16 +571,21 @@ layout_to_channel_map(cubeb_channel_layo
       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)
 {
@@ -610,29 +615,35 @@ pulse_context_init(cubeb * ctx)
 
   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) {
-    return CUBEB_ERROR;
+    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;                       \
     }                                           \
@@ -674,16 +685,19 @@ pulse_init(cubeb ** context, char const 
   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)
 {
@@ -1164,31 +1178,16 @@ pulse_stream_set_volume(cubeb_stream * s
     }
   }
 
   WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop);
 
   return CUBEB_OK;
 }
 
-struct sink_input_info_result {
-  pa_cvolume * cvol;
-  pa_threaded_mainloop * mainloop;
-};
-
-static void
-sink_input_info_cb(pa_context * c, pa_sink_input_info const * i, int eol, void * u)
-{
-  struct sink_input_info_result * r = u;
-  if (!eol) {
-    *r->cvol = i->volume;
-  }
-  WRAP(pa_threaded_mainloop_signal)(r->mainloop, 0);
-}
-
 typedef struct {
   char * default_sink_name;
   char * default_source_name;
 
   cubeb_device_info * devinfo;
   uint32_t max;
   uint32_t count;
   cubeb * context;
@@ -1477,16 +1476,22 @@ 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 &&
@@ -1520,62 +1525,65 @@ subscribe_success(pa_context *c, int suc
 {
   (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;
   }
 
-  WRAP(pa_threaded_mainloop_lock)(context->mainloop);
-
   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;
   }
-
-  if (collection_changed_callback == NULL) {
-    // Unregister subscription.
-    if (mask == PA_SUBSCRIPTION_MASK_NULL) {
-      WRAP(pa_context_set_subscribe_callback)(context->context, NULL, NULL);
-    }
-  } else {
-    WRAP(pa_context_set_subscribe_callback)(context->context, pulse_subscribe_callback, context);
-  }
+  /* 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;
 
-  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;
+  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,
@@ -1584,14 +1592,15 @@ static struct cubeb_ops const pulse_ops 
   .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_get_input_latency = NULL,
   .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
 };
--- a/third_party/rust/cubeb-sys/libcubeb/src/cubeb_resampler.cpp
+++ b/third_party/rust/cubeb-sys/libcubeb/src/cubeb_resampler.cpp
@@ -56,41 +56,79 @@ passthrough_resampler<T>::passthrough_re
 
 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) ||
+  assert((input_buffer && output_buffer) ||
          (output_buffer && !input_buffer && (!input_frames_count || *input_frames_count == 0)) ||
          (input_buffer && !output_buffer && output_frames == 0));
 
-  if (input_buffer) {
-    if (!output_buffer) {
+  // 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 ||
+        *input_frames_count < output_frames) {
+      // If we have pending input data left and have to first append the input
+      // so we can pass it as one pointer to the callback. Or this is a glitch.
+      // It can happen when system's performance is poor. Audible silence is
+      // being pushed at the end of the short input buffer. An improvement for
+      // the future is to resample to the output number of frames, when that happens.
+      internal_input_buffer.push(static_cast<T*>(input_buffer),
+                                 frames_to_samples(*input_frames_count));
+      if (internal_input_buffer.length() < frames_to_samples(output_frames)) {
+        // This is unxpected but it can happen when a glitch occurs. Fill the
+        // buffer with silence. First keep the actual number of input samples
+        // used without the silence.
+        pop_input_count = internal_input_buffer.length();
+        internal_input_buffer.push_silence(
+            frames_to_samples(output_frames) - internal_input_buffer.length());
+      } else {
+        pop_input_count = frames_to_samples(output_frames);
+      }
+      in_buf = internal_input_buffer.data();
+    } 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));
     }
-    internal_input_buffer.push(static_cast<T*>(input_buffer),
-                               frames_to_samples(*input_frames_count));
   }
 
-  long rv = data_callback(stream, user_ptr, internal_input_buffer.data(),
-                          output_buffer, output_frames);
+  long rv = data_callback(stream, user_ptr, in_buf, output_buffer, output_frames);
 
   if (input_buffer) {
-    internal_input_buffer.pop(nullptr, frames_to_samples(output_frames));
-    *input_frames_count = output_frames;
+    if (pop_input_count) {
+      internal_input_buffer.pop(nullptr, pop_input_count);
+      *input_frames_count = samples_to_frames(pop_input_count);
+    } else {
+      *input_frames_count = output_frames;
+    }
     drop_audio_if_needed();
   }
 
   return rv;
 }
 
+// Explicit instantiation of template class.
+template class passthrough_resampler<float>;
+template class passthrough_resampler<short>;
+
 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)
--- a/third_party/rust/cubeb-sys/libcubeb/src/cubeb_sndio.c
+++ b/third_party/rust/cubeb-sys/libcubeb/src/cubeb_sndio.c
@@ -7,30 +7,57 @@
 #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 */
@@ -93,39 +120,61 @@ s16_to_float(void *ptr, long nsamp)
   float *dst = ptr;
 
   src += nsamp;
   dst += nsamp;
   while (nsamp-- > 0)
     *(--dst) = (1. / 32768) * *(--src);
 }
 
+static const char *
+sndio_get_device()
+{
+#ifdef __linux__
+  /*
+   * 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)
 {
-#define MAXFDS 8
-  struct pollfd pfds[MAXFDS];
+  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 (!sio_start(s->hdl)) {
+  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) {
@@ -198,84 +247,125 @@ sndio_mainloop(void *arg)
         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 = sio_pollfd(s->hdl, pfds, events);
+    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 = sio_revents(s->hdl, pfds);
+    revents = WRAP(sio_revents)(s->hdl, pfds);
 
     if (revents & POLLHUP) {
       state = CUBEB_STATE_ERROR;
       break;
     }
 
     if (revents & POLLOUT) {
-      n = sio_write(s->hdl, s->pbuf + pstart, pend - pstart);
-      if (n == 0 && sio_eof(s->hdl)) {
+      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 = sio_read(s->hdl, s->rbuf + rstart, rend - rstart);
-      if (n == 0 && sio_eof(s->hdl)) {
+      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;
   }
-  sio_stop(s->hdl);
+  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));
+  *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,
@@ -318,22 +408,22 @@ sndio_stream_init(cubeb * context,
     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 = sio_open(NULL, s->mode, 1);
+  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;
   }
-  sio_initpar(&wpar);
+  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;
@@ -346,28 +436,28 @@ sndio_stream_init(cubeb * context,
     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 (!sio_setpar(s->hdl, &wpar) || !sio_getpar(s->hdl, &rpar)) {
+  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;
   }
-  sio_onmove(s->hdl, sndio_onmove, s);
+  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;
@@ -395,17 +485,17 @@ sndio_stream_init(cubeb * context,
   s->volume = 1.;
   *stream = s;
   DPR("sndio_stream_init() end, ok\n");
   (void)context;
   (void)stream_name;
   return CUBEB_OK;
 err:
   if (s->hdl)
-    sio_close(s->hdl);
+    WRAP(sio_close)(s->hdl);
   if (s->pbuf)
     free(s->pbuf);
   if (s->rbuf)
     free(s->pbuf);
   free(s);
   return CUBEB_ERROR;
 }
 
@@ -441,17 +531,17 @@ sndio_get_min_latency(cubeb * ctx, cubeb
 
   return CUBEB_OK;
 }
 
 static void
 sndio_stream_destroy(cubeb_stream *s)
 {
   DPR("sndio_stream_destroy()\n");
-  sio_close(s->hdl);
+  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
--- a/third_party/rust/cubeb-sys/libcubeb/src/cubeb_sun.c
+++ b/third_party/rust/cubeb-sys/libcubeb/src/cubeb_sun.c
@@ -1,32 +1,27 @@
 /*
- * Copyright © 2019 Nia Alarie
+ * Copyright © 2019-2020 Nia Alarie <nia@NetBSD.org>
  *
  * 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 <stdbool.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
-#include <math.h>
+#include <limits.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)
@@ -36,20 +31,16 @@
 #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.
  */
 
@@ -70,36 +61,36 @@
 #endif
 
 static struct cubeb_ops const sun_ops;
 
 struct cubeb {
   struct cubeb_ops const * ops;
 };
 
+struct sun_stream {
+  char name[32];
+  int fd;
+  void * buf;
+  struct audio_info info;
+  unsigned frame_size; /* precision in bytes * channels */
+  bool floating;
+};
+
 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;
+  bool running;
   float volume;
-  struct audio_info p_info; /* info for the play fd */
-  struct audio_info r_info; /* info for the record fd */
+  struct sun_stream play;
+  struct sun_stream record;
   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;
@@ -307,41 +298,43 @@ sun_device_collection_destroy(cubeb * co
 }
 
 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;
+    prinfo->precision = 16;
     break;
   case CUBEB_SAMPLE_S16BE:
     prinfo->encoding = AUDIO_ENCODING_SLINEAR_BE;
+    prinfo->precision = 16;
     break;
   case CUBEB_SAMPLE_FLOAT32NE:
-    stream->floating = 1;
     prinfo->encoding = AUDIO_ENCODING_SLINEAR;
+    prinfo->precision = 32;
     break;
   default:
     LOG("Unsupported format");
     return CUBEB_ERROR_INVALID_FORMAT;
   }
 #else
   switch (params->format) {
   case CUBEB_SAMPLE_S16NE:
     prinfo->encoding = AUDIO_ENCODING_LINEAR;
+    prinfo->precision = 16;
     break;
   case CUBEB_SAMPLE_FLOAT32NE:
-    stream->floating = 1;
     prinfo->encoding = AUDIO_ENCODING_LINEAR;
+    prinfo->precision = 32;
     break;
   default:
     LOG("Unsupported format");
     return CUBEB_ERROR_INVALID_FORMAT;
   }
 #endif
   if (ioctl(fd, AUDIO_SETINFO, info) == -1) {
     return CUBEB_ERROR;
@@ -352,77 +345,74 @@ sun_copy_params(int fd, cubeb_stream * s
   return CUBEB_OK;
 }
 
 static int
 sun_stream_stop(cubeb_stream * s)
 {
   pthread_mutex_lock(&s->mutex);
   if (s->running) {
-    s->running = 0;
+    s->running = false;
     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->play.fd != -1) {
+    close(s->play.fd);
   }
-  if (s->record_fd != -1) {
-    close(s->record_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->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)
+sun_float_to_linear32(void * buf, unsigned sample_count, float vol)
 {
-  unsigned i, sample_count = frames * channels;
-  float multiplier = vol * 0x8000;
+  float * in = buf;
+  int32_t * out = buf;
+  int32_t * tail = out + sample_count;
 
-  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;
-    }
+  while (out < tail) {
+    float f = *(in++) * vol;
+    if (f < -1.0)
+      f = -1.0;
+    else if (f > 1.0)
+      f = 1.0;
+    *(out++) = f * (float)INT32_MAX;
   }
 }
 
 static void
-sun_linear_to_float(int16_t * in, float * out,
-                    unsigned channels, long frames)
+sun_linear32_to_float(void * buf, unsigned sample_count) 
 {
-  unsigned i, sample_count = frames * channels;
+  int32_t * in = buf;
+  float * out = buf;
+  float * tail = out + sample_count;
 
-  for (i = 0; i < sample_count; ++i) {
-    out[i] = (1.0 / 0x8000) * in[i];
+  while (out < tail) {
+    *(out++) = (1.0 / 0x80000000) * *(in++);
   }
 }
 
 static void
-sun_linear_set_vol(int16_t * buf, unsigned channels, long frames, float vol)
+sun_linear16_set_vol(int16_t * buf, unsigned sample_count, float vol)
 {
-  unsigned i, sample_count = frames * channels;
+  unsigned i;
   int32_t multiplier = vol * 0x8000;
 
   for (i = 0; i < sample_count; ++i) {
     buf[i] = (buf[i] * multiplier) >> 15;
   }
 }
 
 static void *
@@ -440,77 +430,72 @@ sun_io_routine(void * arg)
   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 (s->record.fd != -1 && s->record.floating) {
+      sun_linear32_to_float(s->record.buf,
+                            s->record.info.record.channels * SUN_BUFFER_FRAMES);
+    }
+    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) {
+      float vol;
+
+      pthread_mutex_lock(&s->mutex);
+      vol = s->volume;
+      pthread_mutex_unlock(&s->mutex);
+
+      if (s->play.floating) {
+        sun_float_to_linear32(s->play.buf,
+                              s->play.info.play.channels * to_write, vol);
+      } else {
+        sun_linear16_set_vol(s->play.buf,
+                             s->play.info.play.channels * to_write, vol);
       }
     }
     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;
+    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) {
+        bytes = to_write * s->play.frame_size;
+        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);
+        frames = n / s->play.frame_size;
         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) {
+        bytes = to_read * s->record.frame_size;
+        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);
+        frames = n / s->record.frame_size;
         to_read -= frames;
         read_ofs += frames;
       }
     }
     if (drain && state != CUBEB_STATE_ERROR) {
       state = CUBEB_STATE_DRAINED;
       break;
     }
@@ -536,165 +521,157 @@ sun_stream_init(cubeb * context,
   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;
+  s->record.fd = -1;
+  s->play.fd = -1;
   if (input_device != 0) {
-    snprintf(s->input_name, sizeof(s->input_name),
+    snprintf(s->record.name, sizeof(s->record.name),
       "/dev/audio%zu", (uintptr_t)input_device - 1);
   } else {
-    snprintf(s->input_name, sizeof(s->input_name), "%s", SUN_DEFAULT_DEVICE);
+    snprintf(s->record.name, sizeof(s->record.name), "%s", SUN_DEFAULT_DEVICE);
   }
   if (output_device != 0) {
-    snprintf(s->output_name, sizeof(s->output_name),
+    snprintf(s->play.name, sizeof(s->play.name),
       "/dev/audio%zu", (uintptr_t)output_device - 1);
   } else {
-    snprintf(s->output_name, sizeof(s->output_name), "%s", SUN_DEFAULT_DEVICE);
+    snprintf(s->play.name, sizeof(s->play.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");
+    if (s->record.fd == -1) {
+      if ((s->record.fd = open(s->record.name, O_RDONLY)) == -1) {
+        LOG("Audio device could not be opened as read-only");
         ret = CUBEB_ERROR_DEVICE_UNAVAILABLE;
         goto error;
       }
     }
-    AUDIO_INITINFO(&s->r_info);
+    AUDIO_INITINFO(&s->record.info);
 #ifdef AUMODE_RECORD
-    s->r_info.mode = AUMODE_RECORD;
+    s->record.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) {
+    if ((ret = sun_copy_params(s->record.fd, s, input_stream_params,
+                               &s->record.info, &s->record.info.record)) != CUBEB_OK) {
       LOG("Setting record params failed");
       goto error;
     }
+    s->record.floating = (input_stream_params->format == CUBEB_SAMPLE_FLOAT32NE);
   }
   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");
+    if (s->play.fd == -1) {
+      if ((s->play.fd = open(s->play.name, O_WRONLY)) == -1) {
+        LOG("Audio device could not be opened as write-only");
         ret = CUBEB_ERROR_DEVICE_UNAVAILABLE;
         goto error;
       }
     }
-    AUDIO_INITINFO(&s->p_info);
+    AUDIO_INITINFO(&s->play.info);
 #ifdef AUMODE_PLAY
-    s->p_info.mode = AUMODE_PLAY;
+    s->play.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) {
+    if ((ret = sun_copy_params(s->play.fd, s, output_stream_params,
+                               &s->play.info, &s->play.info.play)) != CUBEB_OK) {
       LOG("Setting play params failed");
       goto error;
     }
+    s->play.floating = (output_stream_params->format == CUBEB_SAMPLE_FLOAT32NE);
   }
   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) {
+  s->play.frame_size = s->play.info.play.channels *
+                      (s->play.info.play.precision / 8);
+  if (s->play.fd != -1 &&
+     (s->play.buf = calloc(SUN_BUFFER_FRAMES, s->play.frame_size)) == 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;
-    }
+  s->record.frame_size = s->record.info.record.channels *
+                        (s->record.info.record.precision / 8);
+  if (s->record.fd != -1 &&
+     (s->record.buf = calloc(SUN_BUFFER_FRAMES, s->record.frame_size)) == 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;
+  s->running = true;
   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) {
+  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);
+  *position = (s->blocks_written * s->play.info.blocksize) / s->play.frame_size;
   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)
+sun_stream_get_latency(cubeb_stream * s, uint32_t * latency)
 {
 #ifdef AUDIO_GETBUFINFO
   struct audio_info info;
 
-  if (ioctl(stream->play_fd, AUDIO_GETBUFINFO, &info) == -1) {
+  if (ioctl(s->play.fd, AUDIO_GETBUFINFO, &info) == -1) {
     return CUBEB_ERROR;
   }
 
-  *latency = BYTES_TO_FRAMES(info.play.seek + info.blocksize,
-                             info.play.channels);
+  *latency = (info.play.seek + info.blocksize) / s->play.frame_size;
   return CUBEB_OK;
 #else
   cubeb_stream_params params;
 
-  params.rate = stream->p_info.play.sample_rate;
+  params.rate = stream->play.info.play.sample_rate;
 
   return sun_get_min_latency(NULL, params, latency);
 #endif
 }
 
 static int
 sun_stream_set_volume(cubeb_stream * stream, float volume)
 {
@@ -706,20 +683,20 @@ sun_stream_set_volume(cubeb_stream * str
 
 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;
+  (*device)->input_name = stream->record.fd != -1 ?
+    strdup(stream->record.name) : NULL;
+  (*device)->output_name = stream->play.fd != -1 ?
+    strdup(stream->play.name) : NULL;
   return CUBEB_OK;
 }
 
 static int
 sun_stream_device_destroy(cubeb_stream * stream, cubeb_device * device)
 {
   (void)stream;
   free(device->input_name);
@@ -739,14 +716,15 @@ static struct cubeb_ops const sun_ops = 
   .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_get_input_latency = NULL,
   .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
 };
--- a/third_party/rust/cubeb-sys/libcubeb/src/cubeb_utils.cpp
+++ b/third_party/rust/cubeb-sys/libcubeb/src/cubeb_utils.cpp
@@ -14,10 +14,11 @@ size_t cubeb_sample_size(cubeb_sample_fo
     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);
+      return 0;
   }
 }
--- a/third_party/rust/cubeb-sys/libcubeb/src/cubeb_wasapi.cpp
+++ b/third_party/rust/cubeb-sys/libcubeb/src/cubeb_wasapi.cpp
@@ -84,16 +84,19 @@ public:
 #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 {
+
+const int64_t LATENCY_NOT_AVAILABLE_YET = -1;
+
 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>;
@@ -206,16 +209,17 @@ struct cubeb {
   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;
+  UINT64 performance_counter_frequency;
 };
 
 class wasapi_endpoint_notification_client;
 
 /* We have three possible callbacks we can use with a stream:
  * - input only
  * - output only
  * - synchronized input and output
@@ -326,36 +330,42 @@ struct cubeb_stream {
   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;
+  std::atomic<std::atomic<bool>*> emergency_bailout { nullptr };
+  /* Synchronizes render thread start to ensure safe access to emergency_bailout. */
+  HANDLE thread_ready_event = 0;
+  /* This needs an active audio input stream to be known, and is updated in the
+   * first audio input callback. */
+  std::atomic<int64_t> input_latency_hns { LATENCY_NOT_AVAILABLE_YET };
 };
 
 class monitor_device_notifications {
 public:
   monitor_device_notifications(cubeb * context)
   : cubeb_context(context)
   {
     create_thread();
   }
 
   ~monitor_device_notifications()
   {
-    SetEvent(shutdown);
-    WaitForSingleObject(thread, 5000);
+    SetEvent(begin_shutdown);
+    WaitForSingleObject(shutdown_complete, INFINITE);
     CloseHandle(thread);
 
     CloseHandle(input_changed);
     CloseHandle(output_changed);
-    CloseHandle(shutdown);
+    CloseHandle(begin_shutdown);
+    CloseHandle(shutdown_complete);
   }
 
   void notify(EDataFlow flow)
   {
     XASSERT(cubeb_context);
     if (flow == eCapture && cubeb_context->input_collection_changed_callback) {
       bool res = SetEvent(input_changed);
       if (!res) {
@@ -370,18 +380,19 @@ public:
       }
     }
   }
 private:
   static unsigned int __stdcall
   thread_proc(LPVOID args)
   {
     XASSERT(args);
-    static_cast<monitor_device_notifications*>(args)
-      ->notification_thread_loop();
+    auto mdn = static_cast<monitor_device_notifications*>(args);
+    mdn->notification_thread_loop();
+    SetEvent(mdn->shutdown_complete);
     return 0;
   }
 
   void notification_thread_loop()
   {
     struct auto_com {
       auto_com() {
         HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
@@ -390,17 +401,17 @@ private:
       ~auto_com() {
         CoUninitialize();
       }
     } com;
 
     HANDLE wait_array[3] = {
       input_changed,
       output_changed,
-      shutdown,
+      begin_shutdown,
     };
 
     while (true) {
       Sleep(200);
 
       DWORD wait_result = WaitForMultipleObjects(ARRAY_LENGTH(wait_array),
                                                  wait_array,
                                                  FALSE,
@@ -428,19 +439,25 @@ private:
     }
 
     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.");
+    begin_shutdown = CreateEvent(nullptr, 0, 0, nullptr);
+    if (!begin_shutdown) {
+      LOG("Failed to create begin_shutdown event.");
+      return;
+    }
+
+    shutdown_complete = CreateEvent(nullptr, 0, 0, nullptr);
+    if (!shutdown_complete) {
+      LOG("Failed to create shutdown_complete event.");
       return;
     }
 
     thread = (HANDLE) _beginthreadex(nullptr,
                                      256 * 1024,
                                      thread_proc,
                                      this,
                                      STACK_SIZE_PARAM_IS_A_RESERVATION,
@@ -449,17 +466,18 @@ private:
       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;
+  HANDLE begin_shutdown = INVALID_HANDLE_VALUE;
+  HANDLE shutdown_complete = INVALID_HANDLE_VALUE;
 
   cubeb * cubeb_context = nullptr;
 };
 
 class wasapi_collection_notification_client : public IMMNotificationClient
 {
 public:
   /* The implementation of MSCOM was copied from MSDN. */
@@ -840,16 +858,17 @@ int wasapi_stream_reset_default_device(c
 bool get_input_buffer(cubeb_stream * stm)
 {
   XASSERT(has_input(stm));
 
   HRESULT hr;
   BYTE * input_packet = NULL;
   DWORD flags;
   UINT64 dev_pos;
+  UINT64 pc_position;
   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).
@@ -869,23 +888,35 @@ bool get_input_buffer(cubeb_stream * stm
       return false;
     }
 
     UINT32 frames;
     hr = stm->capture_client->GetBuffer(&input_packet,
                                         &frames,
                                         &flags,
                                         &dev_pos,
-                                        NULL);
+                                        &pc_position);
+
     if (FAILED(hr)) {
       LOG("GetBuffer failed for capture: %lx", hr);
       return false;
     }
     XASSERT(frames == next);
 
+    if (stm->context->performance_counter_frequency) {
+      LARGE_INTEGER now;
+      UINT64 now_hns;
+      // See https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nf-audioclient-iaudiocaptureclient-getbuffer, section "Remarks".
+      QueryPerformanceCounter(&now);
+      now_hns = 10000000 * now.QuadPart / stm->context->performance_counter_frequency;
+      if (now_hns >= pc_position) {
+        stm->input_latency_hns = now_hns - pc_position;
+      }
+    }
+
     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
@@ -1138,16 +1169,23 @@ refill_callback_output(cubeb_stream * st
 }
 
 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;
@@ -1166,21 +1204,16 @@ wasapi_stream_render_loop(LPVOID stream)
   /* 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());
   }
 
-  // This has already been nulled out, simply exit.
-  if (!emergency_bailout) {
-    is_playing = false;
-  }
-
   /* 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
@@ -1509,16 +1542,24 @@ int wasapi_init(cubeb ** context, char c
   cubeb * ctx = new cubeb();
 
   ctx->ops = &wasapi_ops;
   if (cubeb_strings_init(&ctx->device_ids) != CUBEB_OK) {
     delete ctx;
     return CUBEB_ERROR;
   }
 
+  LARGE_INTEGER frequency;
+  if (QueryPerformanceFrequency(&frequency)) {
+    LOG("Failed getting performance counter frequency, latency reporting will be inacurate");
+    ctx->performance_counter_frequency = 0;
+  } else {
+    ctx->performance_counter_frequency = frequency.QuadPart;
+  }
+
   *context = ctx;
 
   return CUBEB_OK;
 }
 }
 
 namespace {
 bool stop_and_join_render_thread(cubeb_stream * stm)
@@ -1539,34 +1580,25 @@ bool stop_and_join_render_thread(cubeb_s
   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_TIMEOUT) {
+  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 timed out,"
-        " leaking the thread: %lx", GetLastError());
+    LOG("Destroy WaitForSingleObject on thread failed: %lx, %lx", r, GetLastError());
     rv = false;
   }
-  if (r == WAIT_FAILED) {
-    *(stm->emergency_bailout) = true;
-    // We give the ownership to the rendering thread.
-    stm->emergency_bailout = nullptr;
-    LOG("Destroy WaitForSingleObject on thread failed: %lx", 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;
@@ -1921,24 +1953,28 @@ int setup_wasapi_stream_one_side(cubeb_s
           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. */
+#if 0 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1590902
     hr = device->Activate(__uuidof(IAudioClient3),
                           CLSCTX_INPROC_SERVER,
                           NULL, audio_client.receive_vpp());
     if (hr == E_NOINTERFACE) {
+#endif
       hr = device->Activate(__uuidof(IAudioClient),
                             CLSCTX_INPROC_SERVER,
                             NULL, audio_client.receive_vpp());
+#if 0
     }
+#endif
 
     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);
@@ -1994,26 +2030,30 @@ int setup_wasapi_stream_one_side(cubeb_s
   // 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 0 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1590902
   if (initialize_iaudioclient3(audio_client, stm, mix_format, flags, direction)) {
     LOG("Initialized with IAudioClient3");
   } else {
+#endif
     hr = audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED,
                                   flags,
                                   frames_to_hns(stm, stm->latency),
                                   0,
                                   mix_format.get(),
                                   NULL);
+#if 0
   }
+#endif
   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"
@@ -2466,23 +2506,37 @@ int wasapi_stream_start(cubeb_stream * s
   }
 
   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);
@@ -2506,21 +2560,18 @@ int wasapi_stream_stop(cubeb_stream * st
         return CUBEB_ERROR;
       }
     }
 
     stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
   }
 
   if (stop_and_join_render_thread(stm)) {
-    // This is null if we've given the pointer to the other thread
-    if (stm->emergency_bailout.load()) {
-      delete stm->emergency_bailout.load();
-      stm->emergency_bailout = nullptr;
-    }
+    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;
 }
@@ -2586,16 +2637,36 @@ int wasapi_stream_get_latency(cubeb_stre
   if (FAILED(hr)) {
     return CUBEB_ERROR;
   }
   *latency = hns_to_frames(stm, latency_hns);
 
   return CUBEB_OK;
 }
 
+
+int wasapi_stream_get_input_latency(cubeb_stream * stm, uint32_t * latency)
+{
+  XASSERT(stm && latency);
+
+  if (!has_input(stm)) {
+    return CUBEB_ERROR;
+  }
+
+  auto_lock lock(stm->stream_reset_lock);
+
+  if (stm->input_latency_hns == LATENCY_NOT_AVAILABLE_YET) {
+    return CUBEB_ERROR;
+  }
+
+  *latency = hns_to_frames(stm, stm->input_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;
   }
 
@@ -2945,15 +3016,16 @@ cubeb_ops const wasapi_ops = {
   /*.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_get_input_latency =*/ wasapi_stream_get_input_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
--- a/third_party/rust/cubeb-sys/libcubeb/src/cubeb_winmm.c
+++ b/third_party/rust/cubeb-sys/libcubeb/src/cubeb_winmm.c
@@ -1054,14 +1054,15 @@ static struct cubeb_ops const winmm_ops 
   /*.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_get_input_latency = */ NULL,
   /*.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
 };
--- a/third_party/rust/cubeb-sys/libcubeb/test/test_device_changed_callback.cpp
+++ b/third_party/rust/cubeb-sys/libcubeb/test/test_device_changed_callback.cpp
@@ -61,32 +61,16 @@ void test_registering_and_unregistering_
     return;
   }
   ASSERT_EQ(r, CUBEB_OK) << "Error registering device changed callback";
 
   r = cubeb_stream_register_device_changed_callback(stream, nullptr);
   ASSERT_EQ(r, CUBEB_OK) << "Error unregistering device changed callback";
 }
 
-void test_registering_second_callbacks(cubeb_stream * stream)
-{
-  int r = cubeb_stream_register_device_changed_callback(stream, device_changed_callback);
-  if (r == CUBEB_ERROR_NOT_SUPPORTED) {
-    return;
-  }
-  ASSERT_EQ(r, CUBEB_OK) << "Error registering device changed callback";
-
-  // Get an assertion fails when registering a callback
-  // without unregistering the original one.
-  ASSERT_DEATH(
-    cubeb_stream_register_device_changed_callback(stream, device_changed_callback),
-    ""
-  );
-}
-
 TEST(cubeb, device_changed_callbacks)
 {
   cubeb * ctx;
   cubeb_stream * stream;
   cubeb_stream_params input_params;
   cubeb_stream_params output_params;
   int r = CUBEB_OK;
   uint32_t latency_frames = 0;
@@ -116,12 +100,10 @@ TEST(cubeb, device_changed_callbacks)
                         NULL, &input_params, NULL, &output_params,
                         latency_frames, data_callback, state_callback, nullptr);
   ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
 
   test_registering_null_callback_twice(stream);
 
   test_registering_and_unregistering_callback(stream);
 
-  test_registering_second_callbacks(stream);
-
   cubeb_stream_destroy(stream);
 }
--- a/third_party/rust/cubeb-sys/libcubeb/test/test_devices.cpp
+++ b/third_party/rust/cubeb-sys/libcubeb/test/test_devices.cpp
@@ -156,17 +156,17 @@ TEST(cubeb, enumerate_devices)
 
   fprintf(stdout, "Enumerating input devices for backend %s\n",
       cubeb_get_backend_id(ctx));
 
   r = cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_INPUT, &collection);
   if (r == CUBEB_ERROR_NOT_SUPPORTED) {
     fprintf(stderr, "Device enumeration not supported"
                     " for this backend, skipping this test.\n");
-    r = CUBEB_OK;
+    return;
   }
   ASSERT_EQ(r, CUBEB_OK) << "Error enumerating devices " << r;
 
   fprintf(stdout, "Found %zu input devices\n", collection.count);
   print_device_collection(&collection, stdout);
   cubeb_device_collection_destroy(ctx, &collection);
 
   fprintf(stdout, "Enumerating output devices for backend %s\n",
--- a/third_party/rust/cubeb-sys/libcubeb/test/test_loopback.cpp
+++ b/third_party/rust/cubeb-sys/libcubeb/test/test_loopback.cpp
@@ -21,17 +21,17 @@
 #include <string>
 #include "cubeb/cubeb.h"
 //#define ENABLE_NORMAL_LOG
 //#define ENABLE_VERBOSE_LOG
 #include "common.h"
 const uint32_t SAMPLE_FREQUENCY = 48000;
 const uint32_t TONE_FREQUENCY = 440;
 const double OUTPUT_AMPLITUDE = 0.25;
-const uint32_t NUM_FRAMES_TO_OUTPUT = SAMPLE_FREQUENCY / 20; /* play ~50ms of samples */
+const int32_t NUM_FRAMES_TO_OUTPUT = SAMPLE_FREQUENCY / 20; /* play ~50ms of samples */
 
 template<typename T> T ConvertSampleToOutput(double input);
 template<> float ConvertSampleToOutput(double input) { return float(input); }
 template<> short ConvertSampleToOutput(double input) { return short(input * 32767.0f); }
 
 template<typename T> double ConvertSampleFromOutput(T sample);
 template<> double ConvertSampleFromOutput(float sample) { return double(sample); }
 template<> double ConvertSampleFromOutput(short sample) { return double(sample / 32767.0); }
--- a/third_party/rust/cubeb-sys/libcubeb/test/test_resampler.cpp
+++ b/third_party/rust/cubeb-sys/libcubeb/test/test_resampler.cpp
@@ -884,8 +884,180 @@ TEST(cubeb, resampler_drift_drop_data)
       }
       ASSERT_EQ(got, BUF_BASE_SIZE);
     }
 
     cubeb_resampler_destroy(resampler);
   }
 }
 
+static long
+passthrough_resampler_fill_eq_input(cubeb_stream * stream,
+                                    void * user_ptr,
+                                    void const * input_buffer,
+                                    void * output_buffer,
+                                    long nframes) {
+  // gtest does not support using ASSERT_EQ and friends in a
+  // function that returns a value.
+  [nframes, input_buffer]() {
+    ASSERT_EQ(nframes, 32);
+    const float* input = static_cast<const float*>(input_buffer);
+    for (int i = 0; i < 64; ++i) {
+      ASSERT_FLOAT_EQ(input[i], 0.01 * i);
+    }
+  }();
+  return nframes;
+}
+
+TEST(cubeb, passthrough_resampler_fill_eq_input) {
+  uint32_t channels = 2;
+  uint32_t sample_rate = 44100;
+  passthrough_resampler<float> resampler =
+    passthrough_resampler<float>(nullptr, passthrough_resampler_fill_eq_input,
+                                 nullptr, channels, sample_rate);
+
+  long input_frame_count = 32;
+  long output_frame_count = 32;
+  float input[64] = {};
+  float output[64] = {};
+  for (uint32_t i = 0; i < input_frame_count * channels; ++i) {
+    input[i] = 0.01 * i;
+  }
+  long got = resampler.fill(input, &input_frame_count, output, output_frame_count);
+  ASSERT_EQ(got, output_frame_count);
+  // Input frames used must be equal to output frames.
+  ASSERT_EQ(input_frame_count, output_frame_count);
+}
+
+static long
+passthrough_resampler_fill_short_input(cubeb_stream * stream,
+                                       void * user_ptr,
+                                       void const * input_buffer,
+                                       void * output_buffer,
+                                       long nframes) {
+  // gtest does not support using ASSERT_EQ and friends in a
+  // function that returns a value.
+  [nframes, input_buffer]() {
+    ASSERT_EQ(nframes, 32);
+    const float* input = static_cast<const float*>(input_buffer);
+    // First part contains the input
+    for (int i = 0; i < 32; ++i) {
+      ASSERT_FLOAT_EQ(input[i], 0.01 * i);
+    }
+    // missing part contains silence
+    for (int i = 32; i < 64; ++i) {
+      ASSERT_FLOAT_EQ(input[i], 0.0);
+    }
+  }();
+  return nframes;
+}
+
+TEST(cubeb, passthrough_resampler_fill_short_input) {
+  uint32_t channels = 2;
+  uint32_t sample_rate = 44100;
+  passthrough_resampler<float> resampler =
+    passthrough_resampler<float>(nullptr, passthrough_resampler_fill_short_input,
+                                 nullptr, channels, sample_rate);
+
+  long input_frame_count = 16;
+  long output_frame_count = 32;
+  float input[64] = {};
+  float output[64] = {};
+  for (uint32_t i = 0; i < input_frame_count * channels; ++i) {
+    input[i] = 0.01 * i;
+  }
+  long got = resampler.fill(input, &input_frame_count, output, output_frame_count);
+  ASSERT_EQ(got, output_frame_count);
+  // Input frames used are less than the output frames due to glitch.
+  ASSERT_EQ(input_frame_count, output_frame_count - 16);
+}
+
+static long
+passthrough_resampler_fill_input_left(cubeb_stream * stream,
+                                     void * user_ptr,
+                                     void const * input_buffer,
+                                     void * output_buffer,
+                                     long nframes) {
+  // gtest does not support using ASSERT_EQ and friends in a
+  // function that returns a value.
+  int iteration = *static_cast<int*>(user_ptr);
+  if (iteration == 1) {
+    [nframes, input_buffer]() {
+      ASSERT_EQ(nframes, 32);
+      const float* input = static_cast<const float*>(input_buffer);
+      for (int i = 0; i < 64; ++i) {
+        ASSERT_FLOAT_EQ(input[i], 0.01 * i);
+      }
+    }();
+  } else if (iteration == 2) {
+    [nframes, input_buffer]() {
+      ASSERT_EQ(nframes, 32);
+      const float* input = static_cast<const float*>(input_buffer);
+      for (int i = 0; i < 32; ++i) {
+        // First part contains the reamaining input samples from previous
+        // iteration (since they were more).
+        ASSERT_FLOAT_EQ(input[i], 0.01 * (i + 64));
+        // next part contains the new buffer
+        ASSERT_FLOAT_EQ(input[i + 32], 0.01 * i);
+      }
+    }();
+  } else if (iteration == 3) {
+    [nframes, input_buffer]() {
+      ASSERT_EQ(nframes, 32);
+      const float* input = static_cast<const float*>(input_buffer);
+      for (int i = 0; i < 32; ++i) {
+        // First part (16 frames) contains the reamaining input samples
+        // from previous iteration (since they were more).
+        ASSERT_FLOAT_EQ(input[i], 0.01 * (i + 32));
+      }
+      for (int i = 0; i < 16; ++i) {
+        // next part (8 frames) contains the new input buffer.
+        ASSERT_FLOAT_EQ(input[i + 32], 0.01 * i);
+        // last part (8 frames) contains silence.
+        ASSERT_FLOAT_EQ(input[i + 32 + 16], 0.0);
+      }
+    }();
+  }
+  return nframes;
+}
+
+TEST(cubeb, passthrough_resampler_fill_input_left) {
+  const uint32_t channels = 2;
+  const uint32_t sample_rate = 44100;
+  int iteration = 0;
+  passthrough_resampler<float> resampler =
+    passthrough_resampler<float>(nullptr, passthrough_resampler_fill_input_left,
+                                 &iteration, channels, sample_rate);
+
+  long input_frame_count = 48; // 32 + 16
+  const long output_frame_count = 32;
+  float input[96] = {};
+  float output[64] = {};
+  for (uint32_t i = 0; i < input_frame_count * channels; ++i) {
+    input[i] = 0.01 * i;
+  }
+
+  // 1st iteration, add the extra input.
+  iteration = 1;
+  long got = resampler.fill(input, &input_frame_count, output, output_frame_count);
+  ASSERT_EQ(got, output_frame_count);
+  // Input frames used must be equal to output frames.
+  ASSERT_EQ(input_frame_count, output_frame_count);
+
+  // 2st iteration, use the extra input from previous iteration,
+  // 16 frames are remaining in the input buffer.
+  input_frame_count = 32; // we need 16 input frames but we get more;
+  iteration = 2;
+  got = resampler.fill(input, &input_frame_count, output, output_frame_count);
+  ASSERT_EQ(got, output_frame_count);
+  // Input frames used must be equal to output frames.
+  ASSERT_EQ(input_frame_count, output_frame_count);
+
+  // 3rd iteration, use the extra input from previous iteration.
+  // 16 frames are remaining in the input buffer.
+  input_frame_count = 16 - 8; // We need 16 more input frames but we only get 8.
+  iteration = 3;
+  got = resampler.fill(input, &input_frame_count, output, output_frame_count);
+  ASSERT_EQ(got, output_frame_count);
+  // Input frames used are less than the output frames due to glitch.
+  ASSERT_EQ(input_frame_count, output_frame_count - 8);
+}
+
--- a/third_party/rust/cubeb-sys/libcubeb/test/test_ring_buffer.cpp
+++ b/third_party/rust/cubeb-sys/libcubeb/test/test_ring_buffer.cpp
@@ -97,30 +97,30 @@ void test_ring_multi(lock_free_audio_rin
   const int block_size = 128;
 
   std::thread t([=, &buf] {
     int iterations = 1002;
     std::unique_ptr<T[]> in_buffer(new T[capacity_frames * channels]);
     sequence_generator<T> gen(channels);
 
     while(iterations--) {
-      std::this_thread::sleep_for(std::chrono::microseconds(10));
+      std::this_thread::yield();
       gen.get(in_buffer.get(), block_size);
       int rv = buf.enqueue(in_buffer.get(), block_size);
       ASSERT_TRUE(rv <= block_size);
       if (rv != block_size) {
         gen.rewind(block_size - rv);
       }
     }
   });
 
   int remaining = 1002;
 
   while(remaining--) {
-    std::this_thread::sleep_for(std::chrono::microseconds(10));
+    std::this_thread::yield();
     int rv = buf.dequeue(out_buffer.get(), block_size);
     ASSERT_TRUE(rv <= block_size);
     checker.check(out_buffer.get(), rv);
   }
 
   t.join();
 }
 
--- a/third_party/rust/cubeb-sys/libcubeb/tools/cubeb-test.cpp
+++ b/third_party/rust/cubeb-sys/libcubeb/tools/cubeb-test.cpp
@@ -44,64 +44,69 @@ void print_log(const char* msg, ...) {
 
 class cubeb_client final {
 public:
   cubeb_client() {}
   ~cubeb_client() {}
 
   bool init(char const * backend_name = nullptr);
   bool init_stream();
-  bool start_stream() const;
-  bool stop_stream() const;
+  bool start_stream();
+  bool stop_stream();
   bool destroy_stream() const;
   bool destroy();
   bool activate_log(cubeb_log_level log_level) const;
   void set_latency_testing(bool on);
   void set_latency_frames(uint32_t latency_frames);
   uint64_t get_stream_position() const;
   uint32_t get_stream_latency() const;
+  uint32_t get_max_channel_count() const;
 
   long user_data_cb(cubeb_stream* stm, void* user, const void* input_buffer,
                     void* output_buffer, long nframes);
 
   void user_state_cb(cubeb_stream* stm, void* user, cubeb_state state);
 
   bool register_device_collection_changed(cubeb_device_type devtype) const;
   bool unregister_device_collection_changed(cubeb_device_type devtype) const;
 
   cubeb_stream_params output_params = {};
   cubeb_stream_params input_params = {};
 
+  void force_drain() { _force_drain = true; }
+
 private:
   bool has_input() { return input_params.rate != 0; }
   bool has_output() { return output_params.rate != 0; }
 
   cubeb* context = nullptr;
 
   cubeb_stream* stream = nullptr;
   cubeb_devid output_device = nullptr;
   cubeb_devid input_device = nullptr;
 
   /* Accessed only from client and audio thread. */
   std::atomic<uint32_t> _rate = {0};
   std::atomic<uint32_t> _channels = {0};
   std::atomic<bool> _latency_testing = {false};
   std::atomic<uint32_t> _latency_frames = {0}; // if !0, override. Else, use min.
+  std::atomic<bool> _force_drain = {false};
 
 
   /* Accessed only from audio thread. */
   uint32_t _total_frames = 0;
 };
 
 bool cubeb_client::init(char const * backend_name) {
-  int rv = cubeb_init(&context, "Cubeb Test Application", nullptr);
+  int rv = cubeb_init(&context, "Cubeb Test Application", backend_name);
   if (rv != CUBEB_OK) {
     fprintf(stderr, "Could not init cubeb\n");
     return false;
   }
+  fprintf(stderr, "Init cubeb backend: %s\n", cubeb_get_backend_id(context));
   return true;
 }
 
 static long user_data_cb_s(cubeb_stream* stm, void* user,
                            const void* input_buffer, void* output_buffer,
                            long nframes) {
   assert(user);
   return static_cast<cubeb_client*>(user)->user_data_cb(stm, user, input_buffer,
@@ -154,26 +159,28 @@ bool cubeb_client::init_stream() {
                         has_output() ? &output_params : nullptr, latency, user_data_cb_s, user_state_cb_s, this);
   if (rv != CUBEB_OK) {
     fprintf(stderr, "Could not open the stream\n");
     return false;
   }
   return true;
 }
 
-bool cubeb_client::start_stream() const {
+bool cubeb_client::start_stream() {
+  _force_drain = false;
   int rv = cubeb_stream_start(stream);
   if (rv != CUBEB_OK) {
     fprintf(stderr, "Could not start the stream\n");
     return false;
   }
   return true;
 }
 
-bool cubeb_client::stop_stream() const {
+bool cubeb_client::stop_stream() {
+  _force_drain = false;
   int rv = cubeb_stream_stop(stream);
   if (rv != CUBEB_OK) {
     fprintf(stderr, "Could not stop the stream\n");
     return false;
   }
   return true;
 }
 
@@ -192,16 +199,26 @@ uint32_t cubeb_client::get_stream_latenc
   int rv = cubeb_stream_get_latency(stream, &latency);
   if (rv != CUBEB_OK) {
     fprintf(stderr, "Could not get the latency of the stream\n");
     return 0;
   }
   return latency;
 }
 
+uint32_t cubeb_client::get_max_channel_count() const {
+  uint32_t channels = 0;
+  int rv = cubeb_get_max_channel_count(context, &channels);
+  if (rv != CUBEB_OK) {
+    fprintf(stderr, "Could not get max channel count\n");
+    return 0;
+  }
+  return channels;
+}
+
 bool cubeb_client::destroy_stream() const {
   cubeb_stream_destroy(stream);
   return true;
 }
 
 bool cubeb_client::destroy() {
   cubeb_destroy(context);
   return true;
@@ -243,43 +260,48 @@ static void fill_with_sine_tone(float* b
 
 long cubeb_client::user_data_cb(cubeb_stream* stm, void* user,
                                 const void* input_buffer, void* output_buffer,
                                 long nframes) {
   if (input_buffer && output_buffer) {
     const float* in = static_cast<const float*>(input_buffer);
     float* out = static_cast<float*>(output_buffer);
     if (_latency_testing) {
-      for (uint32_t i = 0; i < nframes; i++) {
+      for (int32_t i = 0; i < nframes; i++) {
         // Impulses every second, mixed with the input signal fed back at half
         // gain, to measure the input-to-output latency via feedback.
         uint32_t clock = ((_total_frames + i) % _rate);
         if (!clock) {
           for (uint32_t j = 0; j < _channels; j++) {
             out[i * _channels + j] = 1.0 + in[i] * 0.5;
           }
         } else {
           for (uint32_t j = 0; j < _channels; j++) {
             out[i * _channels + j] = 0.0 + in[i] * 0.5;
           }
         }
       }
     } else {
-      for (uint32_t i = 0; i < nframes; i++) {
+      for (int32_t i = 0; i < nframes; i++) {
         for (uint32_t j = 0; j < _channels; j++) {
           out[i * _channels + j] = in[i];
         }
       }
     }
   } else if (output_buffer && !input_buffer) {
     fill_with_sine_tone(static_cast<float*>(output_buffer), nframes, _channels,
                        _rate, _total_frames);
   }
 
   _total_frames += nframes;
+
+  if (_force_drain) {
+    return nframes - 1;
+  }
+
   return nframes;
 }
 
 void cubeb_client::user_state_cb(cubeb_stream* stm, void* user,
                                  cubeb_state state) {
   fprintf(stderr, "state is %s\n", state_to_string(state));
 }
 
@@ -326,31 +348,34 @@ struct operation_data {
   cubeb_device_type collection_device_type;
 };
 
 void print_help() {
   const char * msg =
     "0: change log level to disabled\n"
     "1: change log level to normal\n"
     "2: change log level to verbose\n"
+    "c: get max number of channels\n"
     "p: start a initialized stream\n"
     "s: stop a started stream\n"
-    "c: get stream position (client thread)\n"
+    "d: destroy stream\n"
+    "e: force stream to drain\n"
+    "f: get stream position (client thread)\n"
     "i: change device type to input\n"
     "o: change device type to output\n"
     "a: change device type to input and output\n"
     "k: change device type to unknown\n"
     "r: register device collection changed callback for the current device type\n"
     "u: unregister device collection changed callback for the current device type\n"
     "q: quit\n"
     "h: print this message\n";
   fprintf(stderr, "%s\n", msg);
 }
 
-bool choose_action(const cubeb_client& cl, operation_data * op, int c) {
+bool choose_action(cubeb_client& cl, operation_data * op, int c) {
   // Consume "enter" and "space"
   while (c == 10 || c == 32) {
     c = getchar();
   }
   if (c == EOF) {
     c = 'q';
   }
 
@@ -393,17 +418,29 @@ bool choose_action(const cubeb_client& c
     }
   } else if (c == 's') {
     bool res = cl.stop_stream();
     if (res) {
       fprintf(stderr, "stop_stream succeed\n");
     } else {
       fprintf(stderr, "stop_stream failed\n");
     }
+  } else if (c == 'd') {
+    bool res = cl.destroy_stream();
+    if (res) {
+      fprintf(stderr, "destroy_stream succeed\n");
+    } else {
+      fprintf(stderr, "destroy_stream failed\n");
+    }
+  } else if (c == 'e') {
+    cl.force_drain();
   } else if (c == 'c') {
+    uint32_t channel_count = cl.get_max_channel_count();
+    fprintf(stderr, "max channel count (default output device): %u\n", channel_count);
+  } else if (c == 'f') {
     uint64_t pos = cl.get_stream_position();
     uint64_t latency = cl.get_stream_latency();
     fprintf(stderr, "stream position %" PRIu64 " (latency %" PRIu64 ")\n", pos, latency);
   } else if (c == 'i') {
     op->collection_device_type = CUBEB_DEVICE_TYPE_INPUT;
     fprintf(stderr, "collection device type changed to INPUT\n");
   } else if (c == 'o') {
     op->collection_device_type = CUBEB_DEVICE_TYPE_OUTPUT;
@@ -463,17 +500,17 @@ int main(int argc, char* argv[]) {
   } else if (argc > 2) {
     op.rate = strtoul(argv[2], NULL, 0);
   }
 
   bool res = false;
   cubeb_client cl;
   cl.activate_log(CUBEB_LOG_DISABLED);
   fprintf(stderr, "Log level is DISABLED\n");
-  cl.init();
+  cl.init(/* default backend */);
 
   op.collection_device_type = CUBEB_DEVICE_TYPE_UNKNOWN;
   fprintf(stderr, "collection device type is UNKNOWN\n");
   if (op.pm == COLLECTION_CHANGE) {
     op.collection_device_type = CUBEB_DEVICE_TYPE_OUTPUT;
     fprintf(stderr, "collection device type changed to OUTPUT\n");
     res = cl.register_device_collection_changed(op.collection_device_type);
     if (res) {
--- a/third_party/rust/cubeb-sys/src/device.rs
+++ b/third_party/rust/cubeb-sys/src/device.rs
@@ -22,21 +22,21 @@ pub const CUBEB_DEVICE_FMT_S16NE: cubeb_
 #[cfg(target_endian = "big")]
 pub const CUBEB_DEVICE_FMT_F32NE: cubeb_device_fmt = CUBEB_DEVICE_FMT_F32BE;
 #[cfg(target_endian = "little")]
 pub const CUBEB_DEVICE_FMT_S16NE: cubeb_device_fmt = CUBEB_DEVICE_FMT_S16LE;
 #[cfg(target_endian = "little")]
 pub const CUBEB_DEVICE_FMT_F32NE: cubeb_device_fmt = CUBEB_DEVICE_FMT_F32LE;
 
 pub const CUBEB_DEVICE_FMT_S16_MASK: cubeb_device_fmt =
-    (CUBEB_DEVICE_FMT_S16LE | CUBEB_DEVICE_FMT_S16BE);
+    CUBEB_DEVICE_FMT_S16LE | CUBEB_DEVICE_FMT_S16BE;
 pub const CUBEB_DEVICE_FMT_F32_MASK: cubeb_device_fmt =
-    (CUBEB_DEVICE_FMT_F32LE | CUBEB_DEVICE_FMT_F32BE);
+    CUBEB_DEVICE_FMT_F32LE | CUBEB_DEVICE_FMT_F32BE;
 pub const CUBEB_DEVICE_FMT_ALL: cubeb_device_fmt =
-    (CUBEB_DEVICE_FMT_S16_MASK | CUBEB_DEVICE_FMT_F32_MASK);
+    CUBEB_DEVICE_FMT_S16_MASK | CUBEB_DEVICE_FMT_F32_MASK;
 
 cubeb_enum! {
     pub enum cubeb_device_pref  {
         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,
--- a/third_party/rust/cubeb-sys/src/stream.rs
+++ b/third_party/rust/cubeb-sys/src/stream.rs
@@ -61,16 +61,17 @@ impl fmt::Debug for cubeb_stream_params 
 
 extern "C" {
     pub fn cubeb_stream_destroy(stream: *mut cubeb_stream);
     pub fn cubeb_stream_start(stream: *mut cubeb_stream) -> c_int;
     pub fn cubeb_stream_stop(stream: *mut cubeb_stream) -> c_int;
     pub fn cubeb_stream_reset_default_device(stream: *mut cubeb_stream) -> c_int;
     pub fn cubeb_stream_get_position(stream: *mut cubeb_stream, position: *mut u64) -> c_int;
     pub fn cubeb_stream_get_latency(stream: *mut cubeb_stream, latency: *mut c_uint) -> c_int;
+    pub fn cubeb_stream_get_input_latency(stream: *mut cubeb_stream, latency: *mut c_uint) -> c_int;
     pub fn cubeb_stream_set_volume(stream: *mut cubeb_stream, volume: c_float) -> c_int;
     pub fn cubeb_stream_get_current_device(
         stream: *mut cubeb_stream,
         device: *mut *mut cubeb_device,
     ) -> c_int;
     pub fn cubeb_stream_device_destroy(
         stream: *mut cubeb_stream,
         devices: *mut cubeb_device,
--- a/third_party/rust/cubeb/.cargo-checksum.json
+++ b/third_party/rust/cubeb/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{"Cargo.toml":"3f16c9c35795e9a05783518797fba3fd031ae46b61232ce0c05e13e5203506d7","LICENSE":"8c044baa5d883274736eeece0b955249076c2697b826e576fce59496235b2cf5","README.md":"408c573ec240927cf5b9c036098e94e374ec41f71991415422586f450586b214","examples/common/mod.rs":"a5e1b79fc2b4addff1e442879ba3dbcb1cf5973e76b9a62d97dd0042597480db","examples/devices.rs":"89e13542853995d1ae4a49d6829156efb29dd25c6caffdf22496c28c8263ffeb","examples/tone.rs":"8f5f9851b6d99f6f16c597fcb9312e3ef81769cbfb89341d2ea2522ca2e2214e","src/context.rs":"03511fa960a411728163e700edc2fd6cfbfcf09766ffe62ee82a2cbd08fdf243","src/frame.rs":"ed1e8f4576022d0c23106bb115125e5a2967b0375a10d0c54bbe99f04a70cc3f","src/lib.rs":"98e9280890551ac9305f2f808e315b6aa6bcd5781b8e96a078787ded0ef91e2a","src/log.rs":"704faeb31934dad6bc6d02e01caa85118754209bd559d30d03fcfa5cb8c1603c","src/sample.rs":"e23be3b691052001916f920ce9c1a0051bd097e39c9d34cbcb80ab8120265f45","src/stream.rs":"3ee0432f655cd42959cd5d8e75cb4fe2322e1f88fa5d9cc83e615ae229cdeb8a"},"package":"3cbcdfde9ea319160af6eff068ffaa96aad3532e1b5c0ebc134614cfacacae24"}
\ No newline at end of file
+{"files":{"Cargo.lock":"8768f2709f7b520aa3bd0973a03333a1880eb65625af912514d1a26b712326fe","Cargo.toml":"28be23f743402b5e1b63a04246aeb186cdae205bf253617ef4bab42408ee1263","LICENSE":"8c044baa5d883274736eeece0b955249076c2697b826e576fce59496235b2cf5","README.md":"408c573ec240927cf5b9c036098e94e374ec41f71991415422586f450586b214","examples/common/mod.rs":"a5e1b79fc2b4addff1e442879ba3dbcb1cf5973e76b9a62d97dd0042597480db","examples/devices.rs":"ff5dcd588e7036165c4b4c20ec355d036e0ae90cf88b3b0f5cd86621fe2ce61d","examples/tone.rs":"8f5f9851b6d99f6f16c597fcb9312e3ef81769cbfb89341d2ea2522ca2e2214e","src/context.rs":"72507f5338a2f520fef9e2eface0638afba4c0d9e87fde92a0aaade643ba1335","src/frame.rs":"ed1e8f4576022d0c23106bb115125e5a2967b0375a10d0c54bbe99f04a70cc3f","src/lib.rs":"98e9280890551ac9305f2f808e315b6aa6bcd5781b8e96a078787ded0ef91e2a","src/log.rs":"704faeb31934dad6bc6d02e01caa85118754209bd559d30d03fcfa5cb8c1603c","src/sample.rs":"e23be3b691052001916f920ce9c1a0051bd097e39c9d34cbcb80ab8120265f45","src/stream.rs":"b3babf86252cd19cfbc98ffbc8f48bb033284f89db9cbdc46836611893356eff"},"package":"1116606d6045c9199f6a1e082f3cf63383ba6f9961339701faa6370dcf73135f"}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/third_party/rust/cubeb/Cargo.lock
@@ -0,0 +1,55 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+[[package]]
+name = "bitflags"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
+
+[[package]]
+name = "cc"
+version = "1.0.52"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3d87b23d6a92cd03af510a5ade527033f6aa6fa92161e2d5863a907d4c5e31d"
+
+[[package]]
+name = "cmake"
+version = "0.1.42"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81fb25b677f8bf1eb325017cb6bb8452f87969db0fedb4f757b297bee78a7c62"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "cubeb"
+version = "0.7.0"
+dependencies = [
+ "cubeb-core",
+]
+
+[[package]]
+name = "cubeb-core"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7c55529b8f47926e4242e1fc01d31b08a5a4847967c5c250644e33fe237cfe5"
+dependencies = [
+ "bitflags",
+ "cubeb-sys",
+]
+
+[[package]]
+name = "cubeb-sys"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcbc562eb6ccf62abacf9e3eebce992e5c36b230ca313ebd7c2d7d0e99deae90"
+dependencies = [
+ "cmake",
+ "pkg-config",
+]
+
+[[package]]
+name = "pkg-config"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677"
--- a/third_party/rust/cubeb/Cargo.toml
+++ b/third_party/rust/cubeb/Cargo.toml
@@ -7,24 +7,24 @@
 #
 # If you believe there's an error in this file please file an
 # issue against the rust-lang/cargo repository. If you're
 # editing this file be aware that the upstream Cargo.toml
 # will likely look very different (and much more reasonable)
 
 [package]
 name = "cubeb"
-version = "0.6.2"
+version = "0.7.0"
 authors = ["Dan Glastonbury <dglastonbury@mozilla.com>"]
 description = "Bindings to libcubeb for interacting with system audio from rust.\n"
 homepage = "https://github.com/djg/cubeb-rs"
 readme = "README.md"
 keywords = ["cubeb"]
 categories = ["api-bindings"]
 license = "ISC"
 repository = "https://github.com/djg/cubeb-rs"
 [dependencies.cubeb-core]
-version = "0.6.2"
+version = "0.7.0"
 
 [features]
 gecko-in-tree = ["cubeb-core/gecko-in-tree"]
 [badges.circle-ci]
 repository = "djg/cubeb-rs"
--- a/third_party/rust/cubeb/examples/devices.rs
+++ b/third_party/rust/cubeb/examples/devices.rs
@@ -6,17 +6,16 @@
 
 //! libcubeb enumerate device test/example.
 //! Prints out a list of input/output devices connected to the system.
 extern crate cubeb;
 
 mod common;
 
 use cubeb::{DeviceFormat, DeviceType};
-use std::error::Error;
 
 fn print_device_info(info: &cubeb::DeviceInfo) {
     let devtype = if info.device_type().contains(DeviceType::INPUT) {
         "input"
     } else if info.device_type().contains(DeviceType::OUTPUT) {
         "output"
     } else {
         "unknown?"
@@ -96,17 +95,17 @@ fn main() {
 
     let devices = match ctx.enumerate_devices(DeviceType::INPUT) {
         Ok(devices) => devices,
         Err(e) if e.code() == cubeb::ErrorCode::NotSupported => {
             println!("Device enumeration not support for this backend.");
             return;
         }
         Err(e) => {
-            println!("Error enumerating devices: {}", e.description());
+            println!("Error enumerating devices: {}", e);
             return;
         }
     };
 
     println!("Found {} input devices", devices.len());
     for d in devices.iter() {
         print_device_info(d);
     }
@@ -114,17 +113,17 @@ fn main() {
     println!(
         "Enumerating output devices for backend {}",
         ctx.backend_id()
     );
 
     let devices = match ctx.enumerate_devices(DeviceType::OUTPUT) {
         Ok(devices) => devices,
         Err(e) => {
-            println!("Error enumerating devices: {}", e.description());
+            println!("Error enumerating devices: {}", e);
             return;
         }
     };
 
     println!("Found {} output devices", devices.len());
     for d in devices.iter() {
         print_device_info(d);
     }
--- a/third_party/rust/cubeb/src/context.rs
+++ b/third_party/rust/cubeb/src/context.rs
@@ -1,8 +1,8 @@
 use {Context, Result};
 use std::ffi::CString;
 
 pub fn init<T: Into<Vec<u8>>>(name: T) -> Result<Context> {
-    let name = try!(CString::new(name));
+    let name = CString::new(name)?;
 
     Context::init(Some(name.as_c_str()), None)
 }
--- a/third_party/rust/cubeb/src/stream.rs
+++ b/third_party/rust/cubeb/src/stream.rs
@@ -63,19 +63,19 @@ use cubeb_core;
 use ffi;
 use std::{ops, panic, ptr};
 use std::ffi::CString;
 use std::marker::PhantomData;
 use std::mem::ManuallyDrop;
 use std::os::raw::{c_long, c_void};
 use std::slice::{from_raw_parts, from_raw_parts_mut};
 
-pub type DataCallback<F> = FnMut(&[F], &mut [F]) -> isize + Send + Sync + 'static;
-pub type StateCallback = FnMut(State) + Send + Sync + 'static;
-pub type DeviceChangedCallback = FnMut() + Send + Sync + 'static;
+pub type DataCallback<F> = dyn FnMut(&[F], &mut [F]) -> isize + Send + Sync + 'static;
+pub type StateCallback = dyn FnMut(State) + Send + Sync + 'static;
+pub type DeviceChangedCallback = dyn FnMut() + Send + Sync + 'static;
 
 pub struct StreamCallbacks<F> {
     pub(crate) data: Box<DataCallback<F>>,
     pub(crate) state: Box<StateCallback>,
     pub(crate) device_changed: Option<Box<DeviceChangedCallback>>,
 }
 
 pub struct Stream<F>(ManuallyDrop<cubeb_core::Stream>,
@@ -196,32 +196,32 @@ impl<'a, F> StreamBuilder<'a, F> {
             self.input.map_or((ptr::null(), None), |x| (x.0, Some(x.1)));
         let (output_device, output_stream_params) = self.output
             .map_or((ptr::null(), None), |x| (x.0, Some(x.1)));
         let latency = self.latency.unwrap_or(1);
         let data_callback: ffi::cubeb_data_callback = Some(data_cb_c::<F>);
         let state_callback: ffi::cubeb_state_callback = Some(state_cb_c::<F>);
 
         let stream = unsafe {
-            try!(ctx.stream_init(
+            ctx.stream_init(
                 stream_name,
                 input_device,
                 input_stream_params,
                 output_device,
                 output_stream_params,
                 latency,
                 data_callback,
                 state_callback,
                 cbs as *mut _
-            ))
+            )?
         };
         if has_device_changed {
             let device_changed_callback: ffi::cubeb_device_changed_callback =
                 Some(device_changed_cb_c::<F>);
-            try!(stream.register_device_changed_callback(device_changed_callback));
+            stream.register_device_changed_callback(device_changed_callback)?;
         }
         Ok(Stream::new(stream))
     }
 }
 
 // C callable callbacks
 unsafe extern "C" fn data_cb_c<F>(
     _: *mut ffi::cubeb_stream,