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.lock521
-rw-r--r--third_party/rust/audio-mixer/Cargo.toml35
-rw-r--r--third_party/rust/audio-mixer/README.md18
-rw-r--r--third_party/rust/audio-mixer/benches/benchmark.rs95
-rw-r--r--third_party/rust/audio-mixer/src/channel.rs85
-rw-r--r--third_party/rust/audio-mixer/src/coefficient.rs754
-rw-r--r--third_party/rust/audio-mixer/src/lib.rs81
-rw-r--r--third_party/rust/audio-mixer/src/main.rs50
9 files changed, 1640 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..b3cca2f8ef
--- /dev/null
+++ b/third_party/rust/audio-mixer/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"Cargo.lock":"e55c6e55d2731323e2064463916bfa64b35a4ba32513767ccedc5b7de16c370b","Cargo.toml":"58e3d5371c6e8c1a291edab3a174090488aea928aeed40f0b9e9bf6a510bfdc2","README.md":"e63533f229e9c7ec65f7c69c32a225e5adc0d96c626864de7a35b76381adb38d","benches/benchmark.rs":"33e9d85cee38fb7752e9315ccf5f816179309062bfb78118288f7e1495e3616f","src/channel.rs":"9a5aad342674629b895f4a3c9b3a3e90f4c05ef1cd4cb0b524cf16afa754edd8","src/coefficient.rs":"004b39116626073b47427223bf50852d414f558d34811aa859d2925e3b42ae5b","src/lib.rs":"73221a83d21f12c1ac7f95ef952dc7e849d53e6a742921c20f8ef4360b53844e","src/main.rs":"0240cd535746bd793cee7088389dc6856944c93a963fd2494cad06c33fe3a3af"},"package":"cd92208b8b0c2477d1123f9fa898e35d9f7d9340e3f26ddda27bf37a608eff99"} \ 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..f992470a47
--- /dev/null
+++ b/third_party/rust/audio-mixer/Cargo.lock
@@ -0,0 +1,521 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+[[package]]
+name = "atty"
+version = "0.2.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "audio-mixer"
+version = "0.1.2"
+dependencies = [
+ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "criterion 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "float-cmp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "autocfg"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "bitflags"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "bstr"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex-automata 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "byteorder"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "cast"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "cfg-if"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "clap"
+version = "2.33.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "criterion"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "criterion-plot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "csv 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand_os 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand_xoshiro 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tinytemplate 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "criterion-plot"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "crossbeam-queue"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "csv"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bstr 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "csv-core"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "either"
+version = "1.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "float-cmp"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "itertools"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "itoa"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "libc"
+version = "0.2.66"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "memchr"
+version = "2.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "memoffset"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "hermit-abi 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rand_os"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rand_xoshiro"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rayon"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rayon-core 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rustc_version"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "same-file"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "semver"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "semver-parser"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "serde"
+version = "1.0.104"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "serde_derive"
+version = "1.0.104"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "textwrap"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tinytemplate"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "unicode-width"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "walkdir"
+version = "2.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "wasi"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "winapi"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[metadata]
+"checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90"
+"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
+"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
+"checksum bstr 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8d6c2c5b58ab920a4f5aeaaca34b4488074e8cc7596af94e6f8c6ff247c60245"
+"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
+"checksum cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b9434b9a5aa1450faa3f9cb14ea0e8c53bb5d2b3c1bfd1ab4fc03e9f33fbfb0"
+"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
+"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
+"checksum criterion 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "938703e165481c8d612ea3479ac8342e5615185db37765162e762ec3523e2fc6"
+"checksum criterion-plot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eccdc6ce8bbe352ca89025bee672aa6d24f4eb8c53e3a8b5d1bc58011da072a2"
+"checksum crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3aa945d63861bfe624b55d153a39684da1e8c0bc8fba932f7ee3a3c16cea3ca"
+"checksum crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5064ebdbf05ce3cb95e45c8b086f72263f4166b29b97f6baff7ef7fe047b55ac"
+"checksum crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db"
+"checksum crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4"
+"checksum csv 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "37519ccdfd73a75821cac9319d4fce15a81b9fcf75f951df5b9988aa3a0af87d"
+"checksum csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9b5cadb6b25c77aeff80ba701712494213f4a8418fcda2ee11b6560c3ad0bf4c"
+"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
+"checksum float-cmp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "da62c4f1b81918835a8c6a484a397775fff5953fe83529afd51b05f5c6a6617d"
+"checksum getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407"
+"checksum hermit-abi 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f629dc602392d3ec14bfc8a09b5e644d7ffd725102b48b81e59f90f2633621d7"
+"checksum itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484"
+"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f"
+"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558"
+"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"
+"checksum memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9"
+"checksum num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c81ffc11c212fa327657cb19dd85eb7419e163b5b076bede2bdb5c974c07e4"
+"checksum num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "76dac5ed2a876980778b8b85f75a71b6cbf0db0b1232ee12f826bccb00d09d72"
+"checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27"
+"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
+"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
+"checksum rand_os 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a788ae3edb696cfcba1c19bfd388cc4b8c21f8a408432b199c072825084da58a"
+"checksum rand_xoshiro 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e18c91676f670f6f0312764c759405f13afb98d5d73819840cf72a518487bff"
+"checksum rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "db6ce3297f9c85e16621bb8cca38a06779ffc31bb8184e1be4bed2be4678a098"
+"checksum rayon-core 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08a89b46efaf957e52b18062fb2f4660f8b8a4dde1807ca002690868ef2c85a9"
+"checksum regex-automata 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "92b73c2a1770c255c240eaa4ee600df1704a38dc3feaa6e949e7fcd4f8dc09f9"
+"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
+"checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8"
+"checksum same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421"
+"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d"
+"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
+"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
+"checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449"
+"checksum serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64"
+"checksum serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)" = "48c575e0cc52bdd09b47f330f646cf59afc586e9c4e3ccd6fc1f625b8ea1dad7"
+"checksum syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "dff0acdb207ae2fe6d5976617f887eb1e35a2ba52c13c7234c790960cdad9238"
+"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
+"checksum tinytemplate 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "57a3c6667d3e65eb1bc3aed6fd14011c6cbc3a0665218ab7f5daf040b9ec371a"
+"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
+"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
+"checksum walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9658c94fa8b940eab2250bd5a457f9c48b748420d71293b165c8cdbe2f55f71e"
+"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d"
+"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
+"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+"checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9"
+"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "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..085d7c4152
--- /dev/null
+++ b/third_party/rust/audio-mixer/Cargo.toml
@@ -0,0 +1,35 @@
+# 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 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]
+edition = "2018"
+name = "audio-mixer"
+version = "0.1.2"
+authors = ["Chun-Min Chang <chun.m.chang@gmail.com>"]
+description = "Mixing audio by the input and output channel layout"
+keywords = ["audio", "mixer", "mixing"]
+categories = ["multimedia::audio"]
+license = "MPL-2.0"
+repository = "https://github.com/ChunMinChang/audio-mixer"
+
+[[bench]]
+name = "benchmark"
+harness = false
+[dependencies.bitflags]
+version = "1.2"
+[dev-dependencies.criterion]
+version = "0.3"
+
+[dev-dependencies.float-cmp]
+version = "0.6"
+[badges.travis-ci]
+repository = "ChunMinChang/audio-mixer"
diff --git a/third_party/rust/audio-mixer/README.md b/third_party/rust/audio-mixer/README.md
new file mode 100644
index 0000000000..79afaccac2
--- /dev/null
+++ b/third_party/rust/audio-mixer/README.md
@@ -0,0 +1,18 @@
+# Audio Mixer
+
+[![Build Status](https://travis-ci.com/ChunMinChang/audio-mixer.svg?branch=master)](https://travis-ci.com/ChunMinChang/audio-mixer)
+
+Mixing audio data from any input channel layout to any output channel layout.
+
+The code here is a refactored version from [cubeb][cubeb]'s [cubeb_mixer(_C/C++_)][cubeb_mixer], which adapts the code from [FFmpeg libswresample's rematrix.c][rematrix] .
+The original implementation is in [cubeb-coreaudio-rs][cubeb-coreaudio-rs]'s [PR23][pr23]
+
+## License
+
+MPL-2
+
+[cubeb]: https://github.com/kinetiknz/cubeb
+[cubeb_mixer]: https://github.com/kinetiknz/cubeb/blob/aa636017aca8f79ebc95fb9f03b9333eaf9fd8fc/src/cubeb_mixer.cpp
+[rematrix]: https://github.com/FFmpeg/FFmpeg/blob/4fa2d5a692f40c398a299acf2c6a20f5b98a3708/libswresample/rematrix.c
+[cubeb-coreaudio-rs]: https://github.com/ChunMinChang/cubeb-coreaudio-rs/
+[pr23]: https://github.com/ChunMinChang/cubeb-coreaudio-rs/pull/23 \ No newline at end of file
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/src/channel.rs b/third_party/rust/audio-mixer/src/channel.rs
new file mode 100644
index 0000000000..ef58018ad9
--- /dev/null
+++ b/third_party/rust/audio-mixer/src/channel.rs
@@ -0,0 +1,85 @@
+// 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 {
+ bits: Self::FRONT_LEFT.bits() | Self::FRONT_RIGHT.bits(),
+ };
+ pub const BACK_2: Self = Self {
+ bits: Self::BACK_LEFT.bits() | Self::BACK_RIGHT.bits(),
+ };
+ pub const FRONT_2_OF_CENTER: Self = Self {
+ bits: Self::FRONT_LEFT_OF_CENTER.bits() | Self::FRONT_RIGHT_OF_CENTER.bits(),
+ };
+ pub const SIDE_2: Self = Self {
+ bits: Self::SIDE_LEFT.bits() | Self::SIDE_RIGHT.bits(),
+ };
+}
+
+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..82ff97dbcf
--- /dev/null
+++ b/third_party/rust/audio-mixer/src/coefficient.rs
@@ -0,0 +1,754 @@
+// 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.clone().number();
+ let mut coefficients = Vec::with_capacity(input_channels.len());
+ for input_channel in input_channels {
+ let input_channel_index = input_channel.clone().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>;
+
+ 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..10bf54603e
--- /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..edd6932d77
--- /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(), &mut 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(), &mut output_buffer.as_mut_slice());
+ println!("{:?} is mixed to {:?}", input_buffer, output_buffer);
+}