summaryrefslogtreecommitdiffstats
path: root/third_party/rust/audio-mixer
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/audio-mixer')
-rw-r--r--third_party/rust/audio-mixer/.cargo-checksum.json1
-rw-r--r--third_party/rust/audio-mixer/Cargo.lock603
-rw-r--r--third_party/rust/audio-mixer/Cargo.toml42
-rw-r--r--third_party/rust/audio-mixer/LICENSE373
-rw-r--r--third_party/rust/audio-mixer/README.md39
-rw-r--r--third_party/rust/audio-mixer/benches/benchmark.rs95
-rw-r--r--third_party/rust/audio-mixer/install_rustfmt_clippy.sh17
-rwxr-xr-xthird_party/rust/audio-mixer/run_sanitizers.sh53
-rwxr-xr-xthird_party/rust/audio-mixer/run_tests.sh17
-rw-r--r--third_party/rust/audio-mixer/src/channel.rs78
-rw-r--r--third_party/rust/audio-mixer/src/coefficient.rs756
-rw-r--r--third_party/rust/audio-mixer/src/lib.rs81
-rw-r--r--third_party/rust/audio-mixer/src/main.rs50
13 files changed, 2205 insertions, 0 deletions
diff --git a/third_party/rust/audio-mixer/.cargo-checksum.json b/third_party/rust/audio-mixer/.cargo-checksum.json
new file mode 100644
index 0000000000..4fbdd1518d
--- /dev/null
+++ b/third_party/rust/audio-mixer/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"Cargo.lock":"ac8d6a7af46ee8f72d22abb80575109b75bcb2a20ea6cacb6626a20f337fe51f","Cargo.toml":"9316753099606b9531ea52aec87270a83e74c15b6563f8f979b0e961e0cf73fd","LICENSE":"1f256ecad192880510e84ad60474eab7589218784b9a50bc7ceee34c2b91f1d5","README.md":"e2e6b7d80550743e01faa26c19f6c8520fd0dc5db68021c042fd523eed46c799","benches/benchmark.rs":"33e9d85cee38fb7752e9315ccf5f816179309062bfb78118288f7e1495e3616f","install_rustfmt_clippy.sh":"4ae90d8dcb9757cb3ae4ae142ef80e5377c0dde61c63f4a3c32418646e80ca7b","run_sanitizers.sh":"e31b4a11559b52043920c63e6ad4e57051d6205883ecf2b2c48e105ba522015b","run_tests.sh":"21ca30cb6d41c6b35e6207e3283292933ef6628575dd587287fad6db2afb22bd","src/channel.rs":"d948d890219b810632277d2b04297334a47dc6639ef93309e74495ff408f82b6","src/coefficient.rs":"24f56281a1b800769feefc735d77c2ec03f1aa8d7c58c256b25ce1a9053a06cc","src/lib.rs":"f0ee59ac224ce6ba0cc6cb9e1796e85b9b751ba4d92c58fe3fb8b10c95b72cc4","src/main.rs":"b5ba05638c727040581c49ffd2d784493af658495cd424ce8c9365f779994baf"},"package":"c41dc008c1973ce58ff3cfc52df53814a9b7b78d73d95b071b5ff0ed4b2db3e1"} \ No newline at end of file
diff --git a/third_party/rust/audio-mixer/Cargo.lock b/third_party/rust/audio-mixer/Cargo.lock
new file mode 100644
index 0000000000..68d1ba2441
--- /dev/null
+++ b/third_party/rust/audio-mixer/Cargo.lock
@@ -0,0 +1,603 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f2135563fb5c609d2b2b87c1e8ce7bc41b0b45430fa9661f457981503dd5bf0"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "atty"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+dependencies = [
+ "hermit-abi 0.1.19",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "audio-mixer"
+version = "0.1.3"
+dependencies = [
+ "bitflags",
+ "criterion",
+ "float-cmp",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bumpalo"
+version = "3.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
+
+[[package]]
+name = "cast"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "clap"
+version = "2.34.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
+dependencies = [
+ "bitflags",
+ "textwrap",
+ "unicode-width",
+]
+
+[[package]]
+name = "criterion"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f"
+dependencies = [
+ "atty",
+ "cast",
+ "clap",
+ "criterion-plot",
+ "csv",
+ "itertools",
+ "lazy_static",
+ "num-traits",
+ "oorandom",
+ "plotters",
+ "rayon",
+ "regex",
+ "serde",
+ "serde_cbor",
+ "serde_derive",
+ "serde_json",
+ "tinytemplate",
+ "walkdir",
+]
+
+[[package]]
+name = "criterion-plot"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876"
+dependencies = [
+ "cast",
+ "itertools",
+]
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
+dependencies = [
+ "cfg-if",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
+dependencies = [
+ "cfg-if",
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
+dependencies = [
+ "autocfg",
+ "cfg-if",
+ "crossbeam-utils",
+ "memoffset",
+ "scopeguard",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "csv"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "626ae34994d3d8d668f4269922248239db4ae42d538b14c398b74a52208e8086"
+dependencies = [
+ "csv-core",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "csv-core"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "either"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
+
+[[package]]
+name = "float-cmp"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da62c4f1b81918835a8c6a484a397775fff5953fe83529afd51b05f5c6a6617d"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "half"
+version = "1.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
+
+[[package]]
+name = "hermit-abi"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
+
+[[package]]
+name = "itertools"
+version = "0.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
+
+[[package]]
+name = "js-sys"
+version = "0.3.64"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.148"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
+
+[[package]]
+name = "log"
+version = "0.4.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
+
+[[package]]
+name = "memchr"
+version = "2.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c"
+
+[[package]]
+name = "memoffset"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
+dependencies = [
+ "hermit-abi 0.3.2",
+ "libc",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+
+[[package]]
+name = "oorandom"
+version = "11.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
+
+[[package]]
+name = "plotters"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45"
+dependencies = [
+ "num-traits",
+ "plotters-backend",
+ "plotters-svg",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "plotters-backend"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609"
+
+[[package]]
+name = "plotters-svg"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab"
+dependencies = [
+ "plotters-backend",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.67"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rayon"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
+dependencies = [
+ "crossbeam-channel",
+ "crossbeam-deque",
+ "crossbeam-utils",
+ "num_cpus",
+]
+
+[[package]]
+name = "regex"
+version = "1.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
+
+[[package]]
+name = "ryu"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "serde"
+version = "1.0.188"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_cbor"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5"
+dependencies = [
+ "half",
+ "serde",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.188"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.107"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "textwrap"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
+dependencies = [
+ "unicode-width",
+]
+
+[[package]]
+name = "tinytemplate"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
+dependencies = [
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
+
+[[package]]
+name = "walkdir"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
+
+[[package]]
+name = "web-sys"
+version = "0.3.64"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
diff --git a/third_party/rust/audio-mixer/Cargo.toml b/third_party/rust/audio-mixer/Cargo.toml
new file mode 100644
index 0000000000..92293f25db
--- /dev/null
+++ b/third_party/rust/audio-mixer/Cargo.toml
@@ -0,0 +1,42 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2018"
+name = "audio-mixer"
+version = "0.1.3"
+authors = ["Chun-Min Chang <chun.m.chang@gmail.com>"]
+description = "Mixing audio by the input and output channel layout"
+readme = "README.md"
+keywords = [
+ "audio",
+ "mixer",
+ "mixing",
+]
+categories = ["multimedia::audio"]
+license = "MPL-2.0"
+repository = "https://github.com/mozilla/audio-mixer"
+
+[[bench]]
+name = "benchmark"
+harness = false
+
+[dependencies.bitflags]
+version = "1.3"
+
+[dev-dependencies.criterion]
+version = "0.3"
+
+[dev-dependencies.float-cmp]
+version = "0.6"
+
+[badges.travis-ci]
+repository = "mozilla/audio-mixer"
diff --git a/third_party/rust/audio-mixer/LICENSE b/third_party/rust/audio-mixer/LICENSE
new file mode 100644
index 0000000000..a612ad9813
--- /dev/null
+++ b/third_party/rust/audio-mixer/LICENSE
@@ -0,0 +1,373 @@
+Mozilla Public License Version 2.0
+==================================
+
+1. Definitions
+--------------
+
+1.1. "Contributor"
+ means each individual or legal entity that creates, contributes to
+ the creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+ means the combination of the Contributions of others (if any) used
+ by a Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+ means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+ means Source Code Form to which the initial Contributor has attached
+ the notice in Exhibit A, the Executable Form of such Source Code
+ Form, and Modifications of such Source Code Form, in each case
+ including portions thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+ means
+
+ (a) that the initial Contributor has attached the notice described
+ in Exhibit B to the Covered Software; or
+
+ (b) that the Covered Software was made available under the terms of
+ version 1.1 or earlier of the License, but not also under the
+ terms of a Secondary License.
+
+1.6. "Executable Form"
+ means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+ means a work that combines Covered Software with other material, in
+ a separate file or files, that is not Covered Software.
+
+1.8. "License"
+ means this document.
+
+1.9. "Licensable"
+ means having the right to grant, to the maximum extent possible,
+ whether at the time of the initial grant or subsequently, any and
+ all of the rights conveyed by this License.
+
+1.10. "Modifications"
+ means any of the following:
+
+ (a) any file in Source Code Form that results from an addition to,
+ deletion from, or modification of the contents of Covered
+ Software; or
+
+ (b) any new file in Source Code Form that contains any Covered
+ Software.
+
+1.11. "Patent Claims" of a Contributor
+ means any patent claim(s), including without limitation, method,
+ process, and apparatus claims, in any patent Licensable by such
+ Contributor that would be infringed, but for the grant of the
+ License, by the making, using, selling, offering for sale, having
+ made, import, or transfer of either its Contributions or its
+ Contributor Version.
+
+1.12. "Secondary License"
+ means either the GNU General Public License, Version 2.0, the GNU
+ Lesser General Public License, Version 2.1, the GNU Affero General
+ Public License, Version 3.0, or any later versions of those
+ licenses.
+
+1.13. "Source Code Form"
+ means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+ means an individual or a legal entity exercising rights under this
+ License. For legal entities, "You" includes any entity that
+ controls, is controlled by, or is under common control with You. For
+ purposes of this definition, "control" means (a) the power, direct
+ or indirect, to cause the direction or management of such entity,
+ whether by contract or otherwise, or (b) ownership of more than
+ fifty percent (50%) of the outstanding shares or beneficial
+ ownership of such entity.
+
+2. License Grants and Conditions
+--------------------------------
+
+2.1. Grants
+
+Each Contributor hereby grants You a world-wide, royalty-free,
+non-exclusive license:
+
+(a) under intellectual property rights (other than patent or trademark)
+ Licensable by such Contributor to use, reproduce, make available,
+ modify, display, perform, distribute, and otherwise exploit its
+ Contributions, either on an unmodified basis, with Modifications, or
+ as part of a Larger Work; and
+
+(b) under Patent Claims of such Contributor to make, use, sell, offer
+ for sale, have made, import, and otherwise transfer either its
+ Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+The licenses granted in Section 2.1 with respect to any Contribution
+become effective for each Contribution on the date the Contributor first
+distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+The licenses granted in this Section 2 are the only rights granted under
+this License. No additional rights or licenses will be implied from the
+distribution or licensing of Covered Software under this License.
+Notwithstanding Section 2.1(b) above, no patent license is granted by a
+Contributor:
+
+(a) for any code that a Contributor has removed from Covered Software;
+ or
+
+(b) for infringements caused by: (i) Your and any other third party's
+ modifications of Covered Software, or (ii) the combination of its
+ Contributions with other software (except as part of its Contributor
+ Version); or
+
+(c) under Patent Claims infringed by Covered Software in the absence of
+ its Contributions.
+
+This License does not grant any rights in the trademarks, service marks,
+or logos of any Contributor (except as may be necessary to comply with
+the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+No Contributor makes additional grants as a result of Your choice to
+distribute the Covered Software under a subsequent version of this
+License (see Section 10.2) or under the terms of a Secondary License (if
+permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+Each Contributor represents that the Contributor believes its
+Contributions are its original creation(s) or it has sufficient rights
+to grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+This License is not intended to limit any rights You have under
+applicable copyright doctrines of fair use, fair dealing, or other
+equivalents.
+
+2.7. Conditions
+
+Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
+in Section 2.1.
+
+3. Responsibilities
+-------------------
+
+3.1. Distribution of Source Form
+
+All distribution of Covered Software in Source Code Form, including any
+Modifications that You create or to which You contribute, must be under
+the terms of this License. You must inform recipients that the Source
+Code Form of the Covered Software is governed by the terms of this
+License, and how they can obtain a copy of this License. You may not
+attempt to alter or restrict the recipients' rights in the Source Code
+Form.
+
+3.2. Distribution of Executable Form
+
+If You distribute Covered Software in Executable Form then:
+
+(a) such Covered Software must also be made available in Source Code
+ Form, as described in Section 3.1, and You must inform recipients of
+ the Executable Form how they can obtain a copy of such Source Code
+ Form by reasonable means in a timely manner, at a charge no more
+ than the cost of distribution to the recipient; and
+
+(b) You may distribute such Executable Form under the terms of this
+ License, or sublicense it under different terms, provided that the
+ license for the Executable Form does not attempt to limit or alter
+ the recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+You may create and distribute a Larger Work under terms of Your choice,
+provided that You also comply with the requirements of this License for
+the Covered Software. If the Larger Work is a combination of Covered
+Software with a work governed by one or more Secondary Licenses, and the
+Covered Software is not Incompatible With Secondary Licenses, this
+License permits You to additionally distribute such Covered Software
+under the terms of such Secondary License(s), so that the recipient of
+the Larger Work may, at their option, further distribute the Covered
+Software under the terms of either this License or such Secondary
+License(s).
+
+3.4. Notices
+
+You may not remove or alter the substance of any license notices
+(including copyright notices, patent notices, disclaimers of warranty,
+or limitations of liability) contained within the Source Code Form of
+the Covered Software, except that You may alter any license notices to
+the extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+You may choose to offer, and to charge a fee for, warranty, support,
+indemnity or liability obligations to one or more recipients of Covered
+Software. However, You may do so only on Your own behalf, and not on
+behalf of any Contributor. You must make it absolutely clear that any
+such warranty, support, indemnity, or liability obligation is offered by
+You alone, and You hereby agree to indemnify every Contributor for any
+liability incurred by such Contributor as a result of warranty, support,
+indemnity or liability terms You offer. You may include additional
+disclaimers of warranty and limitations of liability specific to any
+jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+---------------------------------------------------
+
+If it is impossible for You to comply with any of the terms of this
+License with respect to some or all of the Covered Software due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description must
+be placed in a text file included with all distributions of the Covered
+Software under this License. Except to the extent prohibited by statute
+or regulation, such description must be sufficiently detailed for a
+recipient of ordinary skill to be able to understand it.
+
+5. Termination
+--------------
+
+5.1. The rights granted under this License will terminate automatically
+if You fail to comply with any of its terms. However, if You become
+compliant, then the rights granted under this License from a particular
+Contributor are reinstated (a) provisionally, unless and until such
+Contributor explicitly and finally terminates Your grants, and (b) on an
+ongoing basis, if such Contributor fails to notify You of the
+non-compliance by some reasonable means prior to 60 days after You have
+come back into compliance. Moreover, Your grants from a particular
+Contributor are reinstated on an ongoing basis if such Contributor
+notifies You of the non-compliance by some reasonable means, this is the
+first time You have received notice of non-compliance with this License
+from such Contributor, and You become compliant prior to 30 days after
+Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+infringement claim (excluding declaratory judgment actions,
+counter-claims, and cross-claims) alleging that a Contributor Version
+directly or indirectly infringes any patent, then the rights granted to
+You by any and all Contributors for the Covered Software under Section
+2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all
+end user license agreements (excluding distributors and resellers) which
+have been validly granted by You or Your distributors under this License
+prior to termination shall survive termination.
+
+************************************************************************
+* *
+* 6. Disclaimer of Warranty *
+* ------------------------- *
+* *
+* Covered Software is provided under this License on an "as is" *
+* basis, without warranty of any kind, either expressed, implied, or *
+* statutory, including, without limitation, warranties that the *
+* Covered Software is free of defects, merchantable, fit for a *
+* particular purpose or non-infringing. The entire risk as to the *
+* quality and performance of the Covered Software is with You. *
+* Should any Covered Software prove defective in any respect, You *
+* (not any Contributor) assume the cost of any necessary servicing, *
+* repair, or correction. This disclaimer of warranty constitutes an *
+* essential part of this License. No use of any Covered Software is *
+* authorized under this License except under this disclaimer. *
+* *
+************************************************************************
+
+************************************************************************
+* *
+* 7. Limitation of Liability *
+* -------------------------- *
+* *
+* Under no circumstances and under no legal theory, whether tort *
+* (including negligence), contract, or otherwise, shall any *
+* Contributor, or anyone who distributes Covered Software as *
+* permitted above, be liable to You for any direct, indirect, *
+* special, incidental, or consequential damages of any character *
+* including, without limitation, damages for lost profits, loss of *
+* goodwill, work stoppage, computer failure or malfunction, or any *
+* and all other commercial damages or losses, even if such party *
+* shall have been informed of the possibility of such damages. This *
+* limitation of liability shall not apply to liability for death or *
+* personal injury resulting from such party's negligence to the *
+* extent applicable law prohibits such limitation. Some *
+* jurisdictions do not allow the exclusion or limitation of *
+* incidental or consequential damages, so this exclusion and *
+* limitation may not apply to You. *
+* *
+************************************************************************
+
+8. Litigation
+-------------
+
+Any litigation relating to this License may be brought only in the
+courts of a jurisdiction where the defendant maintains its principal
+place of business and such litigation shall be governed by laws of that
+jurisdiction, without reference to its conflict-of-law provisions.
+Nothing in this Section shall prevent a party's ability to bring
+cross-claims or counter-claims.
+
+9. Miscellaneous
+----------------
+
+This License represents the complete agreement concerning the subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. Any law or regulation which provides
+that the language of a contract shall be construed against the drafter
+shall not be used to construe this License against a Contributor.
+
+10. Versions of the License
+---------------------------
+
+10.1. New Versions
+
+Mozilla Foundation is the license steward. Except as provided in Section
+10.3, no one other than the license steward has the right to modify or
+publish new versions of this License. Each version will be given a
+distinguishing version number.
+
+10.2. Effect of New Versions
+
+You may distribute the Covered Software under the terms of the version
+of the License under which You originally received the Covered Software,
+or under the terms of any subsequent version published by the license
+steward.
+
+10.3. Modified Versions
+
+If you create software not governed by this License, and you want to
+create a new license for such software, you may create and use a
+modified version of this License if you rename the license and remove
+any references to the name of the license steward (except to note that
+such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+Licenses
+
+If You choose to distribute Source Code Form that is Incompatible With
+Secondary Licenses under the terms of this version of the License, the
+notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+-------------------------------------------
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular
+file, then You may include the notice in a location (such as a LICENSE
+file in a relevant directory) where a recipient would be likely to look
+for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+---------------------------------------------------------
+
+ This Source Code Form is "Incompatible With Secondary Licenses", as
+ defined by the Mozilla Public License, v. 2.0.
diff --git a/third_party/rust/audio-mixer/README.md b/third_party/rust/audio-mixer/README.md
new file mode 100644
index 0000000000..c8a649e07d
--- /dev/null
+++ b/third_party/rust/audio-mixer/README.md
@@ -0,0 +1,39 @@
+# Audio Mixer
+
+[![CircleCI](https://circleci.com/gh/mozilla/audio-mixer.svg?style=svg)](https://circleci.com/gh/mozilla/audio-mixer)
+[![Build & Test](https://github.com/mozilla/audio-mixer/actions/workflows/test.yml/badge.svg)](https://github.com/mozilla/audio-mixer/actions/workflows/test.yml)
+
+Mixing audio data from any input channel layout to any output channel layout,
+in a *matrix-multiplication* form.
+
+```
+output channel #1 ▸ │ Silence │ │ 0, 0, 0, 0 │ │ FrontRight │ ◂ input channel #1
+output channel #2 ▸ │ FrontRight │ = │ R, C, 0, F │ x │ FrontCenter │ ◂ input channel #2
+output channel #3 ▸ │ FrontLeft │ │ 0, C, L, F │ │ FrontLeft │ ◂ input channel #3
+ ▴ ▴ │ LowFrequency │ ◂ input channel #4
+ ┊ ┊ ▴
+ ┊ ┊ ┊
+ out_audio mixing matrix m in_audio
+```
+
+For example, the above means there are 3 output channels and 4 input channels.
+The order of output channels is _Silence, FrontRight, and FrontLeft_.
+The order of input channels is _FrontRight, FrontCenter, FrontLeft, LowFrequency_.
+
+So the output data in the _channel #2_ will be:
+```
+
+Output data of ch #2 (FrontRight) =
+ R x input channel #1 (FrontRight) +
+ C x input channel #2 (FrontCenter) +
+ 0 x input channel #3 (FrontLeft) +
+ F x input channel #4 (LowFrequency)
+```
+
+where the _C, F, L, R_ are mixing coefficients.
+The _Silence_ channel is a unused channel in the output device,
+so its channel data will always be zero.
+
+## License
+
+MPL-2
diff --git a/third_party/rust/audio-mixer/benches/benchmark.rs b/third_party/rust/audio-mixer/benches/benchmark.rs
new file mode 100644
index 0000000000..11c47e55c7
--- /dev/null
+++ b/third_party/rust/audio-mixer/benches/benchmark.rs
@@ -0,0 +1,95 @@
+use audio_mixer::{Channel, Mixer};
+use criterion::{black_box, criterion_group, criterion_main, Criterion};
+
+use std::any::{Any, TypeId};
+
+fn criterion_benchmark(c: &mut Criterion) {
+ let frames = 512;
+ c.bench_function("downmix_f32", |b| {
+ b.iter(|| downmix::<f32>(black_box(frames)))
+ });
+ c.bench_function("downmix_i16", |b| {
+ b.iter(|| downmix::<i16>(black_box(frames)))
+ });
+ c.bench_function("upmix_f32", |b| b.iter(|| upmix::<f32>(black_box(frames))));
+ c.bench_function("upmix_i16", |b| b.iter(|| upmix::<i16>(black_box(frames))));
+}
+
+fn downmix<T>(frames: usize)
+where
+ T: Clone + Default + From<u8> + ?Sized + Any,
+{
+ // Downmix from 5.1 to stereo.
+ let input_channels = [
+ Channel::FrontLeft,
+ Channel::FrontRight,
+ Channel::FrontCenter,
+ Channel::LowFrequency,
+ Channel::BackLeft,
+ Channel::BackRight,
+ ];
+ let output_channels = [Channel::FrontLeft, Channel::FrontRight];
+ mix::<T>(&input_channels, &output_channels, frames);
+}
+
+fn upmix<T>(frames: usize)
+where
+ T: Clone + Default + From<u8> + ?Sized + Any,
+{
+ // upmix from mono to stereo.
+ let input_channels = [Channel::FrontCenter];
+ let output_channels = [Channel::FrontLeft, Channel::FrontRight];
+ mix::<T>(&input_channels, &output_channels, frames);
+}
+
+fn mix<T>(input_channels: &[Channel], output_channels: &[Channel], frames: usize)
+where
+ T: Clone + Default + From<u8> + ?Sized + Any,
+{
+ if TypeId::of::<T>() == TypeId::of::<f32>() {
+ let (input_buffer, mut output_buffer) = create_buffers::<f32>(
+ input_channels.len() * frames,
+ output_channels.len() * frames,
+ );
+ let mut in_buf = input_buffer.chunks(input_channels.len());
+ let mut out_buf = output_buffer.chunks_mut(output_channels.len());
+ let mixer = Mixer::<f32>::new(input_channels, output_channels);
+ for _ in 0..frames {
+ mixer.mix(in_buf.next().unwrap(), out_buf.next().unwrap());
+ }
+ } else if TypeId::of::<T>() == TypeId::of::<i16>() {
+ let (input_buffer, mut output_buffer) = create_buffers::<i16>(
+ input_channels.len() * frames,
+ output_channels.len() * frames,
+ );
+ let mut in_buf = input_buffer.chunks(input_channels.len());
+ let mut out_buf = output_buffer.chunks_mut(output_channels.len());
+ let mixer = Mixer::<i16>::new(input_channels, output_channels);
+ for _ in 0..frames {
+ mixer.mix(in_buf.next().unwrap(), out_buf.next().unwrap());
+ }
+ } else {
+ panic!("Unsupport type");
+ }
+}
+
+fn create_buffers<T: Clone + Default + From<u8>>(
+ input_size: usize,
+ output_size: usize,
+) -> (Vec<T>, Vec<T>) {
+ let mut input_buffer = default_buffer::<T>(input_size);
+ for (i, data) in input_buffer.iter_mut().enumerate() {
+ *data = T::from((i + 1) as u8);
+ }
+ let output_buffer = default_buffer::<T>(output_size);
+ (input_buffer, output_buffer)
+}
+
+fn default_buffer<T: Clone + Default>(size: usize) -> Vec<T> {
+ let mut v = Vec::with_capacity(size);
+ v.resize(size, T::default());
+ v
+}
+
+criterion_group!(benches, criterion_benchmark);
+criterion_main!(benches);
diff --git a/third_party/rust/audio-mixer/install_rustfmt_clippy.sh b/third_party/rust/audio-mixer/install_rustfmt_clippy.sh
new file mode 100644
index 0000000000..5063059c60
--- /dev/null
+++ b/third_party/rust/audio-mixer/install_rustfmt_clippy.sh
@@ -0,0 +1,17 @@
+# https://github.com/rust-lang/rustup-components-history/blob/master/README.md
+# https://github.com/rust-lang/rust-clippy/blob/27acea0a1baac6cf3ac6debfdbce04f91e15d772/.travis.yml#L40-L46
+if ! rustup component add rustfmt; then
+ TARGET=$(rustc -Vv | awk '/host/{print $2}')
+ NIGHTLY=$(curl -s "https://rust-lang.github.io/rustup-components-history/${TARGET}/rustfmt")
+ curl -sSL "https://static.rust-lang.org/dist/${NIGHTLY}/rustfmt-nightly-${TARGET}.tar.xz" | \
+ tar -xJf - --strip-components=3 -C ~/.cargo/bin
+ rm -rf ~/.cargo/bin/doc
+fi
+
+if ! rustup component add clippy; then
+ TARGET=$(rustc -Vv | awk '/host/{print $2}')
+ NIGHTLY=$(curl -s "https://rust-lang.github.io/rustup-components-history/${TARGET}/clippy")
+ rustup default nightly-${NIGHTLY}
+ rustup component add rustfmt
+ rustup component add clippy
+fi
diff --git a/third_party/rust/audio-mixer/run_sanitizers.sh b/third_party/rust/audio-mixer/run_sanitizers.sh
new file mode 100755
index 0000000000..7f73c2b9a0
--- /dev/null
+++ b/third_party/rust/audio-mixer/run_sanitizers.sh
@@ -0,0 +1,53 @@
+# The option `Z` is only accepted on the nightly compiler
+# so changing to nightly toolchain by `rustup default nightly` is required.
+
+# See: https://github.com/rust-lang/rust/issues/39699 for more sanitizer support.
+
+toolchain="$(rustup default)"
+echo -e "\nUse Rust toolchain: $toolchain"
+
+NIGHTLY_PREFIX="nightly"
+if [ ! -z "${toolchain##$NIGHTLY_PREFIX*}" ]; then
+ echo -e "The sanitizer is only available on Rust Nightly only. Skip."
+ exit
+fi
+
+if [ -z "${toolchain##*windows*}" ]; then
+ echo -e "The sanitizer doesn't work on Windows platform yet"
+ exit
+fi
+
+# Display verbose backtrace for debugging if backtrace is unset
+if [ -z "${RUST_BACKTRACE}" ]; then
+ export RUST_BACKTRACE=1
+fi
+echo -e "RUST_BACKTRACE is set to ${RUST_BACKTRACE}\n"
+
+# {Address, Thread}Sanitizer cannot build with `criterion` crate, so `criterion` will be removed
+# from the `Cargo.toml` temporarily during the sanitizer tests.
+ORIGINAL_MANIFEST="Cargo_ori.toml"
+mv Cargo.toml $ORIGINAL_MANIFEST
+# Delete lines that contain a `criterion` from Cargo.toml.
+sed '/criterion/d' $ORIGINAL_MANIFEST > Cargo.toml
+
+sanitizers=("address" "leak" "memory" "thread")
+for san in "${sanitizers[@]}"
+do
+ San="$(tr '[:lower:]' '[:upper:]' <<< ${san:0:1})${san:1}"
+ echo -e "\n\nRun ${San}Sanitizer\n------------------------------"
+ if [ $san = "leak" ]; then
+ echo -e "Skip the test that panics. Leaking memory when the program drops out abnormally is ok."
+ options="-- --Z unstable-options --exclude-should-panic"
+ fi
+ if [ $san = "memory" ]; then
+ if [ -z "${toolchain##*darwin*}" ]; then
+ echo -e "Skip the MemorySanitizer on Mac OS since it doesn't works with $toolchain"
+ else
+ echo -e "Skip the MemorySanitizer since it fails to run custom build command for bitflags crate"
+ fi
+ continue
+ fi
+ RUSTFLAGS="-Z sanitizer=${san}" cargo test $options
+done
+
+mv $ORIGINAL_MANIFEST Cargo.toml \ No newline at end of file
diff --git a/third_party/rust/audio-mixer/run_tests.sh b/third_party/rust/audio-mixer/run_tests.sh
new file mode 100755
index 0000000000..afb0f9ad04
--- /dev/null
+++ b/third_party/rust/audio-mixer/run_tests.sh
@@ -0,0 +1,17 @@
+# Bail out once getting an error.
+set -e
+
+# Display verbose backtrace for debugging if backtrace is unset
+if [ -z "${RUST_BACKTRACE}" ]; then
+ export RUST_BACKTRACE=1
+fi
+echo -e "RUST_BACKTRACE is set to ${RUST_BACKTRACE}\n"
+
+# Format check
+cargo fmt --all -- --check
+
+# Lints check
+cargo clippy --all-targets --all-features -- -D warnings
+
+# Regular Tests
+cargo test --verbose --all
diff --git a/third_party/rust/audio-mixer/src/channel.rs b/third_party/rust/audio-mixer/src/channel.rs
new file mode 100644
index 0000000000..b32936895e
--- /dev/null
+++ b/third_party/rust/audio-mixer/src/channel.rs
@@ -0,0 +1,78 @@
+// The number of channels must be unique and start from 0. They will be treated as indice in the
+// mixing matrix and used to form unique bitflags in the channel map, which is a bitmap.
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum Channel {
+ FrontLeft = 0,
+ FrontRight = 1,
+ FrontCenter = 2,
+ LowFrequency = 3,
+ BackLeft = 4,
+ BackRight = 5,
+ FrontLeftOfCenter = 6,
+ FrontRightOfCenter = 7,
+ BackCenter = 8,
+ SideLeft = 9,
+ SideRight = 10,
+ TopCenter = 11,
+ TopFrontLeft = 12,
+ TopFrontCenter = 13,
+ TopFrontRight = 14,
+ TopBackLeft = 15,
+ TopBackCenter = 16,
+ TopBackRight = 17,
+ Silence = 18,
+}
+
+impl Channel {
+ pub const fn number(self) -> usize {
+ self as usize
+ }
+
+ pub const fn count() -> usize {
+ Channel::Silence as usize + 1
+ }
+
+ pub const fn bitmask(self) -> u32 {
+ 1 << self as usize
+ }
+}
+
+bitflags! {
+ pub struct ChannelMap: u32 {
+ const FRONT_LEFT = Channel::FrontLeft.bitmask();
+ const FRONT_RIGHT = Channel::FrontRight.bitmask();
+ const FRONT_CENTER = Channel::FrontCenter.bitmask();
+ const LOW_FREQUENCY = Channel::LowFrequency.bitmask();
+ const BACK_LEFT = Channel::BackLeft.bitmask();
+ const BACK_RIGHT = Channel::BackRight.bitmask();
+ const FRONT_LEFT_OF_CENTER = Channel::FrontLeftOfCenter.bitmask();
+ const FRONT_RIGHT_OF_CENTER = Channel::FrontRightOfCenter.bitmask();
+ const BACK_CENTER = Channel::BackCenter.bitmask();
+ const SIDE_LEFT = Channel::SideLeft.bitmask();
+ const SIDE_RIGHT = Channel::SideRight.bitmask();
+ const TOP_CENTER = Channel::TopCenter.bitmask();
+ const TOP_FRONT_LEFT = Channel::TopFrontLeft.bitmask();
+ const TOP_FRONT_CENTER = Channel::TopFrontCenter.bitmask();
+ const TOP_FRONT_RIGHT = Channel::TopFrontRight.bitmask();
+ const TOP_BACK_LEFT = Channel::TopBackLeft.bitmask();
+ const TOP_BACK_CENTER = Channel::TopBackCenter.bitmask();
+ const TOP_BACK_RIGHT = Channel::TopBackRight.bitmask();
+ const SILENCE = Channel::Silence.bitmask();
+ }
+}
+
+// Avoid printing the following types in debugging context {:?} by declaring them in impl
+// rather than bitflags! {} scope.
+impl ChannelMap {
+ pub const FRONT_2: Self = Self::union(Self::FRONT_LEFT, Self::FRONT_RIGHT);
+ pub const BACK_2: Self = Self::union(Self::BACK_LEFT, Self::BACK_RIGHT);
+ pub const FRONT_2_OF_CENTER: Self =
+ Self::union(Self::FRONT_LEFT_OF_CENTER, Self::FRONT_RIGHT_OF_CENTER);
+ pub const SIDE_2: Self = Self::union(Self::SIDE_LEFT, Self::SIDE_RIGHT);
+}
+
+impl From<Channel> for ChannelMap {
+ fn from(channel: Channel) -> Self {
+ ChannelMap::from_bits(channel.bitmask()).expect("convert an invalid channel")
+ }
+}
diff --git a/third_party/rust/audio-mixer/src/coefficient.rs b/third_party/rust/audio-mixer/src/coefficient.rs
new file mode 100644
index 0000000000..799d50289c
--- /dev/null
+++ b/third_party/rust/audio-mixer/src/coefficient.rs
@@ -0,0 +1,756 @@
+// The code is based from libcubeb's cubeb_mixer.cpp,
+// which adapts the code from libswresample's rematrix.c
+
+use crate::channel::{Channel, ChannelMap};
+
+use std::fmt::Debug;
+
+const CHANNELS: usize = Channel::count();
+
+#[derive(Debug)]
+enum Error {
+ DuplicateNonSilenceChannel,
+ AsymmetricChannels,
+}
+
+#[derive(Debug)]
+struct ChannelLayout {
+ channels: Vec<Channel>,
+ channel_map: ChannelMap,
+}
+
+impl ChannelLayout {
+ fn new(channels: &[Channel]) -> Result<Self, Error> {
+ let channel_map = Self::get_channel_map(channels)?;
+ Ok(Self {
+ channels: channels.to_vec(),
+ channel_map,
+ })
+ }
+
+ // Except Silence channel, the duplicate channels are not allowed.
+ fn get_channel_map(channels: &[Channel]) -> Result<ChannelMap, Error> {
+ let mut map = ChannelMap::empty();
+ for channel in channels {
+ let bitmask = ChannelMap::from(*channel);
+ if channel != &Channel::Silence && map.contains(bitmask) {
+ return Err(Error::DuplicateNonSilenceChannel);
+ }
+ map.insert(bitmask);
+ }
+ Ok(map)
+ }
+}
+
+#[derive(Debug)]
+pub struct Coefficient<T>
+where
+ T: MixingCoefficient,
+ T::Coef: Copy,
+{
+ input_layout: ChannelLayout,
+ output_layout: ChannelLayout,
+ matrix: Vec<Vec<T::Coef>>,
+ would_overflow_from_coefficient_value: Option<bool>, // Only used when T is i16
+}
+
+impl<T> Coefficient<T>
+where
+ T: MixingCoefficient,
+ T::Coef: Copy,
+{
+ // Given a M-channel input layout and a N-channel output layout, generate a NxM coefficients
+ // matrix m such that out_audio = m * in_audio, where in_audio, out_audio are Mx1, Nx1 matrix
+ // storing input and output audio data in their rows respectively.
+ //
+ // data in channel #1 ▸ │ Silence │ │ 0, 0, 0, 0 │ │ FrontRight │ ◂ data in channel #1
+ // data in channel #2 ▸ │ FrontRight │ = │ 1, C, 0, L │ x │ FrontCenter │ ◂ data in channel #2
+ // data in channel #3 ▸ │ FrontLeft │ │ 0, C, 1, L │ │ FrontLeft │ ◂ data in channel #3
+ // ▴ ▴ │ LowFrequency │ ◂ data in channel #4
+ // ┊ ┊ ▴
+ // ┊ ┊ ┊
+ // out_audio mixing matrix m in_audio
+ //
+ // The FrontLeft, FrontRight, ... etc label the data for front-left, front-right ... etc channel
+ // in both input and output audio data buffer.
+ //
+ // C and L are coefficients mixing input data from front-center channel and low-frequency channel
+ // to front-left and front-right.
+ //
+ // In math, the in_audio and out_audio should be a 2D-matrix with several rows containing only
+ // one column. However, the in_audio and out_audio are passed by 1-D matrix here for convenience.
+ pub fn create(input_channels: &[Channel], output_channels: &[Channel]) -> Self {
+ let input_layout = ChannelLayout::new(input_channels).expect("Invalid input layout");
+ let output_layout = ChannelLayout::new(output_channels).expect("Invalid output layout");
+
+ let mixing_matrix =
+ Self::build_mixing_matrix(input_layout.channel_map, output_layout.channel_map)
+ .unwrap_or_else(|_| Self::get_basic_matrix());
+
+ let coefficient_matrix = Self::pick_coefficients(
+ &input_layout.channels,
+ &output_layout.channels,
+ &mixing_matrix,
+ );
+
+ let normalized_matrix = Self::normalize(T::max_coefficients_sum(), coefficient_matrix);
+
+ let would_overflow = T::would_overflow_from_coefficient_value(&normalized_matrix);
+
+ // Convert the type of the coefficients from f64 to T::Coef.
+ let matrix = normalized_matrix
+ .into_iter()
+ .map(|row| row.into_iter().map(T::coefficient_from_f64).collect())
+ .collect();
+
+ Self {
+ input_layout,
+ output_layout,
+ matrix,
+ would_overflow_from_coefficient_value: would_overflow,
+ }
+ }
+
+ // Return the coefficient for mixing input channel data into output channel.
+ pub fn get(&self, input: usize, output: usize) -> T::Coef {
+ assert!(output < self.matrix.len());
+ assert!(input < self.matrix[output].len());
+ self.matrix[output][input] // Perform copy so T::Coef must implement Copy.
+ }
+
+ pub fn would_overflow_from_coefficient_value(&self) -> Option<bool> {
+ self.would_overflow_from_coefficient_value
+ }
+
+ pub fn input_channels(&self) -> &[Channel] {
+ &self.input_layout.channels
+ }
+
+ pub fn output_channels(&self) -> &[Channel] {
+ &self.output_layout.channels
+ }
+
+ // Given audio input and output channel-maps, generate a CxC mixing coefficients matrix M,
+ // whose indice are ordered by the values defined in enum Channel, such that
+ // output_data(i) = Σ M[i][j] * input_data(j), for all j in [0, C),
+ // where i is in [0, C) and C is the number of channels defined in enum Channel,
+ // output_data and input_data are buffers containing data for channels that are also ordered
+ // by the values defined in enum Channel.
+ //
+ // │ FrontLeft │ │ 1, 0, ..., 0 │ │ FrontLeft │ ◂ data in front-left channel
+ // │ FrontRight │ │ 0, 1, ..., 0 │ │ FrontRight │ ◂ data in front-right channel
+ // │ FrontCenter │ = │ ........., 0 │ x │ FrontCenter │ ◂ data in front-center channel
+ // │ ........... │ │ ........., 0 | │ ........... │ ◂ ...
+ // │ Silence │ │ 0, 0, ..., 0 | │ Silence │ ◂ data in silence channel
+ // ▴ ▴ ▴
+ // out_audio coef matrix M in_audio
+ //
+ // ChannelMap would be used as a hash table to check the existence of channels.
+ #[allow(clippy::cognitive_complexity)]
+ fn build_mixing_matrix(
+ input_map: ChannelMap,
+ output_map: ChannelMap,
+ ) -> Result<[[f64; CHANNELS]; CHANNELS], Error> {
+ // Mixing coefficients constants.
+ use std::f64::consts::FRAC_1_SQRT_2;
+ use std::f64::consts::SQRT_2;
+ const CENTER_MIX_LEVEL: f64 = FRAC_1_SQRT_2;
+ const SURROUND_MIX_LEVEL: f64 = FRAC_1_SQRT_2;
+ const LFE_MIX_LEVEL: f64 = 1.0;
+
+ // The indices of channels in the mixing coefficients matrix.
+ const FRONT_LEFT: usize = Channel::FrontLeft.number();
+ const FRONT_RIGHT: usize = Channel::FrontRight.number();
+ const FRONT_CENTER: usize = Channel::FrontCenter.number();
+ const LOW_FREQUENCY: usize = Channel::LowFrequency.number();
+ const BACK_LEFT: usize = Channel::BackLeft.number();
+ const BACK_RIGHT: usize = Channel::BackRight.number();
+ const FRONT_LEFT_OF_CENTER: usize = Channel::FrontLeftOfCenter.number();
+ const FRONT_RIGHT_OF_CENTER: usize = Channel::FrontRightOfCenter.number();
+ const BACK_CENTER: usize = Channel::BackCenter.number();
+ const SIDE_LEFT: usize = Channel::SideLeft.number();
+ const SIDE_RIGHT: usize = Channel::SideRight.number();
+
+ // Return true if mixable channels are symmetric.
+ fn is_symmetric(map: ChannelMap) -> bool {
+ fn even(map: ChannelMap) -> bool {
+ map.bits().count_ones() % 2 == 0
+ }
+ even(map & ChannelMap::FRONT_2)
+ && even(map & ChannelMap::BACK_2)
+ && even(map & ChannelMap::FRONT_2_OF_CENTER)
+ && even(map & ChannelMap::SIDE_2)
+ }
+
+ if !is_symmetric(input_map) || !is_symmetric(output_map) {
+ return Err(Error::AsymmetricChannels);
+ }
+
+ let mut matrix = Self::get_basic_matrix();
+
+ // Get input channels that are not in the output channels.
+ let unaccounted_input_map = input_map & !output_map;
+
+ // When input has front-center but output has not, and output has front-stereo,
+ // mix input's front-center to output's front-stereo.
+ if unaccounted_input_map.contains(ChannelMap::FRONT_CENTER)
+ && output_map.contains(ChannelMap::FRONT_2)
+ {
+ let coefficient = if input_map.contains(ChannelMap::FRONT_2) {
+ CENTER_MIX_LEVEL
+ } else {
+ FRAC_1_SQRT_2
+ };
+ matrix[FRONT_LEFT][FRONT_CENTER] += coefficient;
+ matrix[FRONT_RIGHT][FRONT_CENTER] += coefficient;
+ }
+
+ // When input has front-stereo but output has not, and output has front-center,
+ // mix input's front-stereo to output's front-center.
+ if unaccounted_input_map.contains(ChannelMap::FRONT_2)
+ && output_map.contains(ChannelMap::FRONT_CENTER)
+ {
+ matrix[FRONT_CENTER][FRONT_LEFT] += FRAC_1_SQRT_2;
+ matrix[FRONT_CENTER][FRONT_RIGHT] += FRAC_1_SQRT_2;
+ if input_map.contains(ChannelMap::FRONT_CENTER) {
+ matrix[FRONT_CENTER][FRONT_CENTER] = CENTER_MIX_LEVEL * SQRT_2;
+ }
+ }
+
+ // When input has back-center but output has not,
+ if unaccounted_input_map.contains(ChannelMap::BACK_CENTER) {
+ // if output has back-stereo, mix input's back-center to output's back-stereo.
+ if output_map.contains(ChannelMap::BACK_2) {
+ matrix[BACK_LEFT][BACK_CENTER] += FRAC_1_SQRT_2;
+ matrix[BACK_RIGHT][BACK_CENTER] += FRAC_1_SQRT_2;
+ // or if output has side-stereo, mix input's back-center to output's side-stereo.
+ } else if output_map.contains(ChannelMap::SIDE_2) {
+ matrix[SIDE_LEFT][BACK_CENTER] += FRAC_1_SQRT_2;
+ matrix[SIDE_RIGHT][BACK_CENTER] += FRAC_1_SQRT_2;
+ // or if output has front-stereo, mix input's back-center to output's front-stereo.
+ } else if output_map.contains(ChannelMap::FRONT_2) {
+ matrix[FRONT_LEFT][BACK_CENTER] += SURROUND_MIX_LEVEL * FRAC_1_SQRT_2;
+ matrix[FRONT_RIGHT][BACK_CENTER] += SURROUND_MIX_LEVEL * FRAC_1_SQRT_2;
+ // or if output has front-center, mix input's back-center to output's front-center.
+ } else if output_map.contains(ChannelMap::FRONT_CENTER) {
+ matrix[FRONT_CENTER][BACK_CENTER] += SURROUND_MIX_LEVEL * FRAC_1_SQRT_2;
+ }
+ }
+
+ // When input has back-stereo but output has not,
+ if unaccounted_input_map.contains(ChannelMap::BACK_2) {
+ // if output has back-center, mix input's back-stereo to output's back-center.
+ if output_map.contains(ChannelMap::BACK_CENTER) {
+ matrix[BACK_CENTER][BACK_LEFT] += FRAC_1_SQRT_2;
+ matrix[BACK_CENTER][BACK_RIGHT] += FRAC_1_SQRT_2;
+ // or if output has side-stereo, mix input's back-stereo to output's side-stereo.
+ } else if output_map.contains(ChannelMap::SIDE_2) {
+ let coefficient = if input_map.contains(ChannelMap::SIDE_2) {
+ FRAC_1_SQRT_2
+ } else {
+ 1.0
+ };
+ matrix[SIDE_LEFT][BACK_LEFT] += coefficient;
+ matrix[SIDE_RIGHT][BACK_RIGHT] += coefficient;
+ // or if output has front-stereo, mix input's back-stereo to output's side-stereo.
+ } else if output_map.contains(ChannelMap::FRONT_2) {
+ matrix[FRONT_LEFT][BACK_LEFT] += SURROUND_MIX_LEVEL;
+ matrix[FRONT_RIGHT][BACK_RIGHT] += SURROUND_MIX_LEVEL;
+ // or if output has front-center, mix input's back-stereo to output's front-center.
+ } else if output_map.contains(ChannelMap::FRONT_CENTER) {
+ matrix[FRONT_CENTER][BACK_LEFT] += SURROUND_MIX_LEVEL * FRAC_1_SQRT_2;
+ matrix[FRONT_CENTER][BACK_RIGHT] += SURROUND_MIX_LEVEL * FRAC_1_SQRT_2;
+ }
+ }
+
+ // When input has side-stereo but output has not,
+ if unaccounted_input_map.contains(ChannelMap::SIDE_2) {
+ // if output has back-stereo, mix input's side-stereo to output's back-stereo.
+ if output_map.contains(ChannelMap::BACK_2) {
+ let coefficient = if input_map.contains(ChannelMap::BACK_2) {
+ FRAC_1_SQRT_2
+ } else {
+ 1.0
+ };
+ matrix[BACK_LEFT][SIDE_LEFT] += coefficient;
+ matrix[BACK_RIGHT][SIDE_RIGHT] += coefficient;
+ // or if output has back-center, mix input's side-stereo to output's back-center.
+ } else if output_map.contains(ChannelMap::BACK_CENTER) {
+ matrix[BACK_CENTER][SIDE_LEFT] += FRAC_1_SQRT_2;
+ matrix[BACK_CENTER][SIDE_RIGHT] += FRAC_1_SQRT_2;
+ // or if output has front-stereo, mix input's side-stereo to output's front-stereo.
+ } else if output_map.contains(ChannelMap::FRONT_2) {
+ matrix[FRONT_LEFT][SIDE_LEFT] += SURROUND_MIX_LEVEL;
+ matrix[FRONT_RIGHT][SIDE_RIGHT] += SURROUND_MIX_LEVEL;
+ // or if output has front-center, mix input's side-stereo to output's front-center.
+ } else if output_map.contains(ChannelMap::FRONT_CENTER) {
+ matrix[FRONT_CENTER][SIDE_LEFT] += SURROUND_MIX_LEVEL * FRAC_1_SQRT_2;
+ matrix[FRONT_CENTER][SIDE_RIGHT] += SURROUND_MIX_LEVEL * FRAC_1_SQRT_2;
+ }
+ }
+
+ // When input has front-stereo-of-center but output has not,
+ if unaccounted_input_map.contains(ChannelMap::FRONT_2_OF_CENTER) {
+ // if output has front-stereo, mix input's front-stereo-of-center to output's front-stereo.
+ if output_map.contains(ChannelMap::FRONT_2) {
+ matrix[FRONT_LEFT][FRONT_LEFT_OF_CENTER] += 1.0;
+ matrix[FRONT_RIGHT][FRONT_RIGHT_OF_CENTER] += 1.0;
+ // or if output has front-center, mix input's front-stereo-of-center to output's front-center.
+ } else if output_map.contains(ChannelMap::FRONT_CENTER) {
+ matrix[FRONT_CENTER][FRONT_LEFT_OF_CENTER] += FRAC_1_SQRT_2;
+ matrix[FRONT_CENTER][FRONT_RIGHT_OF_CENTER] += FRAC_1_SQRT_2;
+ }
+ }
+
+ // When input has low-frequency but output has not,
+ if unaccounted_input_map.contains(ChannelMap::LOW_FREQUENCY) {
+ // if output has front-center, mix input's low-frequency to output's front-center.
+ if output_map.contains(ChannelMap::FRONT_CENTER) {
+ matrix[FRONT_CENTER][LOW_FREQUENCY] += LFE_MIX_LEVEL;
+ // or if output has front-stereo, mix input's low-frequency to output's front-stereo.
+ } else if output_map.contains(ChannelMap::FRONT_2) {
+ matrix[FRONT_LEFT][LOW_FREQUENCY] += LFE_MIX_LEVEL * FRAC_1_SQRT_2;
+ matrix[FRONT_RIGHT][LOW_FREQUENCY] += LFE_MIX_LEVEL * FRAC_1_SQRT_2;
+ }
+ }
+
+ Ok(matrix)
+ }
+
+ // Return a CHANNELSxCHANNELS matrix M that is (CHANNELS-1)x(CHANNELS-1) identity matrix
+ // padding with one extra row and one column containing only zero values. The result would be:
+ //
+ // identity padding
+ // matrix column
+ // ▾ ▾
+ // ┌┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┐ i ┐
+ // │ 1, 0, 0, ..., 0 ┊, 0 │ ◂ 0 ┊ channel i
+ // │ 0, 1, 0, ..., 0 ┊, 0 │ ◂ 1 ┊ for
+ // │ 0, 0, 1, ..., 0 ┊, 0 │ ◂ 2 ┊ audio
+ // │ 0, 0, 0, ..., 0 ┊, 0 │ . ┊ output
+ // │ ............... ┊ │ . ┊
+ // │ 0, 0, 0, ..., 1 ┊, 0 │ ◂ 16 ┊
+ // ├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┼┈┈┈┈┤ ◂ 17 ┊
+ // │ 0, 0, 0, ..., 0 ┊, 0 │ ◂ padding row ◂ 18 ┊
+ // ▴ ▴ ▴ .... ▴ ▴ ┘
+ // j 0 1 2 .... 17 18
+ // └┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┘
+ // channel j for audio input
+ //
+ // Given an audio input buffer, in_audio, and an output buffer, out_audio,
+ // and their channel data are both ordered by the values defined in enum Channel.
+ // The generated matrix M makes sure that:
+ //
+ // out_audio(i) = in_audio(j), if i == j and both i, j are non-silence channel
+ // = 0, if i != j or i, j are silence channel
+ //
+ // │ FrontLeft │ │ FrontLeft │ ◂ data in front-left channel
+ // │ FrontRight │ │ FrontRight │ ◂ data in front-right channel
+ // │ FrontCenter │ = M x │ FrontCenter │ ◂ data in front-center channel
+ // │ ........... │ │ ........... │ ◂ ...
+ // │ Silence │ │ Silence │ ◂ data in silence channel
+ // ▴ ▴
+ // out_audio in_audio
+ //
+ // That is,
+ // 1. If the input-channel is silence, it won't be mixed into any channel.
+ // 2. If the output-channel is silence, its output-channel data will be zero (silence).
+ // 3. If input-channel j is different from output-channel i, audio data in input channel j
+ // won't be mixed into the audio output data in channel i
+ // 4. If input-channel j is same as output-channel i, audio data in input channel j will be
+ // copied to audio output data in channel i
+ //
+ fn get_basic_matrix() -> [[f64; CHANNELS]; CHANNELS] {
+ const SILENCE: usize = Channel::Silence.number();
+ let mut matrix = [[0.0; CHANNELS]; CHANNELS];
+ for (i, row) in matrix.iter_mut().enumerate() {
+ if i != SILENCE {
+ row[i] = 1.0;
+ }
+ }
+ matrix
+ }
+
+ // Given is an CHANNELSxCHANNELS mixing matrix whose indice are ordered by the values defined
+ // in enum Channel, and the channel orders of M-channel input and N-channel output, generate a
+ // mixing matrix m such that output_data(i) = Σ m[i][j] * input_data(j), for all j in [0, M),
+ // where i is in [0, N) and {input/output}_data(k) means the data of the number k channel in
+ // the input/output buffer.
+ fn pick_coefficients(
+ input_channels: &[Channel],
+ output_channels: &[Channel],
+ source: &[[f64; CHANNELS]; CHANNELS],
+ ) -> Vec<Vec<f64>> {
+ let mut matrix = Vec::with_capacity(output_channels.len());
+ for output_channel in output_channels {
+ let output_channel_index = (*output_channel).number();
+ let mut coefficients = Vec::with_capacity(input_channels.len());
+ for input_channel in input_channels {
+ let input_channel_index = (*input_channel).number();
+ coefficients.push(source[output_channel_index][input_channel_index]);
+ }
+ matrix.push(coefficients);
+ }
+ matrix
+ }
+
+ fn normalize(max_coefficients_sum: f64, mut coefficients: Vec<Vec<f64>>) -> Vec<Vec<f64>> {
+ let mut max_sum: f64 = 0.0;
+ for coefs in &coefficients {
+ max_sum = max_sum.max(coefs.iter().sum());
+ }
+ if max_sum != 0.0 && max_sum > max_coefficients_sum {
+ max_sum /= max_coefficients_sum;
+ for coefs in &mut coefficients {
+ for coef in coefs {
+ *coef /= max_sum;
+ }
+ }
+ }
+ coefficients
+ }
+}
+
+pub trait MixingCoefficient {
+ type Coef;
+
+ // TODO: These should be private.
+ fn max_coefficients_sum() -> f64; // Used for normalizing.
+ fn coefficient_from_f64(value: f64) -> Self::Coef;
+ // Precheck if overflow occurs when converting value from Self::Coef type to Self type.
+ fn would_overflow_from_coefficient_value(coefficient: &[Vec<f64>]) -> Option<bool>;
+
+ // Convert f32 (Self) -> f32 (Self::Coef) or i16 (Self) -> i32 (Self::Coef)
+ #[allow(clippy::wrong_self_convention)]
+ fn to_coefficient_value(value: Self) -> Self::Coef;
+ fn from_coefficient_value(value: Self::Coef, would_overflow: Option<bool>) -> Self;
+}
+
+impl MixingCoefficient for f32 {
+ type Coef = f32;
+
+ fn max_coefficients_sum() -> f64 {
+ f64::from(std::i32::MAX)
+ }
+
+ fn coefficient_from_f64(value: f64) -> Self::Coef {
+ value as Self::Coef
+ }
+
+ fn would_overflow_from_coefficient_value(_coefficient: &[Vec<f64>]) -> Option<bool> {
+ None
+ }
+
+ fn to_coefficient_value(value: Self) -> Self::Coef {
+ value
+ }
+
+ fn from_coefficient_value(value: Self::Coef, would_overflow: Option<bool>) -> Self {
+ assert!(would_overflow.is_none());
+ value
+ }
+}
+
+impl MixingCoefficient for i16 {
+ type Coef = i32;
+
+ fn max_coefficients_sum() -> f64 {
+ 1.0
+ }
+
+ fn coefficient_from_f64(value: f64) -> Self::Coef {
+ (value * f64::from(1 << 15)).round() as Self::Coef
+ }
+
+ fn would_overflow_from_coefficient_value(coefficient: &[Vec<f64>]) -> Option<bool> {
+ let mut max_sum: Self::Coef = 0;
+ for row in coefficient {
+ let mut sum: Self::Coef = 0;
+ let mut rem: f64 = 0.0;
+ for coef in row {
+ let target = coef * f64::from(1 << 15) + rem;
+ let value = target.round() as Self::Coef;
+ rem += target - target.round();
+ sum += value.abs();
+ }
+ max_sum = max_sum.max(sum);
+ }
+ Some(max_sum > (1 << 15))
+ }
+
+ fn to_coefficient_value(value: Self) -> Self::Coef {
+ Self::Coef::from(value)
+ }
+
+ fn from_coefficient_value(value: Self::Coef, would_overflow: Option<bool>) -> Self {
+ use std::convert::TryFrom;
+ let would_overflow = would_overflow.expect("would_overflow must have value for i16 type");
+ let mut converted = (value + (1 << 14)) >> 15;
+ // clip the signed integer value into the -32768,32767 range.
+ if would_overflow && ((converted + 0x8000) & !0xFFFF != 0) {
+ converted = (converted >> 31) ^ 0x7FFF;
+ }
+ Self::try_from(converted).expect("Cannot convert coefficient from i32 to i16")
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn test_create_f32() {
+ test_create::<f32>(MixDirection::Downmix);
+ test_create::<f32>(MixDirection::Upmix);
+ }
+
+ #[test]
+ fn test_create_i16() {
+ test_create::<i16>(MixDirection::Downmix);
+ test_create::<i16>(MixDirection::Upmix);
+ }
+
+ fn test_create<T>(direction: MixDirection)
+ where
+ T: MixingCoefficient,
+ T::Coef: Copy + Debug,
+ {
+ let (input_channels, output_channels) = get_test_channels(direction);
+ let coefficient = Coefficient::<T>::create(&input_channels, &output_channels);
+ println!(
+ "{:?} = {:?} * {:?}",
+ output_channels, coefficient.matrix, input_channels
+ );
+ }
+
+ enum MixDirection {
+ Downmix,
+ Upmix,
+ }
+ fn get_test_channels(direction: MixDirection) -> (Vec<Channel>, Vec<Channel>) {
+ let more = vec![
+ Channel::Silence,
+ Channel::FrontRight,
+ Channel::FrontLeft,
+ Channel::LowFrequency,
+ Channel::Silence,
+ Channel::BackCenter,
+ ];
+ let less = vec![
+ Channel::FrontLeft,
+ Channel::Silence,
+ Channel::FrontRight,
+ Channel::FrontCenter,
+ ];
+ match direction {
+ MixDirection::Downmix => (more, less),
+ MixDirection::Upmix => (less, more),
+ }
+ }
+
+ #[test]
+ fn test_create_with_duplicate_silience_channels_f32() {
+ test_create_with_duplicate_silience_channels::<f32>()
+ }
+
+ #[test]
+ fn test_create_with_duplicate_silience_channels_i16() {
+ test_create_with_duplicate_silience_channels::<i16>()
+ }
+
+ #[test]
+ #[should_panic]
+ fn test_create_with_duplicate_input_channels_f32() {
+ test_create_with_duplicate_input_channels::<f32>()
+ }
+
+ #[test]
+ #[should_panic]
+ fn test_create_with_duplicate_input_channels_i16() {
+ test_create_with_duplicate_input_channels::<i16>()
+ }
+
+ #[test]
+ #[should_panic]
+ fn test_create_with_duplicate_output_channels_f32() {
+ test_create_with_duplicate_output_channels::<f32>()
+ }
+
+ #[test]
+ #[should_panic]
+ fn test_create_with_duplicate_output_channels_i16() {
+ test_create_with_duplicate_output_channels::<i16>()
+ }
+
+ fn test_create_with_duplicate_silience_channels<T>()
+ where
+ T: MixingCoefficient,
+ T::Coef: Copy,
+ {
+ // Duplicate of Silence channels is allowed on both input side and output side.
+ let input_channels = [
+ Channel::FrontLeft,
+ Channel::Silence,
+ Channel::FrontRight,
+ Channel::FrontCenter,
+ Channel::Silence,
+ ];
+ let output_channels = [
+ Channel::Silence,
+ Channel::FrontRight,
+ Channel::FrontLeft,
+ Channel::BackCenter,
+ Channel::Silence,
+ ];
+ let _ = Coefficient::<T>::create(&input_channels, &output_channels);
+ }
+
+ fn test_create_with_duplicate_input_channels<T>()
+ where
+ T: MixingCoefficient,
+ T::Coef: Copy,
+ {
+ let input_channels = [
+ Channel::FrontLeft,
+ Channel::Silence,
+ Channel::FrontLeft,
+ Channel::FrontCenter,
+ ];
+ let output_channels = [
+ Channel::Silence,
+ Channel::FrontRight,
+ Channel::FrontLeft,
+ Channel::FrontCenter,
+ Channel::BackCenter,
+ ];
+ let _ = Coefficient::<T>::create(&input_channels, &output_channels);
+ }
+
+ fn test_create_with_duplicate_output_channels<T>()
+ where
+ T: MixingCoefficient,
+ T::Coef: Copy,
+ {
+ let input_channels = [
+ Channel::FrontLeft,
+ Channel::Silence,
+ Channel::FrontRight,
+ Channel::FrontCenter,
+ ];
+ let output_channels = [
+ Channel::Silence,
+ Channel::FrontRight,
+ Channel::FrontLeft,
+ Channel::FrontCenter,
+ Channel::FrontCenter,
+ Channel::BackCenter,
+ ];
+ let _ = Coefficient::<T>::create(&input_channels, &output_channels);
+ }
+
+ #[test]
+ fn test_get_redirect_matrix_f32() {
+ test_get_redirect_matrix::<f32>();
+ }
+
+ #[test]
+ fn test_get_redirect_matrix_i16() {
+ test_get_redirect_matrix::<i16>();
+ }
+
+ fn test_get_redirect_matrix<T>()
+ where
+ T: MixingCoefficient,
+ T::Coef: Copy + Debug + PartialEq,
+ {
+ // Create a matrix that only redirect the channels from input side to output side,
+ // without mixing input audio data to output audio data.
+ fn compute_redirect_matrix<T>(
+ input_channels: &[Channel],
+ output_channels: &[Channel],
+ ) -> Vec<Vec<T::Coef>>
+ where
+ T: MixingCoefficient,
+ {
+ let mut matrix = Vec::with_capacity(output_channels.len());
+ for output_channel in output_channels {
+ let mut row = Vec::with_capacity(input_channels.len());
+ for input_channel in input_channels {
+ row.push(
+ if input_channel != output_channel
+ || input_channel == &Channel::Silence
+ || output_channel == &Channel::Silence
+ {
+ 0.0
+ } else {
+ 1.0
+ },
+ );
+ }
+ matrix.push(row);
+ }
+
+ // Convert the type of the coefficients from f64 to T::Coef.
+ matrix
+ .into_iter()
+ .map(|row| row.into_iter().map(T::coefficient_from_f64).collect())
+ .collect()
+ }
+
+ let input_channels = [
+ Channel::FrontLeft,
+ Channel::Silence,
+ Channel::FrontRight,
+ Channel::FrontCenter,
+ ];
+ let output_channels = [
+ Channel::Silence,
+ Channel::FrontLeft,
+ Channel::Silence,
+ Channel::FrontCenter,
+ Channel::BackCenter,
+ ];
+
+ // Get a redirect matrix since the output layout is asymmetric.
+ let coefficient = Coefficient::<T>::create(&input_channels, &output_channels);
+
+ let expected = compute_redirect_matrix::<T>(&input_channels, &output_channels);
+ assert_eq!(coefficient.matrix, expected);
+
+ println!(
+ "{:?} = {:?} * {:?}",
+ output_channels, coefficient.matrix, input_channels
+ );
+ }
+
+ #[test]
+ fn test_normalize() {
+ use float_cmp::approx_eq;
+
+ let m = vec![
+ vec![1.0_f64, 2.0_f64, 3.0_f64],
+ vec![4.0_f64, 6.0_f64, 10.0_f64],
+ ];
+
+ let mut max_row_sum: f64 = std::f64::MIN;
+ for row in &m {
+ max_row_sum = max_row_sum.max(row.iter().sum());
+ }
+
+ // Type of Coefficient doesn't matter here.
+ // If the first argument of normalize >= max_row_sum, do nothing.
+ let n = Coefficient::<f32>::normalize(max_row_sum, m.clone());
+ assert_eq!(n, m);
+
+ // If the first argument of normalize < max_row_sum, do normalizing.
+ let smaller_max = max_row_sum - 0.5_f64;
+ assert!(smaller_max > 0.0_f64);
+ let n = Coefficient::<f32>::normalize(smaller_max, m);
+ let mut max_row_sum: f64 = std::f64::MIN;
+ for row in &n {
+ max_row_sum = max_row_sum.max(row.iter().sum());
+ assert!(row.iter().sum::<f64>() <= smaller_max);
+ }
+ assert!(approx_eq!(f64, smaller_max, max_row_sum));
+ }
+}
diff --git a/third_party/rust/audio-mixer/src/lib.rs b/third_party/rust/audio-mixer/src/lib.rs
new file mode 100644
index 0000000000..170d5952bc
--- /dev/null
+++ b/third_party/rust/audio-mixer/src/lib.rs
@@ -0,0 +1,81 @@
+#[macro_use]
+extern crate bitflags;
+
+mod channel;
+mod coefficient;
+
+// Export Channel outside.
+pub use channel::Channel;
+use coefficient::{Coefficient, MixingCoefficient};
+
+use std::default::Default;
+use std::fmt::Debug;
+use std::ops::{AddAssign, Mul};
+
+// A mixer mixing M-channel input data to N-channel output data.
+// T::Coef is an associated type defined in MixingCoefficient, which indicates the type of the
+// mixing coefficient that would be used for type T. When T is f32, the T::Coef is f32. When T
+// is i16, the T::Coef is i32. When mixing data, a temporary variable with type T::Coef would be
+// created to hold the mixing result. Since the type of input and output audio data is T,
+// the methods provided from MixingCoefficient trait would be used to convert the value between
+// type T and T::Coef.
+#[derive(Debug)]
+pub struct Mixer<T>
+where
+ T: Copy + Debug + MixingCoefficient,
+ T::Coef: AddAssign + Copy + Debug + Default + Mul<T::Coef, Output = T::Coef>,
+{
+ coefficient: Coefficient<T>,
+}
+
+impl<T> Mixer<T>
+where
+ T: Copy + Debug + MixingCoefficient,
+ T::Coef: AddAssign + Copy + Debug + Default + Mul<T::Coef, Output = T::Coef>,
+{
+ pub fn new(input_channels: &[Channel], output_channels: &[Channel]) -> Self {
+ Self {
+ coefficient: Coefficient::create(input_channels, output_channels),
+ }
+ }
+
+ // To mix M-channel audio input data to N-channel output data, the data in output-channel i
+ // is the sum of product of data in input-channel j and the coefficient for mixing from
+ // input-channel j to output-channel i, for all j in M channels. That is,
+ // output_data(i) = Σ coefficient(j, i) * input_data(j), for all j in [0, M),
+ // where i is in [0, N) and coefficient is a function returning mixing coefficient from
+ // input channel j to output channel i.
+ pub fn mix(&self, input_buffer: &[T], output_buffer: &mut [T]) {
+ assert_eq!(
+ input_buffer.len(),
+ self.input_channels().len(),
+ "input slice must have the same size as the input channel's one."
+ );
+ assert_eq!(
+ output_buffer.len(),
+ self.output_channels().len(),
+ "output slice must have the same size as the output channel's one."
+ );
+ for (i, output) in output_buffer.iter_mut().enumerate() {
+ // T must implement Default that returns a zero value from default().
+ let mut value = T::Coef::default(); // Create a zero value.
+ for (j, input) in input_buffer.iter().enumerate() {
+ // T::Coef needs to implement `AddAssign` and `Mul` to make `+=` and `*` work.
+ // T needs to implement `Copy` so `*input` can be copied.
+ value += self.coefficient.get(j, i) * T::to_coefficient_value(*input);
+ }
+ *output = T::from_coefficient_value(
+ value,
+ self.coefficient.would_overflow_from_coefficient_value(),
+ );
+ }
+ }
+
+ pub fn input_channels(&self) -> &[Channel] {
+ self.coefficient.input_channels()
+ }
+
+ pub fn output_channels(&self) -> &[Channel] {
+ self.coefficient.output_channels()
+ }
+}
diff --git a/third_party/rust/audio-mixer/src/main.rs b/third_party/rust/audio-mixer/src/main.rs
new file mode 100644
index 0000000000..938867f5e8
--- /dev/null
+++ b/third_party/rust/audio-mixer/src/main.rs
@@ -0,0 +1,50 @@
+extern crate audio_mixer;
+use audio_mixer::{Channel, Mixer};
+
+fn main() {
+ // f32
+ let input_channels = [
+ Channel::FrontLeft,
+ Channel::Silence,
+ Channel::FrontRight,
+ Channel::FrontCenter,
+ ];
+ let output_channels = [Channel::FrontLeft, Channel::FrontRight];
+
+ let mut input_buffer = vec![0.0; input_channels.len()];
+ for (i, data) in input_buffer.iter_mut().enumerate() {
+ *data = (i + 1) as f32;
+ }
+ let mut output_buffer = vec![0.0; output_channels.len()];
+
+ let mixer = Mixer::new(&input_channels, &output_channels);
+
+ mixer.mix(input_buffer.as_slice(), output_buffer.as_mut_slice());
+ println!("{:?} is mixed to {:?}", input_buffer, output_buffer);
+
+ // i16
+ let input_channels = [
+ Channel::FrontLeft,
+ Channel::Silence,
+ Channel::FrontRight,
+ Channel::FrontCenter,
+ Channel::BackLeft,
+ Channel::SideRight,
+ Channel::LowFrequency,
+ Channel::SideLeft,
+ Channel::BackCenter,
+ Channel::BackRight,
+ ];
+ let output_channels = [Channel::Silence, Channel::FrontRight, Channel::FrontLeft];
+
+ let mut input_buffer = vec![0; input_channels.len()];
+ for (i, data) in input_buffer.iter_mut().enumerate() {
+ *data = (i + 0x7FFE) as i16;
+ }
+ let mut output_buffer = vec![0; output_channels.len()];
+
+ let mixer = Mixer::new(&input_channels, &output_channels);
+
+ mixer.mix(input_buffer.as_slice(), output_buffer.as_mut_slice());
+ println!("{:?} is mixed to {:?}", input_buffer, output_buffer);
+}