Bug 1605471 - Update cubeb-coreaudio to acb90e9. r=alwu

Pick commits:
acb90e9 - Add license for mixer crate
82e7ff9 - Move criterion crate to [dev-dependencies]
f8b7d96 - Implement a new any-to-any mixer (#23)
ae43813 - Remove the test_set_channel_layout_input (#32)
9d0a0e8 - Improve device switch tests (#14)
833a062 - Enable test_ops_stream_register_device_changed_callback (#33)

Differential Revision: https://phabricator.services.mozilla.com/D58177

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Chun-Min Chang 2019-12-27 16:58:18 +00:00
Родитель 2627475cc8
Коммит 7e8bb1db07
20 изменённых файлов: 1709 добавлений и 626 удалений

Просмотреть файл

@ -65,7 +65,7 @@ replace-with = "vendored-sources"
[source."https://github.com/ChunMinChang/cubeb-coreaudio-rs"]
git = "https://github.com/ChunMinChang/cubeb-coreaudio-rs"
replace-with = "vendored-sources"
rev = "868d847c2e95096b6eec629dfed77ef363363ecb"
rev = "acb90e9bf36e6e035ac6bbe51efa0a8825b5b6be"
[source.crates-io]
replace-with = "vendored-sources"

22
Cargo.lock сгенерированный
Просмотреть файл

@ -626,7 +626,7 @@ dependencies = [
[[package]]
name = "coreaudio-sys-utils"
version = "0.1.0"
source = "git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=868d847c2e95096b6eec629dfed77ef363363ecb#868d847c2e95096b6eec629dfed77ef363363ecb"
source = "git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=acb90e9bf36e6e035ac6bbe51efa0a8825b5b6be#acb90e9bf36e6e035ac6bbe51efa0a8825b5b6be"
dependencies = [
"core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"coreaudio-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
@ -848,15 +848,16 @@ dependencies = [
[[package]]
name = "cubeb-coreaudio"
version = "0.1.0"
source = "git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=868d847c2e95096b6eec629dfed77ef363363ecb#868d847c2e95096b6eec629dfed77ef363363ecb"
source = "git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=acb90e9bf36e6e035ac6bbe51efa0a8825b5b6be#acb90e9bf36e6e035ac6bbe51efa0a8825b5b6be"
dependencies = [
"atomic 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"coreaudio-sys-utils 0.1.0 (git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=868d847c2e95096b6eec629dfed77ef363363ecb)",
"coreaudio-sys-utils 0.1.0 (git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=acb90e9bf36e6e035ac6bbe51efa0a8825b5b6be)",
"cubeb-backend 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)",
"mach 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"mixer 0.1.0 (git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=acb90e9bf36e6e035ac6bbe51efa0a8825b5b6be)",
]
[[package]]
@ -1473,7 +1474,7 @@ dependencies = [
"cert_storage 0.0.1",
"chardetng_c 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cose-c 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"cubeb-coreaudio 0.1.0 (git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=868d847c2e95096b6eec629dfed77ef363363ecb)",
"cubeb-coreaudio 0.1.0 (git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=acb90e9bf36e6e035ac6bbe51efa0a8825b5b6be)",
"cubeb-pulse 0.3.0 (git+https://github.com/djg/cubeb-pulse-rs?rev=8069f8f4189982e0b38fa6dc8993dd4fab41f728)",
"cubeb-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"encoding_glue 0.1.0",
@ -2241,6 +2242,14 @@ dependencies = [
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "mixer"
version = "0.1.0"
source = "git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=acb90e9bf36e6e035ac6bbe51efa0a8825b5b6be#acb90e9bf36e6e035ac6bbe51efa0a8825b5b6be"
dependencies = [
"bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "moz_cbor"
version = "0.1.1"
@ -4696,7 +4705,7 @@ dependencies = [
"checksum core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)" = "56790968ab1c8a1202a102e6de05fc6e1ec87da99e4e93e9a7d13efbfc1e95a9"
"checksum core-text 13.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f3f46450d6f2397261af420b4ccce23807add2e45fa206410a03d66fb7f050ae"
"checksum coreaudio-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e8f5954c1c7ccb55340443e8b29fca24013545a5e7d72c1ca7db4fc02b982ce"
"checksum coreaudio-sys-utils 0.1.0 (git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=868d847c2e95096b6eec629dfed77ef363363ecb)" = "<none>"
"checksum coreaudio-sys-utils 0.1.0 (git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=acb90e9bf36e6e035ac6bbe51efa0a8825b5b6be)" = "<none>"
"checksum cose 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "72fa26cb151d3ae4b70f63d67d0fed57ce04220feafafbae7f503bef7aae590d"
"checksum cose-c 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "49726015ab0ca765144fcca61e4a7a543a16b795a777fa53f554da2fffff9a94"
"checksum cranelift-bforest 0.51.0 (git+https://github.com/bytecodealliance/cranelift?rev=ec787eb281bb2e18e191508c17abe694e91f0677)" = "<none>"
@ -4720,7 +4729,7 @@ dependencies = [
"checksum cubeb 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3cbcdfde9ea319160af6eff068ffaa96aad3532e1b5c0ebc134614cfacacae24"
"checksum cubeb-backend 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5a1e7add4e7642a8aebb24172922318482bed52389a12cb339f728bbd4c4ed9c"
"checksum cubeb-core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfd9b2ea1cb6afed9419b0d18fc4093df552ccb2300eb57793629f8cd370b4c8"
"checksum cubeb-coreaudio 0.1.0 (git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=868d847c2e95096b6eec629dfed77ef363363ecb)" = "<none>"
"checksum cubeb-coreaudio 0.1.0 (git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=acb90e9bf36e6e035ac6bbe51efa0a8825b5b6be)" = "<none>"
"checksum cubeb-pulse 0.3.0 (git+https://github.com/djg/cubeb-pulse-rs?rev=8069f8f4189982e0b38fa6dc8993dd4fab41f728)" = "<none>"
"checksum cubeb-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "309c5839c5fa03c08363bd308566cbe4654b25a9984342d7546a33d55b80a3d6"
"checksum d3d12 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc7ed48e89905e5e146bcc1951cc3facb9e44aea9adf5dc01078cda1bd24b662"
@ -4834,6 +4843,7 @@ dependencies = [
"checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125"
"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
"checksum miow 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "396aa0f2003d7df8395cb93e09871561ccc3e785f0acb369170e8cc74ddf9226"
"checksum mixer 0.1.0 (git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=acb90e9bf36e6e035ac6bbe51efa0a8825b5b6be)" = "<none>"
"checksum moz_cbor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20c82a57087fd5990d7122dbff1607c3b20c3d2958e9d9ad9765aab415e2c91c"
"checksum mp4parse_fallible 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6626c2aef76eb8f984eef02e475883d3fe9112e114720446c5810fc5f045cd30"
"checksum msdos_time 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "aad9dfe950c057b1bfe9c1f2aa51583a8468ef2a5baba2ebbe06d775efeb7729"

Просмотреть файл

@ -1 +1 @@
{"files":{".editorconfig":"4e53b182bcc78b83d7e1b5c03efa14d22d4955c4ed2514d1ba4e99c1eb1a50ba",".travis.yml":"bea421508af5f4d00b941866dae0ae7d94a51b9276688a7f626686e8ed8fbbf3","Cargo.toml":"d1b1a03695cf8c92daa58cccb391a03b1d0b82a70505424c25fa40aba19411af","LICENSE":"6e6f56aff5bbf3cbc60747e152fb1a719bd0716aaf6d711c554f57d92e96297c","README.md":"72d8a890d6bda3cdba393432e5ae2018a385980785ebb2b96e9c3f82a48a1b59","run_tests.sh":"c764a89fe2a6b7ccdeb01895d29b2017c5b76f468aff456050babd66c1472687","src/backend/aggregate_device.rs":"7cd732f3e1e71876753515b26ee69a315414e58869216e99ff95c6828408c4db","src/backend/auto_array.rs":"5f35545baba2b005e13a2225bd1cbdd94ffc2097554d61479929bfc5442a6dd6","src/backend/auto_release.rs":"050fdcee74cf46b9a8a85a877e166d72a853d33220f59cf734cbb6ea09daa441","src/backend/device_property.rs":"1b066b48ed09026a9286b1b8f40e2720854c3410b0f02c795a580792fda34ea9","src/backend/mixer.rs":"74dcac459493e2f919b61ed3bebe500027e422eb06b1ecd30b73a47079c61f7c","src/backend/mod.rs":"67b445d216cad0658c3844cd608ddf1bfc8ab1559ffbee8edf5c340e08a9ce77","src/backend/resampler.rs":"fd1281d28a4db1659d2f75e43b8457651745e1b6eb5a53a77f04d752135f6dc7","src/backend/tests/aggregate_device.rs":"107f5c637844cd5ae43d2b42cec4ef3369bb702751586078c0a9d50f039161cd","src/backend/tests/api.rs":"d76c1574179085e0c1342614cd674e5aa211dd456f3725aeb294def6b32750fd","src/backend/tests/backlog.rs":"3b189a7e036543c467cc242af0ed3332721179ee2b1c8847a6db563546f1ac52","src/backend/tests/device_change.rs":"bebda3dbbcd432d7c545dbe27c388773f61f68a1c8b947f61deb358682dbe53b","src/backend/tests/device_property.rs":"b1a9ae79aa5b9a3f180040d0ef0954b134680d586882d2062c5e017b555bff57","src/backend/tests/interfaces.rs":"01fc2d54ddb50f014a44f9c137f078645738bcc81e48140a3e7ae68e18a61b6b","src/backend/tests/manual.rs":"c0b8888e1f7fc09913862300023452753384e038b7433e3975b772988925f52c","src/backend/tests/mod.rs":"8dba770023d7f9c4228f0e11915347f0e07da5fd818e3ee4478c4b197af9aa2a","src/backend/tests/parallel.rs":"f9e1883660d6146b6e5075806561f5f689810e25c5e7764dfd28c9b939821a49","src/backend/tests/tone.rs":"16150438317ce501986734167b5fb97bfec567228acbcd8f3b4c4484c22f29e0","src/backend/tests/utils.rs":"981e4669902231d10f8c3d5c39d82358acc8159f026b8be27f4ab76ef19fcda3","src/backend/utils.rs":"ee77bc266d672d3d9e23eb3290c1f897687394c6e459338804a17433380a6fd2","src/capi.rs":"61f8f0c4373adaefba1eb6e7084687e83a10136db96438bc35884327668e411f","src/lib.rs":"1ff4b738ed194061fca4ff745f847dea4de4e7a4fa1f898e7b4ad5e70c62386d","todo.md":"a66296c220cad24d08ee780308007a702f7e421edf0bb60464c3ce8feeda1882"},"package":null}
{"files":{".editorconfig":"4e53b182bcc78b83d7e1b5c03efa14d22d4955c4ed2514d1ba4e99c1eb1a50ba",".travis.yml":"bea421508af5f4d00b941866dae0ae7d94a51b9276688a7f626686e8ed8fbbf3","Cargo.toml":"d0fa3332c5101548d5d52afe3ed57488c6d221189c20f9316fc5930f41a50281","LICENSE":"6e6f56aff5bbf3cbc60747e152fb1a719bd0716aaf6d711c554f57d92e96297c","README.md":"72d8a890d6bda3cdba393432e5ae2018a385980785ebb2b96e9c3f82a48a1b59","run_tests.sh":"c764a89fe2a6b7ccdeb01895d29b2017c5b76f468aff456050babd66c1472687","src/backend/aggregate_device.rs":"7cd732f3e1e71876753515b26ee69a315414e58869216e99ff95c6828408c4db","src/backend/auto_array.rs":"5f35545baba2b005e13a2225bd1cbdd94ffc2097554d61479929bfc5442a6dd6","src/backend/auto_release.rs":"050fdcee74cf46b9a8a85a877e166d72a853d33220f59cf734cbb6ea09daa441","src/backend/device_property.rs":"1b066b48ed09026a9286b1b8f40e2720854c3410b0f02c795a580792fda34ea9","src/backend/mixer.rs":"538b53b66695303b9c330c452cad346c0cc27d5064a3fbc63ad8fa29afb4b70a","src/backend/mod.rs":"c8904e1498383f56a86b65aa150325a8c6eb44a25fef854b40efd34b8fa18dff","src/backend/resampler.rs":"fd1281d28a4db1659d2f75e43b8457651745e1b6eb5a53a77f04d752135f6dc7","src/backend/tests/aggregate_device.rs":"107f5c637844cd5ae43d2b42cec4ef3369bb702751586078c0a9d50f039161cd","src/backend/tests/api.rs":"0c78b255921a5c13310132134bb194fc6d9c6fbe90fd05624b82b552711c5a8a","src/backend/tests/backlog.rs":"3b189a7e036543c467cc242af0ed3332721179ee2b1c8847a6db563546f1ac52","src/backend/tests/device_change.rs":"8533df1357c154922200a706bbb21d20007fd3a343d9c47697314f91c1c6a281","src/backend/tests/device_property.rs":"b1a9ae79aa5b9a3f180040d0ef0954b134680d586882d2062c5e017b555bff57","src/backend/tests/interfaces.rs":"1db1aa665a7f85968ca084ce1be725d182b00be5d7fa28d8a3cf178df145f43e","src/backend/tests/manual.rs":"dc707836dab31f83d4b325afbc4dc4c8104ac8036e87f59ade3309ee83fe2d3f","src/backend/tests/mod.rs":"8dba770023d7f9c4228f0e11915347f0e07da5fd818e3ee4478c4b197af9aa2a","src/backend/tests/parallel.rs":"f9e1883660d6146b6e5075806561f5f689810e25c5e7764dfd28c9b939821a49","src/backend/tests/tone.rs":"16150438317ce501986734167b5fb97bfec567228acbcd8f3b4c4484c22f29e0","src/backend/tests/utils.rs":"b74f10216bd02d5336f46db371c9c8e1a648deead84068d9397eca03986aae1e","src/backend/utils.rs":"ee77bc266d672d3d9e23eb3290c1f897687394c6e459338804a17433380a6fd2","src/capi.rs":"61f8f0c4373adaefba1eb6e7084687e83a10136db96438bc35884327668e411f","src/lib.rs":"1ff4b738ed194061fca4ff745f847dea4de4e7a4fa1f898e7b4ad5e70c62386d","todo.md":"29545b4d9c516396f82bd392797e2713d4602036eaba0f151b384af764f8515f"},"package":null}

1
third_party/rust/cubeb-coreaudio/Cargo.toml поставляемый
Просмотреть файл

@ -15,3 +15,4 @@ cubeb-backend = "0.6"
libc = "0.2"
lazy_static = "1.2"
mach = "0.3"
mixer = { path = "mixer" }

Просмотреть файл

@ -1,18 +1,159 @@
use super::auto_release::*;
use super::utils::cubeb_sample_size;
use cubeb_backend::{ffi, ChannelLayout, SampleFormat};
use cubeb_backend::{ChannelLayout, SampleFormat};
use std::mem;
use std::os::raw::{c_int, c_void};
use std::ptr;
extern crate mixer;
pub use self::mixer::Channel;
const CHANNEL_OERDER: [mixer::Channel; mixer::Channel::count()] = [
mixer::Channel::FrontLeft,
mixer::Channel::FrontRight,
mixer::Channel::FrontCenter,
mixer::Channel::LowFrequency,
mixer::Channel::BackLeft,
mixer::Channel::BackRight,
mixer::Channel::FrontLeftOfCenter,
mixer::Channel::FrontRightOfCenter,
mixer::Channel::BackCenter,
mixer::Channel::SideLeft,
mixer::Channel::SideRight,
mixer::Channel::TopCenter,
mixer::Channel::TopFrontLeft,
mixer::Channel::TopFrontCenter,
mixer::Channel::TopFrontRight,
mixer::Channel::TopBackLeft,
mixer::Channel::TopBackCenter,
mixer::Channel::TopBackRight,
mixer::Channel::Silence,
];
pub fn get_channel_order(channel_layout: ChannelLayout) -> Vec<mixer::Channel> {
let mut map = channel_layout.bits();
let mut order = Vec::new();
let mut channel_index: usize = 0;
while map != 0 {
if map & 1 == 1 {
order.push(CHANNEL_OERDER[channel_index]);
}
map >>= 1;
channel_index += 1;
}
order
}
fn get_default_channel_order(channel_count: usize) -> Vec<mixer::Channel> {
assert_ne!(channel_count, 0);
let mut channels = Vec::with_capacity(channel_count);
for i in 0..channel_count {
channels.push(if i < CHANNEL_OERDER.len() {
CHANNEL_OERDER[i]
} else {
mixer::Channel::Silence
});
}
channels
}
#[derive(Debug)]
enum MixerType {
IntegerMixer(mixer::Mixer<i16>),
FloatMixer(mixer::Mixer<f32>),
}
impl MixerType {
fn new(
format: SampleFormat,
input_channels: Vec<mixer::Channel>,
output_channels: Vec<mixer::Channel>,
) -> Self {
match format {
SampleFormat::S16LE | SampleFormat::S16BE | SampleFormat::S16NE => {
cubeb_log!("Create an integer type(i16) mixer");
Self::IntegerMixer(mixer::Mixer::<i16>::new(input_channels, output_channels))
}
SampleFormat::Float32LE | SampleFormat::Float32BE | SampleFormat::Float32NE => {
cubeb_log!("Create an floating type(f32) mixer");
Self::FloatMixer(mixer::Mixer::<f32>::new(input_channels, output_channels))
}
}
}
fn sample_size(&self) -> usize {
match self {
MixerType::IntegerMixer(_) => mem::size_of::<i16>(),
MixerType::FloatMixer(_) => mem::size_of::<f32>(),
}
}
fn input_channels(&self) -> &[Channel] {
match self {
MixerType::IntegerMixer(m) => m.input_channels(),
MixerType::FloatMixer(m) => m.input_channels(),
}
}
fn output_channels(&self) -> &[Channel] {
match self {
MixerType::IntegerMixer(m) => m.output_channels(),
MixerType::FloatMixer(m) => m.output_channels(),
}
}
fn mix(
&self,
input_buffer_ptr: *const u8,
input_buffer_size: usize,
output_buffer_ptr: *mut u8,
output_buffer_size: usize,
frames: usize,
) {
use std::slice;
// Check input buffer size.
let size_needed = frames * self.input_channels().len() * self.sample_size();
assert!(input_buffer_size >= size_needed);
// Check output buffer size.
let size_needed = frames * self.output_channels().len() * self.sample_size();
assert!(output_buffer_size >= size_needed);
match self {
MixerType::IntegerMixer(m) => {
let in_buf_ptr = input_buffer_ptr as *const i16;
let out_buf_ptr = output_buffer_ptr as *mut i16;
let input_buffer = unsafe {
slice::from_raw_parts(in_buf_ptr, frames * self.input_channels().len())
};
let output_buffer = unsafe {
slice::from_raw_parts_mut(out_buf_ptr, frames * self.output_channels().len())
};
let mut in_buf = input_buffer.chunks(self.input_channels().len());
let mut out_buf = output_buffer.chunks_mut(self.output_channels().len());
for _ in 0..frames {
m.mix(in_buf.next().unwrap(), out_buf.next().unwrap());
}
}
MixerType::FloatMixer(m) => {
let in_buf_ptr = input_buffer_ptr as *const f32;
let out_buf_ptr = output_buffer_ptr as *mut f32;
let input_buffer = unsafe {
slice::from_raw_parts(in_buf_ptr, frames * self.input_channels().len())
};
let output_buffer = unsafe {
slice::from_raw_parts_mut(out_buf_ptr, frames * self.output_channels().len())
};
let mut in_buf = input_buffer.chunks(self.input_channels().len());
let mut out_buf = output_buffer.chunks_mut(self.output_channels().len());
for _ in 0..frames {
m.mix(in_buf.next().unwrap(), out_buf.next().unwrap());
}
}
};
}
}
#[derive(Debug)]
pub struct Mixer {
mixer: AutoRelease<ffi::cubeb_mixer>,
format: SampleFormat,
in_channels: u32,
in_layout: ChannelLayout,
out_channels: u32,
out_layout: ChannelLayout,
mixer: MixerType,
// Only accessed from callback thread.
buffer: Vec<u8>,
}
@ -20,34 +161,54 @@ pub struct Mixer {
impl Mixer {
pub fn new(
format: SampleFormat,
in_channels: u32,
in_layout: ChannelLayout,
out_channels: u32,
out_layout: ChannelLayout,
in_channel_count: usize,
input_layout: ChannelLayout,
out_channel_count: usize,
mut output_channels: Vec<mixer::Channel>,
) -> Self {
let raw_mixer = unsafe {
ffi::cubeb_mixer_create(
format.into(),
in_channels,
in_layout.into(),
out_channels,
out_layout.into(),
)
cubeb_log!(
"Create a mixer with input channel count: {}, input layout: {:?}, \
out channel count: {}, output channels: {:?}",
in_channel_count,
input_layout,
out_channel_count,
output_channels
);
let input_channels = if in_channel_count as u32 != input_layout.bits().count_ones() {
cubeb_log!("Mismatch between input channels and layout. Apply default layout instead");
get_default_channel_order(in_channel_count)
} else {
get_channel_order(input_layout)
};
assert!(!raw_mixer.is_null(), "Failed to create mixer");
// When having one or two channel, force mono or stereo. Some devices (namely,
// Bose QC35, mark 1 and 2), expose a single channel mapped to the right for
// some reason.
// TODO: Only apply this setting when device is Bose QC35 (by device_property.rs).
if out_channel_count == 1 {
output_channels = vec![mixer::Channel::FrontCenter];
} else if out_channel_count == 2 {
output_channels = vec![mixer::Channel::FrontLeft, mixer::Channel::FrontRight];
}
let all_silence = vec![mixer::Channel::Silence; out_channel_count];
if output_channels.len() == 0
|| out_channel_count != output_channels.len()
|| all_silence == output_channels
{
cubeb_log!("Mismatch between output channels and layout. Apply default layout instead");
output_channels = get_default_channel_order(out_channel_count);
}
Self {
mixer: AutoRelease::new(raw_mixer, ffi::cubeb_mixer_destroy),
format,
in_channels,
in_layout,
out_channels,
out_layout,
mixer: MixerType::new(format, input_channels, output_channels),
buffer: Vec::new(),
}
}
pub fn update_buffer_size(&mut self, frames: usize) -> bool {
let size_needed = frames * self.in_channels as usize * cubeb_sample_size(self.format);
let size_needed = frames * self.mixer.input_channels().len() * self.mixer.sample_size();
let elements_needed = size_needed / mem::size_of::<u8>();
if self.buffer.len() < elements_needed {
self.buffer.resize(elements_needed, 0);
@ -61,58 +222,202 @@ impl Mixer {
self.buffer.as_mut_ptr()
}
// `update_buffer_size` must be called before this.
pub fn mix(&self, frames: usize, dest_buffer: *mut c_void, dest_buffer_size: usize) -> c_int {
let (src_buffer_ptr, src_buffer_size) = self.get_buffer_info();
self.mixer.mix(
src_buffer_ptr,
src_buffer_size,
dest_buffer as *mut u8,
dest_buffer_size,
frames,
);
0
}
fn get_buffer_info(&self) -> (*const u8, usize) {
(
self.buffer.as_ptr(),
self.buffer.len() * mem::size_of::<u8>(),
)
}
// `update_buffer_size` must be called before this.
pub fn mix(
&mut self,
frames: usize,
dest_buffer: *mut c_void,
dest_buffer_size: usize,
) -> c_int {
let size_needed = frames * self.in_channels as usize * cubeb_sample_size(self.format);
let (src_buffer_ptr, src_buffer_size) = self.get_buffer_info();
assert!(src_buffer_size >= size_needed);
unsafe {
ffi::cubeb_mixer_mix(
self.mixer.as_mut(),
frames,
src_buffer_ptr as *const c_void,
src_buffer_size,
dest_buffer,
dest_buffer_size,
)
}
}
fn destroy(&mut self) {
if !self.mixer.as_ptr().is_null() {
self.mixer.reset(ptr::null_mut());
}
}
}
impl Drop for Mixer {
fn drop(&mut self) {
self.destroy();
}
}
impl Default for Mixer {
fn default() -> Self {
Self {
mixer: AutoRelease::new(ptr::null_mut(), ffi::cubeb_mixer_destroy),
format: SampleFormat::Float32NE,
in_channels: 0,
in_layout: ChannelLayout::UNDEFINED,
out_channels: 0,
out_layout: ChannelLayout::UNDEFINED,
buffer: Vec::default(),
}
}
// This test gives a clear channel order of the ChannelLayout passed from cubeb interface.
#[test]
fn test_get_channel_order() {
assert_eq!(
get_channel_order(ChannelLayout::MONO),
[Channel::FrontCenter]
);
assert_eq!(
get_channel_order(ChannelLayout::MONO_LFE),
[Channel::FrontCenter, Channel::LowFrequency]
);
assert_eq!(
get_channel_order(ChannelLayout::STEREO),
[Channel::FrontLeft, Channel::FrontRight]
);
assert_eq!(
get_channel_order(ChannelLayout::STEREO_LFE),
[
Channel::FrontLeft,
Channel::FrontRight,
Channel::LowFrequency
]
);
assert_eq!(
get_channel_order(ChannelLayout::_3F),
[
Channel::FrontLeft,
Channel::FrontRight,
Channel::FrontCenter
]
);
assert_eq!(
get_channel_order(ChannelLayout::_3F_LFE),
[
Channel::FrontLeft,
Channel::FrontRight,
Channel::FrontCenter,
Channel::LowFrequency
]
);
assert_eq!(
get_channel_order(ChannelLayout::_2F1),
[Channel::FrontLeft, Channel::FrontRight, Channel::BackCenter]
);
assert_eq!(
get_channel_order(ChannelLayout::_2F1_LFE),
[
Channel::FrontLeft,
Channel::FrontRight,
Channel::LowFrequency,
Channel::BackCenter
]
);
assert_eq!(
get_channel_order(ChannelLayout::_3F1),
[
Channel::FrontLeft,
Channel::FrontRight,
Channel::FrontCenter,
Channel::BackCenter
]
);
assert_eq!(
get_channel_order(ChannelLayout::_3F1_LFE),
[
Channel::FrontLeft,
Channel::FrontRight,
Channel::FrontCenter,
Channel::LowFrequency,
Channel::BackCenter
]
);
assert_eq!(
get_channel_order(ChannelLayout::_2F2),
[
Channel::FrontLeft,
Channel::FrontRight,
Channel::SideLeft,
Channel::SideRight
]
);
assert_eq!(
get_channel_order(ChannelLayout::_2F2_LFE),
[
Channel::FrontLeft,
Channel::FrontRight,
Channel::LowFrequency,
Channel::SideLeft,
Channel::SideRight
]
);
assert_eq!(
get_channel_order(ChannelLayout::QUAD),
[
Channel::FrontLeft,
Channel::FrontRight,
Channel::BackLeft,
Channel::BackRight
]
);
assert_eq!(
get_channel_order(ChannelLayout::QUAD_LFE),
[
Channel::FrontLeft,
Channel::FrontRight,
Channel::LowFrequency,
Channel::BackLeft,
Channel::BackRight
]
);
assert_eq!(
get_channel_order(ChannelLayout::_3F2),
[
Channel::FrontLeft,
Channel::FrontRight,
Channel::FrontCenter,
Channel::SideLeft,
Channel::SideRight
]
);
assert_eq!(
get_channel_order(ChannelLayout::_3F2_LFE),
[
Channel::FrontLeft,
Channel::FrontRight,
Channel::FrontCenter,
Channel::LowFrequency,
Channel::SideLeft,
Channel::SideRight
]
);
assert_eq!(
get_channel_order(ChannelLayout::_3F2_BACK),
[
Channel::FrontLeft,
Channel::FrontRight,
Channel::FrontCenter,
Channel::BackLeft,
Channel::BackRight
]
);
assert_eq!(
get_channel_order(ChannelLayout::_3F2_LFE_BACK),
[
Channel::FrontLeft,
Channel::FrontRight,
Channel::FrontCenter,
Channel::LowFrequency,
Channel::BackLeft,
Channel::BackRight
]
);
assert_eq!(
get_channel_order(ChannelLayout::_3F3R_LFE),
[
Channel::FrontLeft,
Channel::FrontRight,
Channel::FrontCenter,
Channel::LowFrequency,
Channel::BackCenter,
Channel::SideLeft,
Channel::SideRight
]
);
assert_eq!(
get_channel_order(ChannelLayout::_3F4_LFE),
[
Channel::FrontLeft,
Channel::FrontRight,
Channel::FrontCenter,
Channel::LowFrequency,
Channel::BackLeft,
Channel::BackRight,
Channel::SideLeft,
Channel::SideRight
]
);
}

Просмотреть файл

@ -32,9 +32,8 @@ use self::resampler::*;
use self::utils::*;
use atomic;
use cubeb_backend::{
ffi, ChannelLayout, Context, ContextOps, DeviceCollectionRef, DeviceId, DeviceRef, DeviceType,
Error, Ops, Result, SampleFormat, State, Stream, StreamOps, StreamParams, StreamParamsRef,
StreamPrefs,
ffi, Context, ContextOps, DeviceCollectionRef, DeviceId, DeviceRef, DeviceType, Error, Ops,
Result, SampleFormat, State, Stream, StreamOps, StreamParams, StreamParamsRef, StreamPrefs,
};
use mach::mach_time::{mach_absolute_time, mach_timebase_info};
use std::cmp;
@ -137,64 +136,31 @@ impl device_property_listener {
#[derive(Debug, PartialEq)]
struct CAChannelLabel(AudioChannelLabel);
impl CAChannelLabel {
fn get_raw_label(&self) -> AudioChannelLabel {
self.0
}
}
impl From<ChannelLayout> for CAChannelLabel {
fn from(layout: ChannelLayout) -> Self {
// Make sure the layout is a channel (only one bit set to 1)
assert_eq!(layout.bits() & (layout.bits() - 1), 0);
let channel = match layout {
ChannelLayout::FRONT_LEFT => kAudioChannelLabel_Left,
ChannelLayout::FRONT_RIGHT => kAudioChannelLabel_Right,
ChannelLayout::FRONT_CENTER => kAudioChannelLabel_Center,
ChannelLayout::LOW_FREQUENCY => kAudioChannelLabel_LFEScreen,
ChannelLayout::BACK_LEFT => kAudioChannelLabel_LeftSurround,
ChannelLayout::BACK_RIGHT => kAudioChannelLabel_RightSurround,
ChannelLayout::FRONT_LEFT_OF_CENTER => kAudioChannelLabel_LeftCenter,
ChannelLayout::FRONT_RIGHT_OF_CENTER => kAudioChannelLabel_RightCenter,
ChannelLayout::BACK_CENTER => kAudioChannelLabel_CenterSurround,
ChannelLayout::SIDE_LEFT => kAudioChannelLabel_LeftSurroundDirect,
ChannelLayout::SIDE_RIGHT => kAudioChannelLabel_RightSurroundDirect,
ChannelLayout::TOP_CENTER => kAudioChannelLabel_TopCenterSurround,
ChannelLayout::TOP_FRONT_LEFT => kAudioChannelLabel_VerticalHeightLeft,
ChannelLayout::TOP_FRONT_CENTER => kAudioChannelLabel_VerticalHeightCenter,
ChannelLayout::TOP_FRONT_RIGHT => kAudioChannelLabel_VerticalHeightRight,
ChannelLayout::TOP_BACK_LEFT => kAudioChannelLabel_TopBackLeft,
ChannelLayout::TOP_BACK_CENTER => kAudioChannelLabel_TopBackCenter,
ChannelLayout::TOP_BACK_RIGHT => kAudioChannelLabel_TopBackRight,
_ => kAudioChannelLabel_Unknown,
};
Self(channel)
}
}
impl Into<ChannelLayout> for CAChannelLabel {
fn into(self) -> ChannelLayout {
impl Into<mixer::Channel> for CAChannelLabel {
fn into(self) -> mixer::Channel {
use self::coreaudio_sys_utils::sys;
match self.0 {
sys::kAudioChannelLabel_Left => ChannelLayout::FRONT_LEFT,
sys::kAudioChannelLabel_Right => ChannelLayout::FRONT_RIGHT,
sys::kAudioChannelLabel_Center => ChannelLayout::FRONT_CENTER,
sys::kAudioChannelLabel_LFEScreen => ChannelLayout::LOW_FREQUENCY,
sys::kAudioChannelLabel_LeftSurround => ChannelLayout::BACK_LEFT,
sys::kAudioChannelLabel_RightSurround => ChannelLayout::BACK_RIGHT,
sys::kAudioChannelLabel_LeftCenter => ChannelLayout::FRONT_LEFT_OF_CENTER,
sys::kAudioChannelLabel_RightCenter => ChannelLayout::FRONT_RIGHT_OF_CENTER,
sys::kAudioChannelLabel_CenterSurround => ChannelLayout::BACK_CENTER,
sys::kAudioChannelLabel_LeftSurroundDirect => ChannelLayout::SIDE_LEFT,
sys::kAudioChannelLabel_RightSurroundDirect => ChannelLayout::SIDE_RIGHT,
sys::kAudioChannelLabel_TopCenterSurround => ChannelLayout::TOP_CENTER,
sys::kAudioChannelLabel_VerticalHeightLeft => ChannelLayout::TOP_FRONT_LEFT,
sys::kAudioChannelLabel_VerticalHeightCenter => ChannelLayout::TOP_FRONT_CENTER,
sys::kAudioChannelLabel_VerticalHeightRight => ChannelLayout::TOP_FRONT_RIGHT,
sys::kAudioChannelLabel_TopBackLeft => ChannelLayout::TOP_BACK_LEFT,
sys::kAudioChannelLabel_TopBackCenter => ChannelLayout::TOP_BACK_CENTER,
sys::kAudioChannelLabel_TopBackRight => ChannelLayout::TOP_BACK_RIGHT,
_ => ChannelLayout::UNDEFINED,
sys::kAudioChannelLabel_Left => mixer::Channel::FrontLeft,
sys::kAudioChannelLabel_Right => mixer::Channel::FrontRight,
sys::kAudioChannelLabel_Center | sys::kAudioChannelLabel_Mono => {
mixer::Channel::FrontCenter
}
sys::kAudioChannelLabel_LFEScreen => mixer::Channel::LowFrequency,
sys::kAudioChannelLabel_LeftSurround => mixer::Channel::BackLeft,
sys::kAudioChannelLabel_RightSurround => mixer::Channel::BackRight,
sys::kAudioChannelLabel_LeftCenter => mixer::Channel::FrontLeftOfCenter,
sys::kAudioChannelLabel_RightCenter => mixer::Channel::FrontRightOfCenter,
sys::kAudioChannelLabel_CenterSurround => mixer::Channel::BackCenter,
sys::kAudioChannelLabel_LeftSurroundDirect => mixer::Channel::SideLeft,
sys::kAudioChannelLabel_RightSurroundDirect => mixer::Channel::SideRight,
sys::kAudioChannelLabel_TopCenterSurround => mixer::Channel::TopCenter,
sys::kAudioChannelLabel_VerticalHeightLeft => mixer::Channel::TopFrontLeft,
sys::kAudioChannelLabel_VerticalHeightCenter => mixer::Channel::TopFrontCenter,
sys::kAudioChannelLabel_VerticalHeightRight => mixer::Channel::TopFrontRight,
sys::kAudioChannelLabel_TopBackLeft => mixer::Channel::TopBackLeft,
sys::kAudioChannelLabel_TopBackCenter => mixer::Channel::TopBackCenter,
sys::kAudioChannelLabel_TopBackRight => mixer::Channel::TopBackRight,
_ => mixer::Channel::Silence,
}
}
}
@ -922,23 +888,14 @@ fn audiounit_get_default_device_id(devtype: DeviceType) -> AudioObjectID {
devid
}
fn audiounit_convert_channel_layout(layout: &AudioChannelLayout) -> ChannelLayout {
// When having one or two channel, force mono or stereo. Some devices (namely,
// Bose QC35, mark 1 and 2), expose a single channel mapped to the right for
// some reason.
if layout.mNumberChannelDescriptions == 1 {
return ChannelLayout::MONO;
} else if layout.mNumberChannelDescriptions == 2 {
return ChannelLayout::STEREO;
}
fn audiounit_convert_channel_layout(layout: &AudioChannelLayout) -> Vec<mixer::Channel> {
if layout.mChannelLayoutTag != kAudioChannelLayoutTag_UseChannelDescriptions {
// kAudioChannelLayoutTag_UseChannelBitmap
// kAudioChannelLayoutTag_Mono
// kAudioChannelLayoutTag_Stereo
// ....
cubeb_log!("Only handle UseChannelDescriptions for now.\n");
return ChannelLayout::UNDEFINED;
return Vec::new();
}
let channel_descriptions = unsafe {
@ -948,20 +905,16 @@ fn audiounit_convert_channel_layout(layout: &AudioChannelLayout) -> ChannelLayou
)
};
let mut cl = ChannelLayout::UNDEFINED;
let mut channels = Vec::with_capacity(layout.mNumberChannelDescriptions as usize);
for description in channel_descriptions {
let label = CAChannelLabel(description.mChannelLabel);
let channel: ChannelLayout = label.into();
if channel == ChannelLayout::UNDEFINED {
return ChannelLayout::UNDEFINED;
}
cl |= channel;
channels.push(label.into());
}
cl
channels
}
fn audiounit_get_preferred_channel_layout(output_unit: AudioUnit) -> ChannelLayout {
fn audiounit_get_preferred_channel_layout(output_unit: AudioUnit) -> Vec<mixer::Channel> {
let mut rv = NO_ERR;
let mut size: usize = 0;
rv = audio_unit_get_property_info(
@ -977,7 +930,7 @@ fn audiounit_get_preferred_channel_layout(output_unit: AudioUnit) -> ChannelLayo
"AudioUnitGetPropertyInfo/kAudioDevicePropertyPreferredChannelLayout rv={}",
rv
);
return ChannelLayout::UNDEFINED;
return Vec::new();
}
assert!(size > 0);
@ -995,13 +948,15 @@ fn audiounit_get_preferred_channel_layout(output_unit: AudioUnit) -> ChannelLayo
"AudioUnitGetProperty/kAudioDevicePropertyPreferredChannelLayout rv={}",
rv
);
return ChannelLayout::UNDEFINED;
return Vec::new();
}
audiounit_convert_channel_layout(layout.as_ref())
}
fn audiounit_get_current_channel_layout(output_unit: AudioUnit) -> ChannelLayout {
// This is for output AudioUnit only. Calling this by input-only AudioUnit is prone
// to crash intermittently.
fn audiounit_get_current_channel_layout(output_unit: AudioUnit) -> Vec<mixer::Channel> {
let mut rv = NO_ERR;
let mut size: usize = 0;
rv = audio_unit_get_property_info(
@ -1036,71 +991,12 @@ fn audiounit_get_current_channel_layout(output_unit: AudioUnit) -> ChannelLayout
"AudioUnitGetProperty/kAudioUnitProperty_AudioChannelLayout rv={}",
rv
);
return ChannelLayout::UNDEFINED;
return Vec::new();
}
audiounit_convert_channel_layout(layout.as_ref())
}
fn audiounit_set_channel_layout(
unit: AudioUnit,
layout: ChannelLayout,
) -> std::result::Result<(), OSStatus> {
assert!(!unit.is_null());
if layout == ChannelLayout::UNDEFINED {
// We leave everything as-is...
return Ok(());
}
let nb_channels = unsafe { ffi::cubeb_channel_layout_nb_channels(layout.into()) };
// We do not use CoreAudio standard layout for lack of documentation on what
// the actual channel orders are. So we set a custom layout.
assert!(nb_channels >= 1);
let size = mem::size_of::<AudioChannelLayout>()
+ (nb_channels as usize - 1) * mem::size_of::<AudioChannelDescription>();
let mut au_layout = make_sized_audio_channel_layout(size);
au_layout.as_mut().mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
au_layout.as_mut().mNumberChannelDescriptions = nb_channels;
let channel_descriptions = unsafe {
slice::from_raw_parts_mut(
au_layout.as_mut().mChannelDescriptions.as_mut_ptr(),
nb_channels as usize,
)
};
let mut channels: usize = 0;
let mut channel_map: ffi::cubeb_channel_layout = layout.into();
let i = 0;
while channel_map != 0 {
assert!(channels < nb_channels as usize);
let channel = (channel_map & 1) << i;
if channel != 0 {
let layout = ChannelLayout::from(channel);
let label = CAChannelLabel::from(layout);
channel_descriptions[channels].mChannelLabel = label.get_raw_label();
channel_descriptions[channels].mChannelFlags = kAudioChannelFlags_AllOff;
channels += 1;
}
channel_map >>= 1;
}
let err = audio_unit_set_property(
unit,
kAudioUnitProperty_AudioChannelLayout,
kAudioUnitScope_Input,
AU_OUT_BUS,
au_layout.as_ref(),
size,
);
if err == NO_ERR {
Ok(())
} else {
Err(err)
}
}
fn start_audiounit(unit: AudioUnit) -> Result<()> {
let status = audio_output_unit_start(unit);
if status == NO_ERR {
@ -2348,7 +2244,7 @@ struct CoreStreamData<'ctx> {
input_hw_rate: f64,
output_hw_rate: f64,
// Channel layout of the output AudioUnit.
device_layout: ChannelLayout,
device_layout: Vec<mixer::Channel>,
// Hold the input samples in every input callback iteration.
// Only accessed on input/output callback thread and during initial configure.
input_linear_buffer: Option<Box<dyn AutoArrayWrapper>>,
@ -2389,7 +2285,7 @@ impl<'ctx> Default for CoreStreamData<'ctx> {
output_device: device_info::default(),
input_hw_rate: 0_f64,
output_hw_rate: 0_f64,
device_layout: ChannelLayout::UNDEFINED,
device_layout: Vec::new(),
input_linear_buffer: None,
default_input_listener: None,
default_output_listener: None,
@ -2434,7 +2330,7 @@ impl<'ctx> CoreStreamData<'ctx> {
output_device: out_dev,
input_hw_rate: 0_f64,
output_hw_rate: 0_f64,
device_layout: ChannelLayout::UNDEFINED,
device_layout: Vec::new(),
input_linear_buffer: None,
default_input_listener: None,
default_output_listener: None,
@ -2727,21 +2623,11 @@ impl<'ctx> CoreStreamData<'ctx> {
self.output_hw_rate = output_hw_desc.mSampleRate;
let hw_channels = output_hw_desc.mChannelsPerFrame;
// Set the input layout to match the output device layout.
self.device_layout = audiounit_get_current_channel_layout(self.output_unit);
let msg = match audiounit_set_channel_layout(self.output_unit, self.device_layout) {
Ok(_) => "successfully".to_string(),
Err(e) => format!("failed. Error: {}", e),
};
cubeb_log!(
"({:p}) Set output hardware layout to {:?} {}.",
self.stm_ptr,
self.device_layout,
msg
);
self.mixer = if hw_channels != self.output_stream_params.channels()
|| self.device_layout != self.output_stream_params.layout()
|| self.device_layout
!= mixer::get_channel_order(self.output_stream_params.layout())
{
cubeb_log!("Incompatible channel layouts detected, setting up remixer");
// We will be remixing the data before it reaches the output device.
@ -2754,10 +2640,10 @@ impl<'ctx> CoreStreamData<'ctx> {
self.output_desc.mBytesPerFrame * self.output_desc.mFramesPerPacket;
Some(Mixer::new(
self.output_stream_params.format(),
self.output_stream_params.channels(),
self.output_stream_params.channels() as usize,
self.output_stream_params.layout(),
hw_channels,
self.device_layout,
hw_channels as usize,
self.device_layout.clone(),
))
} else {
None

Просмотреть файл

@ -31,162 +31,6 @@ fn test_make_sized_audio_channel_layout_with_wrong_size() {
let _ = make_sized_audio_channel_layout(wrong_size);
}
// has_input
// ------------------------------------
// TODO
// has_output
// ------------------------------------
// TODO
// channel_label_to_cubeb_channel
// ------------------------------------
// Convert a CAChannelLabel into a ChannelLayout
#[test]
fn test_channel_label_to_cubeb_channel_layout() {
let pairs = [
(kAudioChannelLabel_Left, ChannelLayout::FRONT_LEFT),
(kAudioChannelLabel_Right, ChannelLayout::FRONT_RIGHT),
(kAudioChannelLabel_Center, ChannelLayout::FRONT_CENTER),
(kAudioChannelLabel_LFEScreen, ChannelLayout::LOW_FREQUENCY),
(kAudioChannelLabel_LeftSurround, ChannelLayout::BACK_LEFT),
(kAudioChannelLabel_RightSurround, ChannelLayout::BACK_RIGHT),
(
kAudioChannelLabel_LeftCenter,
ChannelLayout::FRONT_LEFT_OF_CENTER,
),
(
kAudioChannelLabel_RightCenter,
ChannelLayout::FRONT_RIGHT_OF_CENTER,
),
(
kAudioChannelLabel_CenterSurround,
ChannelLayout::BACK_CENTER,
),
(
kAudioChannelLabel_LeftSurroundDirect,
ChannelLayout::SIDE_LEFT,
),
(
kAudioChannelLabel_RightSurroundDirect,
ChannelLayout::SIDE_RIGHT,
),
(
kAudioChannelLabel_TopCenterSurround,
ChannelLayout::TOP_CENTER,
),
(
kAudioChannelLabel_VerticalHeightLeft,
ChannelLayout::TOP_FRONT_LEFT,
),
(
kAudioChannelLabel_VerticalHeightCenter,
ChannelLayout::TOP_FRONT_CENTER,
),
(
kAudioChannelLabel_VerticalHeightRight,
ChannelLayout::TOP_FRONT_RIGHT,
),
(kAudioChannelLabel_TopBackLeft, ChannelLayout::TOP_BACK_LEFT),
(
kAudioChannelLabel_TopBackCenter,
ChannelLayout::TOP_BACK_CENTER,
),
(
kAudioChannelLabel_TopBackRight,
ChannelLayout::TOP_BACK_RIGHT,
),
(kAudioChannelLabel_Unknown, ChannelLayout::UNDEFINED),
];
for (label, channel) in pairs.iter() {
let channel_label = CAChannelLabel(*label);
let layout: ChannelLayout = channel_label.into();
assert_eq!(layout, *channel);
}
}
// cubeb_channel_to_channel_label
// ------------------------------------
// Convert a ChannelLayout into a CAChannelLabel
#[test]
fn test_cubeb_channel_layout_to_channel_label() {
let pairs = [
(ChannelLayout::FRONT_LEFT, kAudioChannelLabel_Left),
(ChannelLayout::FRONT_RIGHT, kAudioChannelLabel_Right),
(ChannelLayout::FRONT_CENTER, kAudioChannelLabel_Center),
(ChannelLayout::LOW_FREQUENCY, kAudioChannelLabel_LFEScreen),
(ChannelLayout::BACK_LEFT, kAudioChannelLabel_LeftSurround),
(ChannelLayout::BACK_RIGHT, kAudioChannelLabel_RightSurround),
(
ChannelLayout::FRONT_LEFT_OF_CENTER,
kAudioChannelLabel_LeftCenter,
),
(
ChannelLayout::FRONT_RIGHT_OF_CENTER,
kAudioChannelLabel_RightCenter,
),
(
ChannelLayout::BACK_CENTER,
kAudioChannelLabel_CenterSurround,
),
(
ChannelLayout::SIDE_LEFT,
kAudioChannelLabel_LeftSurroundDirect,
),
(
ChannelLayout::SIDE_RIGHT,
kAudioChannelLabel_RightSurroundDirect,
),
(
ChannelLayout::TOP_CENTER,
kAudioChannelLabel_TopCenterSurround,
),
(
ChannelLayout::TOP_FRONT_LEFT,
kAudioChannelLabel_VerticalHeightLeft,
),
(
ChannelLayout::TOP_FRONT_CENTER,
kAudioChannelLabel_VerticalHeightCenter,
),
(
ChannelLayout::TOP_FRONT_RIGHT,
kAudioChannelLabel_VerticalHeightRight,
),
(ChannelLayout::TOP_BACK_LEFT, kAudioChannelLabel_TopBackLeft),
(
ChannelLayout::TOP_BACK_CENTER,
kAudioChannelLabel_TopBackCenter,
),
(
ChannelLayout::TOP_BACK_RIGHT,
kAudioChannelLabel_TopBackRight,
),
];
for (channel, label) in pairs.iter() {
let channel_label = CAChannelLabel(*label);
assert_eq!(CAChannelLabel::from(*channel), channel_label);
}
}
#[test]
#[should_panic]
fn test_cubeb_channel_layout_to_channel_label_with_invalid_channel() {
let _label = CAChannelLabel::from(ChannelLayout::_3F4_LFE);
}
#[test]
#[should_panic]
fn test_cubeb_channel_layout_to_channel_label_with_unknown_channel() {
assert_eq!(
ChannelLayout::from(ffi::CHANNEL_UNKNOWN),
ChannelLayout::UNDEFINED
);
let _label = CAChannelLabel::from(ChannelLayout::UNDEFINED);
}
// active_streams
// update_latency_by_adding_stream
// update_latency_by_removing_stream
@ -264,14 +108,6 @@ fn test_make_silent() {
}
}
// render_input
// ------------------------------------
// TODO
// input_callback
// ------------------------------------
// TODO
// minimum_resampling_input_frames
// ------------------------------------
#[test]
@ -310,10 +146,6 @@ fn test_minimum_resampling_input_frames_equal_input_output_rate() {
);
}
// output_callback
// ------------------------------------
// TODO
// create_device_info
// ------------------------------------
#[test]
@ -378,22 +210,6 @@ fn test_set_device_info_to_nonexistent_output_device() {
let _device = create_device_info(nonexistent_id, DeviceType::OUTPUT);
}
// reinit_stream
// ------------------------------------
// TODO
// reinit_stream_async
// ------------------------------------
// TODO
// event_addr_to_string
// ------------------------------------
// TODO
// property_listener_callback
// ------------------------------------
// TODO
// add_listener (for default output device)
// ------------------------------------
#[test]
@ -505,14 +321,6 @@ fn test_remove_listener_unknown_device() {
});
}
// install_system_changed_callback
// ------------------------------------
// TODO
// uninstall_system_changed_callback
// ------------------------------------
// TODO
// get_default_device_id
// ------------------------------------
#[test]
@ -555,26 +363,30 @@ fn test_get_default_device_id_with_inout_type() {
#[test]
fn test_convert_channel_layout() {
let pairs = [
// The single channel is mapped to mono now.
(vec![kAudioObjectUnknown], ChannelLayout::MONO),
(vec![kAudioChannelLabel_Mono], ChannelLayout::MONO),
// The dual channels are mapped to stereo now.
(vec![kAudioObjectUnknown], vec![mixer::Channel::Silence]),
(
vec![kAudioChannelLabel_Mono],
vec![mixer::Channel::FrontCenter],
),
(
vec![kAudioChannelLabel_Mono, kAudioChannelLabel_LFEScreen],
ChannelLayout::STEREO,
vec![mixer::Channel::FrontCenter, mixer::Channel::LowFrequency],
),
(
vec![kAudioChannelLabel_Left, kAudioChannelLabel_Right],
ChannelLayout::STEREO,
vec![mixer::Channel::FrontLeft, mixer::Channel::FrontRight],
),
// The Layouts containing any unknonwn channel will be mapped to UNDEFINED.
(
vec![
kAudioChannelLabel_Left,
kAudioChannelLabel_Right,
kAudioChannelLabel_Unknown,
],
ChannelLayout::UNDEFINED,
vec![
mixer::Channel::FrontLeft,
mixer::Channel::FrontRight,
mixer::Channel::Silence,
],
),
(
vec![
@ -582,7 +394,11 @@ fn test_convert_channel_layout() {
kAudioChannelLabel_Right,
kAudioChannelLabel_Unused,
],
ChannelLayout::UNDEFINED,
vec![
mixer::Channel::FrontLeft,
mixer::Channel::FrontRight,
mixer::Channel::Silence,
],
),
(
vec![
@ -590,7 +406,11 @@ fn test_convert_channel_layout() {
kAudioChannelLabel_Right,
kAudioChannelLabel_ForeignLanguage,
],
ChannelLayout::UNDEFINED,
vec![
mixer::Channel::FrontLeft,
mixer::Channel::FrontRight,
mixer::Channel::Silence,
],
),
// The SMPTE layouts.
(
@ -599,7 +419,11 @@ fn test_convert_channel_layout() {
kAudioChannelLabel_Right,
kAudioChannelLabel_LFEScreen,
],
ChannelLayout::STEREO_LFE,
vec![
mixer::Channel::FrontLeft,
mixer::Channel::FrontRight,
mixer::Channel::LowFrequency,
],
),
(
vec![
@ -607,7 +431,11 @@ fn test_convert_channel_layout() {
kAudioChannelLabel_Right,
kAudioChannelLabel_Center,
],
ChannelLayout::_3F,
vec![
mixer::Channel::FrontLeft,
mixer::Channel::FrontRight,
mixer::Channel::FrontCenter,
],
),
(
vec![
@ -616,7 +444,12 @@ fn test_convert_channel_layout() {
kAudioChannelLabel_Center,
kAudioChannelLabel_LFEScreen,
],
ChannelLayout::_3F_LFE,
vec![
mixer::Channel::FrontLeft,
mixer::Channel::FrontRight,
mixer::Channel::FrontCenter,
mixer::Channel::LowFrequency,
],
),
(
vec![
@ -624,7 +457,11 @@ fn test_convert_channel_layout() {
kAudioChannelLabel_Right,
kAudioChannelLabel_CenterSurround,
],
ChannelLayout::_2F1,
vec![
mixer::Channel::FrontLeft,
mixer::Channel::FrontRight,
mixer::Channel::BackCenter,
],
),
(
vec![
@ -633,7 +470,12 @@ fn test_convert_channel_layout() {
kAudioChannelLabel_CenterSurround,
kAudioChannelLabel_LFEScreen,
],
ChannelLayout::_2F1_LFE,
vec![
mixer::Channel::FrontLeft,
mixer::Channel::FrontRight,
mixer::Channel::BackCenter,
mixer::Channel::LowFrequency,
],
),
(
vec![
@ -642,7 +484,12 @@ fn test_convert_channel_layout() {
kAudioChannelLabel_Center,
kAudioChannelLabel_CenterSurround,
],
ChannelLayout::_3F1,
vec![
mixer::Channel::FrontLeft,
mixer::Channel::FrontRight,
mixer::Channel::FrontCenter,
mixer::Channel::BackCenter,
],
),
(
vec![
@ -652,7 +499,13 @@ fn test_convert_channel_layout() {
kAudioChannelLabel_CenterSurround,
kAudioChannelLabel_LFEScreen,
],
ChannelLayout::_3F1_LFE,
vec![
mixer::Channel::FrontLeft,
mixer::Channel::FrontRight,
mixer::Channel::FrontCenter,
mixer::Channel::BackCenter,
mixer::Channel::LowFrequency,
],
),
(
vec![
@ -661,7 +514,12 @@ fn test_convert_channel_layout() {
kAudioChannelLabel_LeftSurroundDirect,
kAudioChannelLabel_RightSurroundDirect,
],
ChannelLayout::_2F2,
vec![
mixer::Channel::FrontLeft,
mixer::Channel::FrontRight,
mixer::Channel::SideLeft,
mixer::Channel::SideRight,
],
),
(
vec![
@ -671,7 +529,13 @@ fn test_convert_channel_layout() {
kAudioChannelLabel_RightSurroundDirect,
kAudioChannelLabel_LFEScreen,
],
ChannelLayout::_2F2_LFE,
vec![
mixer::Channel::FrontLeft,
mixer::Channel::FrontRight,
mixer::Channel::SideLeft,
mixer::Channel::SideRight,
mixer::Channel::LowFrequency,
],
),
(
vec![
@ -680,7 +544,12 @@ fn test_convert_channel_layout() {
kAudioChannelLabel_LeftSurround,
kAudioChannelLabel_RightSurround,
],
ChannelLayout::QUAD,
vec![
mixer::Channel::FrontLeft,
mixer::Channel::FrontRight,
mixer::Channel::BackLeft,
mixer::Channel::BackRight,
],
),
(
vec![
@ -690,7 +559,13 @@ fn test_convert_channel_layout() {
kAudioChannelLabel_RightSurround,
kAudioChannelLabel_LFEScreen,
],
ChannelLayout::QUAD_LFE,
vec![
mixer::Channel::FrontLeft,
mixer::Channel::FrontRight,
mixer::Channel::BackLeft,
mixer::Channel::BackRight,
mixer::Channel::LowFrequency,
],
),
(
vec![
@ -700,7 +575,13 @@ fn test_convert_channel_layout() {
kAudioChannelLabel_LeftSurroundDirect,
kAudioChannelLabel_RightSurroundDirect,
],
ChannelLayout::_3F2,
vec![
mixer::Channel::FrontLeft,
mixer::Channel::FrontRight,
mixer::Channel::FrontCenter,
mixer::Channel::SideLeft,
mixer::Channel::SideRight,
],
),
(
vec![
@ -711,7 +592,14 @@ fn test_convert_channel_layout() {
kAudioChannelLabel_RightSurroundDirect,
kAudioChannelLabel_LFEScreen,
],
ChannelLayout::_3F2_LFE,
vec![
mixer::Channel::FrontLeft,
mixer::Channel::FrontRight,
mixer::Channel::FrontCenter,
mixer::Channel::SideLeft,
mixer::Channel::SideRight,
mixer::Channel::LowFrequency,
],
),
(
vec![
@ -721,7 +609,13 @@ fn test_convert_channel_layout() {
kAudioChannelLabel_RightSurround,
kAudioChannelLabel_Center,
],
ChannelLayout::_3F2_BACK,
vec![
mixer::Channel::FrontLeft,
mixer::Channel::FrontRight,
mixer::Channel::BackLeft,
mixer::Channel::BackRight,
mixer::Channel::FrontCenter,
],
),
(
vec![
@ -732,7 +626,14 @@ fn test_convert_channel_layout() {
kAudioChannelLabel_Center,
kAudioChannelLabel_LFEScreen,
],
ChannelLayout::_3F2_LFE_BACK,
vec![
mixer::Channel::FrontLeft,
mixer::Channel::FrontRight,
mixer::Channel::BackLeft,
mixer::Channel::BackRight,
mixer::Channel::FrontCenter,
mixer::Channel::LowFrequency,
],
),
(
vec![
@ -744,7 +645,15 @@ fn test_convert_channel_layout() {
kAudioChannelLabel_LeftSurroundDirect,
kAudioChannelLabel_RightSurroundDirect,
],
ChannelLayout::_3F3R_LFE,
vec![
mixer::Channel::FrontLeft,
mixer::Channel::FrontRight,
mixer::Channel::FrontCenter,
mixer::Channel::LowFrequency,
mixer::Channel::BackCenter,
mixer::Channel::SideLeft,
mixer::Channel::SideRight,
],
),
(
vec![
@ -757,7 +666,16 @@ fn test_convert_channel_layout() {
kAudioChannelLabel_LeftSurroundDirect,
kAudioChannelLabel_RightSurroundDirect,
],
ChannelLayout::_3F4_LFE,
vec![
mixer::Channel::FrontLeft,
mixer::Channel::FrontRight,
mixer::Channel::FrontCenter,
mixer::Channel::LowFrequency,
mixer::Channel::BackLeft,
mixer::Channel::BackRight,
mixer::Channel::SideLeft,
mixer::Channel::SideRight,
],
),
];
@ -794,8 +712,8 @@ fn test_convert_channel_layout() {
}
let layout_ref = unsafe { &(*(&layout as *const TestLayout as *const AudioChannelLayout)) };
assert_eq!(
audiounit_convert_channel_layout(layout_ref),
*expected_layout
&audiounit_convert_channel_layout(layout_ref),
expected_layout
);
}
}
@ -804,12 +722,13 @@ fn test_convert_channel_layout() {
// ------------------------------------
#[test]
fn test_get_preferred_channel_layout_output() {
const STEREO: [mixer::Channel; 2] = [mixer::Channel::FrontLeft, mixer::Channel::FrontRight];
// Predefined whitelist
use std::collections::HashMap;
let devices_layouts: HashMap<&'static str, ChannelLayout> = [
("hdpn", ChannelLayout::STEREO),
("ispk", ChannelLayout::STEREO),
("FApd", ChannelLayout::STEREO),
let devices_layouts: HashMap<&'static str, Vec<mixer::Channel>> = [
("hdpn", STEREO.to_vec()),
("ispk", STEREO.to_vec()),
("FApd", STEREO.to_vec()),
]
.into_iter()
.cloned()
@ -826,29 +745,26 @@ fn test_get_preferred_channel_layout_output() {
let unit = unit.unwrap();
if let Some(layout) = devices_layouts.get(source.as_str()) {
assert_eq!(
audiounit_get_preferred_channel_layout(unit.get_inner()),
*layout
&audiounit_get_preferred_channel_layout(unit.get_inner()),
layout
);
} else {
println!("Device {} is not in the whitelist.", source);
}
}
// TODO: Should it be banned ? It only works with output audiounit for now.
// #[test]
// fn test_get_preferred_channel_layout_input() {
// }
// get_current_channel_layout
// ------------------------------------
#[test]
fn test_get_current_channel_layout_output() {
const STEREO: [mixer::Channel; 2] = [mixer::Channel::FrontLeft, mixer::Channel::FrontRight];
// Predefined whitelist
use std::collections::HashMap;
let devices_layouts: HashMap<&'static str, ChannelLayout> = [
("hdpn", ChannelLayout::STEREO),
("ispk", ChannelLayout::STEREO),
("FApd", ChannelLayout::STEREO),
let devices_layouts: HashMap<&'static str, Vec<mixer::Channel>> = [
("hdpn", STEREO.to_vec()),
("ispk", STEREO.to_vec()),
("FApd", STEREO.to_vec()),
]
.into_iter()
.cloned()
@ -873,11 +789,6 @@ fn test_get_current_channel_layout_output() {
}
}
// TODO: Should it be banned ? It only works with output audiounit for now.
// #[test]
// fn test_get_current_channel_layout_input() {
// }
// create_stream_description
// ------------------------------------
#[test]
@ -928,81 +839,6 @@ fn test_create_stream_description() {
}
}
// set_channel_layout
// ------------------------------------
#[test]
fn test_set_channel_layout_output() {
// Predefined whitelist
use std::collections::HashMap;
let devices_layouts: HashMap<&'static str, ChannelLayout> = [
("hdpn", ChannelLayout::STEREO),
("ispk", ChannelLayout::STEREO),
("FApd", ChannelLayout::STEREO),
]
.into_iter()
.cloned()
.collect();
let source = test_get_default_source_name(Scope::Output);
let unit = test_get_default_audiounit(Scope::Output);
if source.is_none() || unit.is_none() {
println!("No output audiounit or device source name found.");
return;
}
let source = source.unwrap();
let unit = unit.unwrap();
if let Some(layout) = devices_layouts.get(source.as_str()) {
assert!(audiounit_set_channel_layout(unit.get_inner(), *layout).is_ok());
assert_eq!(
audiounit_get_current_channel_layout(unit.get_inner()),
*layout
);
} else {
println!("Device {} is not in the whitelist.", source);
}
}
#[test]
fn test_set_channel_layout_output_undefind() {
if let Some(unit) = test_get_default_audiounit(Scope::Output) {
// Get original layout.
let original_layout = audiounit_get_current_channel_layout(unit.get_inner());
// Leave layout as it is.
assert!(audiounit_set_channel_layout(unit.get_inner(), ChannelLayout::UNDEFINED).is_ok());
// Check the layout is same as the original one.
assert_eq!(
audiounit_get_current_channel_layout(unit.get_inner()),
original_layout
);
} else {
println!("No output audiounit.");
}
}
#[test]
fn test_set_channel_layout_input() {
if let Some(unit) = test_get_default_audiounit(Scope::Input) {
// Get original layout.
let original_layout = audiounit_get_current_channel_layout(unit.get_inner());
// Leave layout as it is.
assert!(audiounit_set_channel_layout(unit.get_inner(), ChannelLayout::UNDEFINED).is_ok());
// Check the layout is same as the original one.
assert_eq!(
audiounit_get_current_channel_layout(unit.get_inner()),
original_layout
);
} else {
println!("No input audiounit.");
}
}
#[test]
#[should_panic]
fn test_set_channel_layout_with_null_unit() {
assert!(audiounit_set_channel_layout(ptr::null_mut(), ChannelLayout::UNDEFINED).is_err());
}
// create_default_audiounit
// ------------------------------------
#[test]
@ -1290,30 +1126,6 @@ fn test_set_buffer_size_sync_by_scope_with_null_unit(scope: Scope) {
assert!(set_buffer_size_sync(unit, scope.into(), 2048).is_err());
}
// setup_stream
// ------------------------------------
// TODO
// stream_destroy_internal
// ------------------------------------
// TODO
// stream_destroy
// ------------------------------------
// TODO
// stream_start_internal
// ------------------------------------
// TODO
// stream_start
// ------------------------------------
// TODO
// stream_stop_internal
// ------------------------------------
// TODO
// get_volume, set_volume
// ------------------------------------
#[test]
@ -1359,10 +1171,6 @@ fn test_get_default_device_name() {
}
}
// strref_to_cstr_utf8
// ------------------------------------
// TODO
// is_device_a_type_of
// ------------------------------------
#[test]

Просмотреть файл

@ -44,7 +44,7 @@ fn test_switch_device_in_scope(scope: Scope) {
scope
);
let device_switcher = TestDeviceSwitcher::new(scope.clone());
let mut device_switcher = TestDeviceSwitcher::new(scope.clone());
let count = Arc::new(Mutex::new(0));
let also_count = Arc::clone(&count);
@ -59,7 +59,7 @@ fn test_switch_device_in_scope(scope: Scope) {
test_get_started_stream_in_scope(scope.clone(), move |_stream| loop {
thread::sleep(Duration::from_millis(500));
changed_watcher.prepare();
assert!(device_switcher.next().unwrap());
device_switcher.next();
changed_watcher.wait_for_change();
if changed_watcher.current_result() >= devices.len() {
break;
@ -354,8 +354,8 @@ fn test_register_device_changed_callback_to_check_default_device_changed(stm_typ
0
};
let input_device_switcher = TestDeviceSwitcher::new(Scope::Input);
let output_device_switcher = TestDeviceSwitcher::new(Scope::Output);
let mut input_device_switcher = TestDeviceSwitcher::new(Scope::Input);
let mut output_device_switcher = TestDeviceSwitcher::new(Scope::Output);
test_get_stream_with_device_changed_callback(
"stream: test callback for default device changed",
@ -378,7 +378,7 @@ fn test_register_device_changed_callback_to_check_default_device_changed(stm_typ
// switching for the default device again will be ignored.
while stream.switching_device.load(atomic::Ordering::SeqCst) {}
changed_watcher.prepare();
assert!(input_device_switcher.next().unwrap());
input_device_switcher.next();
changed_watcher.wait_for_change();
}
@ -387,7 +387,7 @@ fn test_register_device_changed_callback_to_check_default_device_changed(stm_typ
// switching for the default device again will be ignored.
while stream.switching_device.load(atomic::Ordering::SeqCst) {}
changed_watcher.prepare();
assert!(output_device_switcher.next().unwrap());
output_device_switcher.next();
changed_watcher.wait_for_change();
}
},

Просмотреть файл

@ -527,21 +527,22 @@ fn test_ops_stream_device_destroy() {
});
}
// Enable this after cubeb-rs version is updated to one that implements
// stream_register_device_changed_callback operation.
// #[test]
// fn test_ops_stream_register_device_changed_callback() {
// extern "C" fn callback(_: *mut c_void) {}
#[test]
fn test_ops_stream_register_device_changed_callback() {
extern "C" fn callback(_: *mut c_void) {}
// test_default_output_stream_operation("stream: register device changed callback", |stream| {
// assert_eq!(
// unsafe {
// OPS.stream_register_device_changed_callback.unwrap()(
// stream,
// Some(callback)
// )
// },
// ffi::CUBEB_OK
// );
// });
// }
test_default_output_stream_operation("stream: register device changed callback", |stream| {
assert_eq!(
unsafe { OPS.stream_register_device_changed_callback.unwrap()(stream, Some(callback)) },
ffi::CUBEB_OK
);
assert_eq!(
unsafe { OPS.stream_register_device_changed_callback.unwrap()(stream, Some(callback)) },
ffi::CUBEB_ERROR_INVALID_PARAMETER
);
assert_eq!(
unsafe { OPS.stream_register_device_changed_callback.unwrap()(stream, None) },
ffi::CUBEB_OK
);
});
}

Просмотреть файл

@ -19,7 +19,7 @@ fn test_switch_output_device() {
return;
}
let output_device_switcher = TestDeviceSwitcher::new(Scope::Output);
let mut output_device_switcher = TestDeviceSwitcher::new(Scope::Output);
// Make sure the parameters meet the requirements of AudioUnitContext::stream_init
// (in the comments).
@ -52,7 +52,7 @@ fn test_switch_output_device() {
assert_eq!(input.pop().unwrap(), '\n');
match input.as_str() {
"s" => {
assert!(output_device_switcher.next().unwrap());
output_device_switcher.next();
}
"q" => {
println!("Quit.");

Просмотреть файл

@ -293,6 +293,53 @@ pub fn test_get_devices_in_scope(scope: Scope) -> Vec<AudioObjectID> {
devices
}
fn test_print_devices_in_scope(devices: &Vec<AudioObjectID>, scope: Scope) {
println!(
"\n{:?} devices\n\
--------------------",
scope
);
for device in devices {
let info = TestDeviceInfo::new(*device, scope.clone());
print_info(&info);
}
println!("");
fn print_info(info: &TestDeviceInfo) {
println!("{:>4}: {}\n\tuid: {}", info.id, info.label, info.uid);
}
}
#[derive(Debug)]
struct TestDeviceInfo {
id: AudioObjectID,
label: String,
uid: String,
}
impl TestDeviceInfo {
fn new(id: AudioObjectID, scope: Scope) -> Self {
Self {
id,
label: Self::get_label(id, scope.clone()),
uid: Self::get_uid(id, scope),
}
}
fn get_label(id: AudioObjectID, scope: Scope) -> String {
match get_device_uid(id, scope.into()) {
Ok(uid) => uid.into_string(),
Err(status) => format!("Unknow. Error: {}", status).to_string(),
}
}
fn get_uid(id: AudioObjectID, scope: Scope) -> String {
match get_device_label(id, scope.into()) {
Ok(label) => label.into_string(),
Err(status) => format!("Unknown. Error: {}", status).to_string(),
}
}
}
pub fn test_device_channels_in_scope(
id: AudioObjectID,
scope: Scope,
@ -548,30 +595,39 @@ pub fn test_set_default_device(
pub struct TestDeviceSwitcher {
scope: Scope,
devices: Vec<AudioObjectID>,
current_device_index: usize,
}
impl TestDeviceSwitcher {
pub fn new(scope: Scope) -> Self {
Self {
scope: scope.clone(),
devices: test_get_devices_in_scope(scope),
}
}
pub fn next(&self) -> std::result::Result<bool, OSStatus> {
let current = test_get_default_device(self.scope.clone()).unwrap();
let mut index = self
.devices
let devices = test_get_devices_in_scope(scope.clone());
let current = test_get_default_device(scope.clone()).unwrap();
let index = devices
.iter()
.position(|device| *device == current)
.unwrap();
index = (index + 1) % self.devices.len();
let next = self.devices[index];
test_print_devices_in_scope(&devices, scope.clone());
Self {
scope: scope,
devices: devices,
current_device_index: index,
}
}
pub fn next(&mut self) {
let current = self.devices[self.current_device_index];
let next_index = (self.current_device_index + 1) % self.devices.len();
let next = self.devices[next_index];
println!(
"Switch device for {:?}: {} -> {}",
self.scope, current, next
);
test_set_default_device(next, self.scope.clone())
assert!(self.set_device(next).unwrap());
self.current_device_index = next_index;
}
fn set_device(&self, device: AudioObjectID) -> std::result::Result<bool, OSStatus> {
test_set_default_device(device, self.scope.clone())
}
}

4
third_party/rust/cubeb-coreaudio/todo.md поставляемый
Просмотреть файл

@ -71,6 +71,10 @@ and create a new one. It's easier than the current implementation.
sub devices list of the aggregate device
- Check the `name: CFStringRef` of the master device is not `NULL`
### Mixer
- Don't force output device to mono or stereo when the output device has one or two channel
- unless the output devicv is _Bose QC35, mark 1 and 2_.
## Interface to system types and APIs
- Check if we need `AudioDeviceID` and `AudioObjectID` at the same time
- Create wrapper for `AudioObjectGetPropertyData(Size)` with _qualifier_ info

1
third_party/rust/mixer/.cargo-checksum.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
{"files":{"Cargo.toml":"8de7d76d0444b2692fcca3197f1197a13a06575586c7f10b21d18ed641155138","benches/benchmark.rs":"52a51cc36d17e609e63d61de65e49e52fe0df33ba1949da6fad662e70bca7c9b","src/channel.rs":"9a5aad342674629b895f4a3c9b3a3e90f4c05ef1cd4cb0b524cf16afa754edd8","src/coefficient.rs":"c972f8ba510fa72674f4db71db0db45b0e11ff0e5da6a27316b9adef5f498247","src/lib.rs":"19482db0ec5421dbe2269b76736ba64dd50b9faa1d8586e2bcf24256192fa44c","src/main.rs":"f45aec40c4b3a01e63857a0f42ae0b6ab513fac0eb083635af99a755abc36a11"},"package":null}

18
third_party/rust/mixer/Cargo.toml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,18 @@
[package]
name = "mixer"
version = "0.1.0"
authors = ["Chun-Min Chang <chun.m.chang@gmail.com>"]
edition = "2018"
license = "MPL-2.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bitflags = "1.2"
[dev-dependencies]
criterion = "0.3"
[[bench]]
name = "benchmark"
harness = false

95
third_party/rust/mixer/benches/benchmark.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,95 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use mixer::{Channel, Mixer};
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 = vec![
Channel::FrontLeft,
Channel::FrontRight,
Channel::FrontCenter,
Channel::LowFrequency,
Channel::BackLeft,
Channel::BackRight,
];
let output_channels = vec![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 = vec![Channel::FrontCenter];
let output_channels = vec![Channel::FrontLeft, Channel::FrontRight];
mix::<T>(input_channels, output_channels, frames);
}
fn mix<T>(input_channels: Vec<Channel>, output_channels: Vec<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);

85
third_party/rust/mixer/src/channel.rs поставляемый Normal file
Просмотреть файл

@ -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")
}
}

682
third_party/rust/mixer/src/coefficient.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,682 @@
// 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: Vec<Channel>) -> Result<Self, Error> {
let channel_map = Self::get_channel_map(&channels)?;
Ok(Self {
channels,
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: Vec<Channel>, output_channels: Vec<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)]
fn test_create<T>()
where
T: MixingCoefficient,
T::Coef: Copy + Debug,
{
let input_channels = vec![
Channel::Silence,
Channel::FrontRight,
Channel::FrontLeft,
Channel::Silence,
Channel::FrontCenter,
Channel::BackCenter,
Channel::LowFrequency,
];
let output_channels = vec![Channel::Silence, Channel::FrontRight, Channel::FrontLeft];
let coefficient = Coefficient::<T>::create(input_channels.clone(), output_channels.clone());
println!(
"{:?} = {:?} * {:?}",
output_channels, coefficient.matrix, input_channels
);
}
#[test]
fn test_create_f32() {
test_create::<f32>();
}
#[test]
fn test_create_i16() {
test_create::<i16>();
}
#[cfg(test)]
fn test_create_with_duplicate_input_channels<T>()
where
T: MixingCoefficient,
T::Coef: Copy,
{
let input_channels = vec![
Channel::FrontLeft,
Channel::Silence,
Channel::FrontLeft,
Channel::FrontCenter,
];
let output_channels = vec![
Channel::Silence,
Channel::FrontRight,
Channel::FrontLeft,
Channel::Silence,
Channel::FrontCenter,
Channel::BackCenter,
];
let _ = Coefficient::<T>::create(input_channels, output_channels);
}
#[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>()
}
#[cfg(test)]
fn test_create_with_duplicate_output_channels<T>()
where
T: MixingCoefficient,
T::Coef: Copy,
{
let input_channels = vec![
Channel::FrontLeft,
Channel::Silence,
Channel::FrontRight,
Channel::FrontCenter,
];
let output_channels = vec![
Channel::Silence,
Channel::FrontRight,
Channel::FrontLeft,
Channel::FrontCenter,
Channel::FrontCenter,
Channel::BackCenter,
];
let _ = Coefficient::<T>::create(input_channels, output_channels);
}
#[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>()
}
#[cfg(test)]
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 = vec![
Channel::FrontLeft,
Channel::Silence,
Channel::FrontRight,
Channel::FrontCenter,
];
let output_channels = vec![
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.clone(), output_channels.clone());
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_get_redirect_matrix_f32() {
test_get_redirect_matrix::<f32>();
}
#[test]
fn test_get_redirect_matrix_i16() {
test_get_redirect_matrix::<i16>();
}
#[test]
fn test_normalize() {
let m = vec![
vec![1.0_f64, 2.0_f64, 3.0_f64],
vec![4.0_f64, 6.0_f64, 10.0_f64],
];
let n = Coefficient::<f32>::normalize(10.0, m.clone());
println!("m: {:?}, n: {:?}", m, n);
}

81
third_party/rust/mixer/src/lib.rs поставляемый Normal file
Просмотреть файл

@ -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: Vec<Channel>, output_channels: Vec<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()
}
}

50
third_party/rust/mixer/src/main.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,50 @@
extern crate mixer;
use mixer::{Channel, Mixer};
fn main() {
// f32
let input_channels = vec![
Channel::FrontLeft,
Channel::Silence,
Channel::FrontRight,
Channel::FrontCenter,
];
let output_channels = vec![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 = vec![
Channel::FrontLeft,
Channel::Silence,
Channel::FrontRight,
Channel::FrontCenter,
Channel::BackLeft,
Channel::SideRight,
Channel::LowFrequency,
Channel::SideLeft,
Channel::BackCenter,
Channel::BackRight,
];
let output_channels = vec![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);
}

Просмотреть файл

@ -19,7 +19,7 @@ static_prefs = { path = "../../../../modules/libpref/init/static_prefs" }
profiler_helper = { path = "../../../../tools/profiler/rust-helper", optional = true }
mozurl = { path = "../../../../netwerk/base/mozurl" }
webrender_bindings = { path = "../../../../gfx/webrender_bindings", optional = true }
cubeb-coreaudio = { git = "https://github.com/ChunMinChang/cubeb-coreaudio-rs", rev = "868d847c2e95096b6eec629dfed77ef363363ecb", optional = true }
cubeb-coreaudio = { git = "https://github.com/ChunMinChang/cubeb-coreaudio-rs", rev = "acb90e9bf36e6e035ac6bbe51efa0a8825b5b6be", optional = true }
cubeb-pulse = { git = "https://github.com/djg/cubeb-pulse-rs", rev="8069f8f4189982e0b38fa6dc8993dd4fab41f728", optional = true, features=["pulse-dlopen"] }
cubeb-sys = { version = "0.6", optional = true, features=["gecko-in-tree"] }
encoding_glue = { path = "../../../../intl/encoding_glue" }