forked from mirrors/gecko-dev
Bug 1635404 - mach vendor rust. r=achronop
Depends on D73871 Differential Revision: https://phabricator.services.mozilla.com/D73872
This commit is contained in:
parent
0a01403ee5
commit
6a7f9ca0d5
61 changed files with 1205 additions and 520 deletions
|
|
@ -45,7 +45,7 @@ rev = "3541e3818fdc7c2a24f87e3459151a4ce955a67a"
|
|||
[source."https://github.com/djg/cubeb-pulse-rs"]
|
||||
git = "https://github.com/djg/cubeb-pulse-rs"
|
||||
replace-with = "vendored-sources"
|
||||
rev = "5eb38163103b0dae86de81cdaf46070bdeedc0d1"
|
||||
rev = "70431f444cf164177cb3c0f060698fc35f811be5"
|
||||
|
||||
[source."https://github.com/bytecodealliance/wasmtime"]
|
||||
git = "https://github.com/bytecodealliance/wasmtime"
|
||||
|
|
@ -70,7 +70,7 @@ rev = "5e870faf6f95d79d11efc813e56370ad124bbed5"
|
|||
[source."https://github.com/ChunMinChang/cubeb-coreaudio-rs"]
|
||||
git = "https://github.com/ChunMinChang/cubeb-coreaudio-rs"
|
||||
replace-with = "vendored-sources"
|
||||
rev = "6156e941e5f46a641d2da10ca53a9ad318c10722"
|
||||
rev = "c5aacdc75618025e72f62f727a7a0d91606e6276"
|
||||
|
||||
[source.crates-io]
|
||||
replace-with = "vendored-sources"
|
||||
|
|
|
|||
26
Cargo.lock
generated
26
Cargo.lock
generated
|
|
@ -743,7 +743,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "coreaudio-sys-utils"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=6156e941e5f46a641d2da10ca53a9ad318c10722#6156e941e5f46a641d2da10ca53a9ad318c10722"
|
||||
source = "git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=c5aacdc75618025e72f62f727a7a0d91606e6276#c5aacdc75618025e72f62f727a7a0d91606e6276"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"coreaudio-sys",
|
||||
|
|
@ -952,27 +952,27 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cubeb"
|
||||
version = "0.6.2"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3cbcdfde9ea319160af6eff068ffaa96aad3532e1b5c0ebc134614cfacacae24"
|
||||
checksum = "1116606d6045c9199f6a1e082f3cf63383ba6f9961339701faa6370dcf73135f"
|
||||
dependencies = [
|
||||
"cubeb-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cubeb-backend"
|
||||
version = "0.6.3"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "228778b00abd85a2ccca9330bc0ee7e72795dd255039814baef02b99757ad12e"
|
||||
checksum = "cc644425cb33d45994a5df4351958369c00e3f7bbf785b8cab270c7840b75774"
|
||||
dependencies = [
|
||||
"cubeb-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cubeb-core"
|
||||
version = "0.6.2"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfd9b2ea1cb6afed9419b0d18fc4093df552ccb2300eb57793629f8cd370b4c8"
|
||||
checksum = "f7c55529b8f47926e4242e1fc01d31b08a5a4847967c5c250644e33fe237cfe5"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cubeb-sys",
|
||||
|
|
@ -981,7 +981,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "cubeb-coreaudio"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=6156e941e5f46a641d2da10ca53a9ad318c10722#6156e941e5f46a641d2da10ca53a9ad318c10722"
|
||||
source = "git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=c5aacdc75618025e72f62f727a7a0d91606e6276#c5aacdc75618025e72f62f727a7a0d91606e6276"
|
||||
dependencies = [
|
||||
"atomic",
|
||||
"audio-mixer",
|
||||
|
|
@ -998,7 +998,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "cubeb-pulse"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/djg/cubeb-pulse-rs?rev=5eb38163103b0dae86de81cdaf46070bdeedc0d1#5eb38163103b0dae86de81cdaf46070bdeedc0d1"
|
||||
source = "git+https://github.com/djg/cubeb-pulse-rs?rev=70431f444cf164177cb3c0f060698fc35f811be5#70431f444cf164177cb3c0f060698fc35f811be5"
|
||||
dependencies = [
|
||||
"cubeb-backend",
|
||||
"pulse",
|
||||
|
|
@ -1009,9 +1009,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cubeb-sys"
|
||||
version = "0.6.2"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "309c5839c5fa03c08363bd308566cbe4654b25a9984342d7546a33d55b80a3d6"
|
||||
checksum = "dcbc562eb6ccf62abacf9e3eebce992e5c36b230ca313ebd7c2d7d0e99deae90"
|
||||
dependencies = [
|
||||
"cmake",
|
||||
"pkg-config",
|
||||
|
|
@ -3594,7 +3594,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "pulse"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/djg/cubeb-pulse-rs?rev=5eb38163103b0dae86de81cdaf46070bdeedc0d1#5eb38163103b0dae86de81cdaf46070bdeedc0d1"
|
||||
source = "git+https://github.com/djg/cubeb-pulse-rs?rev=70431f444cf164177cb3c0f060698fc35f811be5#70431f444cf164177cb3c0f060698fc35f811be5"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"pulse-ffi",
|
||||
|
|
@ -3603,7 +3603,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "pulse-ffi"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/djg/cubeb-pulse-rs?rev=5eb38163103b0dae86de81cdaf46070bdeedc0d1#5eb38163103b0dae86de81cdaf46070bdeedc0d1"
|
||||
source = "git+https://github.com/djg/cubeb-pulse-rs?rev=70431f444cf164177cb3c0f060698fc35f811be5#70431f444cf164177cb3c0f060698fc35f811be5"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"f14e28a47501dd6861a5f3de61bdf70a2e67d8516c78f0b3a73fcd790ef8c143","LICENSE":"8c044baa5d883274736eeece0b955249076c2697b826e576fce59496235b2cf5","src/capi.rs":"faec78d41430ae26ac24b46d5833c76f94ac6eebb4b2186383ebb5e9de32cf8f","src/lib.rs":"94b80747ae1037423a2281f2572fc6d15cd7702417974ae3730adccd71c7a300","src/log.rs":"4962e9c5fdaf44b660ea6813b26337283496ffae3e9beaf2cda86621e253ae8b","src/ops.rs":"d9465abd16c2ce7414aecfc8f983fc9c29dc9b4780c0ea2074974a9814653576","src/traits.rs":"fa8463bd030c1d7716b05401fd2425ff0a399ee0c4dacfeed22341d89ecad871","tests/test_capi.rs":"25860adff6e9e2bcb06371f1e7e52e5f8a20c61fd6b2efe61b7713f3bc4f1031"},"package":"228778b00abd85a2ccca9330bc0ee7e72795dd255039814baef02b99757ad12e"}
|
||||
{"files":{"Cargo.toml":"1e79db9b3f52dc7d3b4d8354cd92022d6d9540aa2b8eee1be6f653cf0f9b6f79","LICENSE":"8c044baa5d883274736eeece0b955249076c2697b826e576fce59496235b2cf5","src/capi.rs":"a772331992b8e058a11e0a96515092ca19182c8eb4c0766e459ccceeb434b2ad","src/lib.rs":"94b80747ae1037423a2281f2572fc6d15cd7702417974ae3730adccd71c7a300","src/log.rs":"cf8e3a778f6b72d4cd80c1c56963355aa2224f19fd4fdf07d03f6fb366000899","src/ops.rs":"12c808c465a8e40bfd19ef8449035924d795fe19bab25b2f13779dca68040010","src/traits.rs":"e8c268343f21799b806e85b0ab1c0f4350b109d83af3e565ac6ff1eb17413672","tests/test_capi.rs":"eea51e60d631d0dab2c8e7f5172467b4fbafa652487f8840e9c5c89da42bf598"},"package":"cc644425cb33d45994a5df4351958369c00e3f7bbf785b8cab270c7840b75774"}
|
||||
4
third_party/rust/cubeb-backend/Cargo.toml
vendored
4
third_party/rust/cubeb-backend/Cargo.toml
vendored
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
[package]
|
||||
name = "cubeb-backend"
|
||||
version = "0.6.3"
|
||||
version = "0.7.0"
|
||||
authors = ["Dan Glastonbury <dglastonbury@mozilla.com>"]
|
||||
description = "Bindings to libcubeb internals to facilitate implementing cubeb backends in rust.\n"
|
||||
homepage = "https://github.com/djg/cubeb-rs"
|
||||
|
|
@ -21,7 +21,7 @@ categories = ["api-bindings"]
|
|||
license = "ISC"
|
||||
repository = "https://github.com/djg/cubeb-rs"
|
||||
[dependencies.cubeb-core]
|
||||
version = "0.6.2"
|
||||
version = "0.7.0"
|
||||
|
||||
[features]
|
||||
gecko-in-tree = ["cubeb-core/gecko-in-tree"]
|
||||
|
|
|
|||
11
third_party/rust/cubeb-backend/src/capi.rs
vendored
11
third_party/rust/cubeb-backend/src/capi.rs
vendored
|
|
@ -49,6 +49,7 @@ macro_rules! capi_new(
|
|||
Some($crate::capi::capi_stream_reset_default_device::<$stm>),
|
||||
stream_get_position: Some($crate::capi::capi_stream_get_position::<$stm>),
|
||||
stream_get_latency: Some($crate::capi::capi_stream_get_latency::<$stm>),
|
||||
stream_get_input_latency: Some($crate::capi::capi_stream_get_input_latency::<$stm>),
|
||||
stream_set_volume: Some($crate::capi::capi_stream_set_volume::<$stm>),
|
||||
stream_get_current_device: Some($crate::capi::capi_stream_get_current_device::<$stm>),
|
||||
stream_device_destroy: Some($crate::capi::capi_stream_device_destroy::<$stm>),
|
||||
|
|
@ -216,6 +217,16 @@ pub unsafe extern "C" fn capi_stream_get_latency<STM: StreamOps>(
|
|||
ffi::CUBEB_OK
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn capi_stream_get_input_latency<STM: StreamOps>(
|
||||
s: *mut ffi::cubeb_stream,
|
||||
latency: *mut u32,
|
||||
) -> c_int {
|
||||
let stm = &mut *(s as *mut STM);
|
||||
|
||||
*latency = _try!(stm.input_latency());
|
||||
ffi::CUBEB_OK
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn capi_stream_set_volume<STM: StreamOps>(
|
||||
s: *mut ffi::cubeb_stream,
|
||||
volume: f32,
|
||||
|
|
|
|||
2
third_party/rust/cubeb-backend/src/log.rs
vendored
2
third_party/rust/cubeb-backend/src/log.rs
vendored
|
|
@ -37,7 +37,7 @@ macro_rules! cubeb_log_internal {
|
|||
let _ = write!(&mut buf[..], "{}:{}: {}\n", filename, line!(), $msg);
|
||||
let last = std::cmp::min(len, buf.len() - 1);
|
||||
buf[last] = 0;
|
||||
let cstr = unsafe { std::ffi::CStr::from_bytes_with_nul_unchecked(&buf[..last + 1]) };
|
||||
let cstr = unsafe { std::ffi::CStr::from_bytes_with_nul_unchecked(&buf[..=last]) };
|
||||
log_callback(cstr.as_ptr());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
2
third_party/rust/cubeb-backend/src/ops.rs
vendored
2
third_party/rust/cubeb-backend/src/ops.rs
vendored
|
|
@ -63,6 +63,8 @@ pub struct Ops {
|
|||
Option<unsafe extern "C" fn(stream: *mut ffi::cubeb_stream, position: *mut u64) -> c_int>,
|
||||
pub stream_get_latency:
|
||||
Option<unsafe extern "C" fn(stream: *mut ffi::cubeb_stream, latency: *mut u32) -> c_int>,
|
||||
pub stream_get_input_latency:
|
||||
Option<unsafe extern "C" fn(stream: *mut ffi::cubeb_stream, latency: *mut u32) -> c_int>,
|
||||
pub stream_set_volume:
|
||||
Option<unsafe extern "C" fn(stream: *mut ffi::cubeb_stream, volumes: c_float) -> c_int>,
|
||||
pub stream_get_current_device: Option<
|
||||
|
|
|
|||
1
third_party/rust/cubeb-backend/src/traits.rs
vendored
1
third_party/rust/cubeb-backend/src/traits.rs
vendored
|
|
@ -48,6 +48,7 @@ pub trait StreamOps {
|
|||
fn reset_default_device(&mut self) -> Result<()>;
|
||||
fn position(&mut self) -> Result<u64>;
|
||||
fn latency(&mut self) -> Result<u32>;
|
||||
fn input_latency(&mut self) -> Result<u32>;
|
||||
fn set_volume(&mut self, volume: f32) -> Result<()>;
|
||||
fn current_device(&mut self) -> Result<&DeviceRef>;
|
||||
fn device_destroy(&mut self, device: &DeviceRef) -> Result<()>;
|
||||
|
|
|
|||
|
|
@ -100,6 +100,9 @@ impl StreamOps for TestStream {
|
|||
fn latency(&mut self) -> Result<u32> {
|
||||
Ok(0u32)
|
||||
}
|
||||
fn input_latency(&mut self) -> Result<u32> {
|
||||
Ok(0u32)
|
||||
}
|
||||
fn set_volume(&mut self, volume: f32) -> Result<()> {
|
||||
assert_eq!(volume, 0.5);
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"12fc57e44d5f3962acec8860191fe0e9aca6ebe654924f4736b35cf15450b0d1","LICENSE":"8c044baa5d883274736eeece0b955249076c2697b826e576fce59496235b2cf5","src/builders.rs":"ca97e3a3d1f3fc451c17851c8538964ec67f3964dfe29e902d904ee7445becca","src/channel.rs":"c8d5a76ef3ecdd96cd4de516e3d4d139bbb83c4690d1c3f5fd07fffc47be51f1","src/context.rs":"09625b75070ec88d566a907ab2e574e2d85df4c6df295f798b3372df2cdc8f7a","src/device.rs":"490d2e94ecae1e149476c2e8d9aa03c4163987c3efccc962b2d3123e4c09dedf","src/device_collection.rs":"f6d0c1628cc34b524f86b84a1e1c79971c3f64ebc4ac64eeb10a1330bbe8c238","src/error.rs":"855ff3d3597753f832ecea00e403c71129afd80db3d39456cf3e23cb9aeb91e7","src/ffi_types.rs":"d815d7a80895b5e86907e708dc0219fca4ac4668cde114afee434e7d702a145d","src/format.rs":"5513c537a72af1c222ee7c30b26d4de9d368a69772688b95d88b1a99f6892d5c","src/lib.rs":"6010a5e20b836b8e5c9fba382fde819e6f3c18c0ec2016e6e7e118eabedbcd51","src/log.rs":"c46bae3472043fd076df3229c3421d948a87fae8495c1524b41ab2d8608f612a","src/stream.rs":"56de9f3ad372c123932eca23659f76e7b65fc4502c8da5fe91f3ed35666c306d","src/try_call.rs":"231bfa3f3448f7531427bb228beb2bcd4fd711f0b13d2d8f412af013470f40c7","src/util.rs":"308cfbaacd615ff600e74415c52daeef007fff34a4a0648a73c0042f6067f84f"},"package":"bfd9b2ea1cb6afed9419b0d18fc4093df552ccb2300eb57793629f8cd370b4c8"}
|
||||
{"files":{"Cargo.toml":"acc28d182a00d78ed6e11c8773d40939016d3df2cd9f3593fae8983eb05db540","LICENSE":"8c044baa5d883274736eeece0b955249076c2697b826e576fce59496235b2cf5","src/builders.rs":"ca97e3a3d1f3fc451c17851c8538964ec67f3964dfe29e902d904ee7445becca","src/channel.rs":"971488fca24a11d49046f8fe9c396765560c070013e5d022afcbe27cfd51d941","src/context.rs":"09625b75070ec88d566a907ab2e574e2d85df4c6df295f798b3372df2cdc8f7a","src/device.rs":"c526d8f992c8ad6d137b0bda803c5182247c9c32797a6f86e0d86d4f6361eb4c","src/device_collection.rs":"f6d0c1628cc34b524f86b84a1e1c79971c3f64ebc4ac64eeb10a1330bbe8c238","src/error.rs":"60454b30bd496dffa94ed9c1bee5a39f29219f2411b76cff6a4f3074f3154701","src/ffi_types.rs":"d815d7a80895b5e86907e708dc0219fca4ac4668cde114afee434e7d702a145d","src/format.rs":"5513c537a72af1c222ee7c30b26d4de9d368a69772688b95d88b1a99f6892d5c","src/lib.rs":"6010a5e20b836b8e5c9fba382fde819e6f3c18c0ec2016e6e7e118eabedbcd51","src/log.rs":"c46bae3472043fd076df3229c3421d948a87fae8495c1524b41ab2d8608f612a","src/stream.rs":"c237f288e6c78597d5a8f53c30927b1aaa40d482f2edb4343991b4d9d0bb1230","src/try_call.rs":"99c59a13db90326613d8cf91909e8a7eaef80646984d10927cbc7cde2cb4b066","src/util.rs":"308cfbaacd615ff600e74415c52daeef007fff34a4a0648a73c0042f6067f84f"},"package":"f7c55529b8f47926e4242e1fc01d31b08a5a4847967c5c250644e33fe237cfe5"}
|
||||
6
third_party/rust/cubeb-core/Cargo.toml
vendored
6
third_party/rust/cubeb-core/Cargo.toml
vendored
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
[package]
|
||||
name = "cubeb-core"
|
||||
version = "0.6.2"
|
||||
version = "0.7.0"
|
||||
authors = ["Dan Glastonbury <dglastonbury@mozilla.com>"]
|
||||
description = "Common types and definitions for cubeb rust and C bindings. Not intended for direct use.\n"
|
||||
homepage = "https://github.com/djg/cubeb-rs"
|
||||
|
|
@ -21,10 +21,10 @@ categories = ["api-bindings"]
|
|||
license = "ISC"
|
||||
repository = "https://github.com/djg/cubeb-rs"
|
||||
[dependencies.bitflags]
|
||||
version = "1.0"
|
||||
version = "1.2.0"
|
||||
|
||||
[dependencies.cubeb-sys]
|
||||
version = "0.6.2"
|
||||
version = "0.7.0"
|
||||
|
||||
[features]
|
||||
gecko-in-tree = ["cubeb-sys/gecko-in-tree"]
|
||||
|
|
|
|||
2
third_party/rust/cubeb-core/src/channel.rs
vendored
2
third_party/rust/cubeb-core/src/channel.rs
vendored
|
|
@ -88,7 +88,7 @@ mod test {
|
|||
fn channel_layout_from_raw() {
|
||||
macro_rules! check(
|
||||
($($raw:ident => $real:ident),*) => (
|
||||
$(let x = super::ChannelLayout::from(ffi::$raw);;
|
||||
$(let x = super::ChannelLayout::from(ffi::$raw);
|
||||
assert_eq!(x, super::ChannelLayout::$real);
|
||||
)*
|
||||
) );
|
||||
|
|
|
|||
22
third_party/rust/cubeb-core/src/device.rs
vendored
22
third_party/rust/cubeb-core/src/device.rs
vendored
|
|
@ -18,8 +18,8 @@ pub enum DeviceState {
|
|||
Enabled,
|
||||
}
|
||||
|
||||
/// Architecture specific sample type.
|
||||
bitflags! {
|
||||
/// Architecture specific sample type.
|
||||
pub struct DeviceFormat: ffi::cubeb_device_fmt {
|
||||
const S16LE = ffi::CUBEB_DEVICE_FMT_S16LE;
|
||||
const S16BE = ffi::CUBEB_DEVICE_FMT_S16BE;
|
||||
|
|
@ -28,10 +28,10 @@ bitflags! {
|
|||
}
|
||||
}
|
||||
|
||||
/// Channel type for a `cubeb_stream`. Depending on the backend and platform
|
||||
/// used, this can control inter-stream interruption, ducking, and volume
|
||||
/// control.
|
||||
bitflags! {
|
||||
/// Channel type for a `cubeb_stream`. Depending on the backend and platform
|
||||
/// used, this can control inter-stream interruption, ducking, and volume
|
||||
/// control.
|
||||
pub struct DevicePref: ffi::cubeb_device_pref {
|
||||
const NONE = ffi::CUBEB_DEVICE_PREF_NONE;
|
||||
const MULTIMEDIA = ffi::CUBEB_DEVICE_PREF_MULTIMEDIA;
|
||||
|
|
@ -41,9 +41,9 @@ bitflags! {
|
|||
}
|
||||
}
|
||||
|
||||
/// Whether a particular device is an input device (e.g. a microphone), or an
|
||||
/// output device (e.g. headphones).
|
||||
bitflags! {
|
||||
/// Whether a particular device is an input device (e.g. a microphone), or an
|
||||
/// output device (e.g. headphones).
|
||||
pub struct DeviceType: ffi::cubeb_device_type {
|
||||
const UNKNOWN = ffi::CUBEB_DEVICE_TYPE_UNKNOWN as _;
|
||||
const INPUT = ffi::CUBEB_DEVICE_TYPE_INPUT as _;
|
||||
|
|
@ -55,8 +55,8 @@ bitflags! {
|
|||
/// across calls.
|
||||
pub type DeviceId = ffi::cubeb_devid;
|
||||
|
||||
/// Audio device description
|
||||
ffi_type_heap! {
|
||||
/// Audio device description
|
||||
type CType = ffi::cubeb_device;
|
||||
#[derive(Debug)]
|
||||
pub struct Device;
|
||||
|
|
@ -91,11 +91,11 @@ impl DeviceRef {
|
|||
}
|
||||
}
|
||||
|
||||
/// This structure holds the characteristics of an input or output
|
||||
/// audio device. It is obtained using `enumerate_devices`, which
|
||||
/// returns these structures via `device_collection` and must be
|
||||
/// destroyed via `device_collection_destroy`.
|
||||
ffi_type_stack! {
|
||||
/// This structure holds the characteristics of an input or output
|
||||
/// audio device. It is obtained using `enumerate_devices`, which
|
||||
/// returns these structures via `device_collection` and must be
|
||||
/// destroyed via `device_collection_destroy`.
|
||||
type CType = ffi::cubeb_device_info;
|
||||
pub struct DeviceInfo;
|
||||
pub struct DeviceInfoRef;
|
||||
|
|
|
|||
3
third_party/rust/cubeb-core/src/error.rs
vendored
3
third_party/rust/cubeb-core/src/error.rs
vendored
|
|
@ -100,8 +100,7 @@ impl error::Error for Error {
|
|||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use std::error::Error;
|
||||
write!(f, "{}", self.description())
|
||||
write!(f, "{}", self)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
15
third_party/rust/cubeb-core/src/stream.rs
vendored
15
third_party/rust/cubeb-core/src/stream.rs
vendored
|
|
@ -45,8 +45,8 @@ impl Into<ffi::cubeb_state> for State {
|
|||
}
|
||||
}
|
||||
|
||||
/// Miscellaneous stream preferences.
|
||||
bitflags! {
|
||||
/// Miscellaneous stream preferences.
|
||||
pub struct StreamPrefs: ffi::cubeb_stream_prefs {
|
||||
const NONE = ffi::CUBEB_STREAM_PREF_NONE;
|
||||
const LOOPBACK = ffi::CUBEB_STREAM_PREF_LOOPBACK;
|
||||
|
|
@ -55,8 +55,8 @@ bitflags! {
|
|||
}
|
||||
}
|
||||
|
||||
/// Stream format initialization parameters.
|
||||
ffi_type_stack!{
|
||||
/// Stream format initialization parameters.
|
||||
type CType = ffi::cubeb_stream_params;
|
||||
#[derive(Debug)]
|
||||
pub struct StreamParams;
|
||||
|
|
@ -143,6 +143,17 @@ impl StreamRef {
|
|||
Ok(latency)
|
||||
}
|
||||
|
||||
/// Get the input latency for this stream, in frames. This is the number of frames between the
|
||||
/// time the audio input device records the audio, and the cubeb callback delivers it.
|
||||
/// This returns an error if the stream is output-only.
|
||||
pub fn input_latency(&self) -> Result<u32> {
|
||||
let mut latency = 0u32;
|
||||
unsafe {
|
||||
let _ = try_call!(ffi::cubeb_stream_get_input_latency(self.as_ptr(), &mut latency));
|
||||
}
|
||||
Ok(latency)
|
||||
}
|
||||
|
||||
/// Set the volume for a stream.
|
||||
pub fn set_volume(&self, volume: f32) -> Result<()> {
|
||||
unsafe { call!(ffi::cubeb_stream_set_volume(self.as_ptr(), volume)) }
|
||||
|
|
|
|||
2
third_party/rust/cubeb-core/src/try_call.rs
vendored
2
third_party/rust/cubeb-core/src/try_call.rs
vendored
|
|
@ -22,6 +22,6 @@ macro_rules! call {
|
|||
|
||||
macro_rules! try_call {
|
||||
(ffi::$p:ident ($($e:expr),*)) => ({
|
||||
try!(::try_call::cvt_r(ffi::$p($($e),*)))
|
||||
::try_call::cvt_r(ffi::$p($($e),*))?
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"files":{".editorconfig":"4e53b182bcc78b83d7e1b5c03efa14d22d4955c4ed2514d1ba4e99c1eb1a50ba",".githooks/pre-push":"8b8b26544cd56f54c0c33812551f786bb25cb08c86dbfeb6bf3daad881c826a1",".travis.yml":"dc07bac53f70f16c9bdf52264bdc58500ae6018c1b4c567bc7642f6b4ca3cc35","Cargo.toml":"333de4946bd1fbe250d68241d3f085a47d6990053a54999e2b16f3c46779d228","LICENSE":"6e6f56aff5bbf3cbc60747e152fb1a719bd0716aaf6d711c554f57d92e96297c","README.md":"ab0f168080dfdfc1512484aeb4a41e1625f6b4147e534899fab56b2cb6d9f32a","install_git_hook.sh":"d38c8e51e636f6b90b489621ac34ccd1d1b1f40dccce3d178ed1da1c5068f16d","install_rustfmt_clippy.sh":"4ae90d8dcb9757cb3ae4ae142ef80e5377c0dde61c63f4a3c32418646e80ca7b","run_device_tests.sh":"4b7d65eb638c1a278ffc8ecb6d30d47b3b8405392e976cae38c6f744e2bed532","run_sanitizers.sh":"2f0934ba01cbcd74485f19d50147f6b604cf9730bbd3a3d3f3d958e40d0f799f","run_tests.sh":"3dd76659f6dceeb0490dd92b355e113301ba0d0a8f034993a56f40e09edd25b2","src/backend/aggregate_device.rs":"ae21129aa6b3c7bd3376751b6a94d1ebe6c9f7afcd1db3107fb4d703d04da6b3","src/backend/auto_release.rs":"050fdcee74cf46b9a8a85a877e166d72a853d33220f59cf734cbb6ea09daa441","src/backend/buffer_manager.rs":"9c10a261792e32e75833b5f976b18547c338ca6beb2330eeab1ad203cc8c32bf","src/backend/device_property.rs":"d43642ea6e5f40e29c2a59ec7d81b42c154134685e417585045785359aa31686","src/backend/mixer.rs":"14e2156a8c1aeabcd4adb3336c3c9401b9c8526ec82a8c78942af7a79648f0f8","src/backend/mod.rs":"b566a4f853563109b74c84bc519323b42fb96d8af58e4030225aade7cd47d6c6","src/backend/resampler.rs":"fd1281d28a4db1659d2f75e43b8457651745e1b6eb5a53a77f04d752135f6dc7","src/backend/tests/aggregate_device.rs":"107f5c637844cd5ae43d2b42cec4ef3369bb702751586078c0a9d50f039161cd","src/backend/tests/api.rs":"b6692aa2cab751e8d52aee40c8177ce15912338cdbe3daa93d3a25ce9e92031b","src/backend/tests/backlog.rs":"3b189a7e036543c467cc242af0ed3332721179ee2b1c8847a6db563546f1ac52","src/backend/tests/device_change.rs":"8261f561f69dabd374ac47d69aa484812b65070a9e9581dfb2605e8404eaad6d","src/backend/tests/device_property.rs":"373f76d3bee83b263db3f02be3b94b408bdf852d84e4b5153273fda34b11a374","src/backend/tests/interfaces.rs":"14943e84a79976a7ef52882edeb9330350705d5190db6647f98b4ffa851a8396","src/backend/tests/manual.rs":"dc707836dab31f83d4b325afbc4dc4c8104ac8036e87f59ade3309ee83fe2d3f","src/backend/tests/mod.rs":"8dba770023d7f9c4228f0e11915347f0e07da5fd818e3ee4478c4b197af9aa2a","src/backend/tests/parallel.rs":"f9e1883660d6146b6e5075806561f5f689810e25c5e7764dfd28c9b939821a49","src/backend/tests/tone.rs":"16150438317ce501986734167b5fb97bfec567228acbcd8f3b4c4484c22f29e0","src/backend/tests/utils.rs":"1bb99ef71d3c18545bca49767e7b6bfffbe11896246994f41be7ed372772fd48","src/backend/utils.rs":"5ce1b753af0ffb654b6b2038d649aea88eebd27390a607a6d5988a9e40a4a717","src/capi.rs":"21b66b70545bf04ec719928004d1d9adb45b24ced51288f5b2993d79aaf78f5f","src/lib.rs":"5e586d45cd6b3722f0a6736d9252593299269817a153eef1930a5fb9bfbb56f5","todo.md":"7323051fa7f0c51eb2eb0d495dcd951502e4cc8ce0088e6e7b3b3a95180f43d4"},"package":null}
|
||||
{"files":{".editorconfig":"4e53b182bcc78b83d7e1b5c03efa14d22d4955c4ed2514d1ba4e99c1eb1a50ba",".githooks/pre-push":"8b8b26544cd56f54c0c33812551f786bb25cb08c86dbfeb6bf3daad881c826a1",".travis.yml":"dc07bac53f70f16c9bdf52264bdc58500ae6018c1b4c567bc7642f6b4ca3cc35","Cargo.toml":"93449267612163fe621c75c6195fcf5f961c2bddd975468d67fc2121d538f1c7","LICENSE":"6e6f56aff5bbf3cbc60747e152fb1a719bd0716aaf6d711c554f57d92e96297c","README.md":"ab0f168080dfdfc1512484aeb4a41e1625f6b4147e534899fab56b2cb6d9f32a","install_git_hook.sh":"d38c8e51e636f6b90b489621ac34ccd1d1b1f40dccce3d178ed1da1c5068f16d","install_rustfmt_clippy.sh":"4ae90d8dcb9757cb3ae4ae142ef80e5377c0dde61c63f4a3c32418646e80ca7b","run_device_tests.sh":"4b7d65eb638c1a278ffc8ecb6d30d47b3b8405392e976cae38c6f744e2bed532","run_sanitizers.sh":"2f0934ba01cbcd74485f19d50147f6b604cf9730bbd3a3d3f3d958e40d0f799f","run_tests.sh":"3dd76659f6dceeb0490dd92b355e113301ba0d0a8f034993a56f40e09edd25b2","src/backend/aggregate_device.rs":"ae21129aa6b3c7bd3376751b6a94d1ebe6c9f7afcd1db3107fb4d703d04da6b3","src/backend/auto_release.rs":"050fdcee74cf46b9a8a85a877e166d72a853d33220f59cf734cbb6ea09daa441","src/backend/buffer_manager.rs":"9c10a261792e32e75833b5f976b18547c338ca6beb2330eeab1ad203cc8c32bf","src/backend/device_property.rs":"d43642ea6e5f40e29c2a59ec7d81b42c154134685e417585045785359aa31686","src/backend/mixer.rs":"14e2156a8c1aeabcd4adb3336c3c9401b9c8526ec82a8c78942af7a79648f0f8","src/backend/mod.rs":"4c2967b7f581cebce875ea342658d32650695ebd57a026bd36e92ef3807866ae","src/backend/resampler.rs":"fd1281d28a4db1659d2f75e43b8457651745e1b6eb5a53a77f04d752135f6dc7","src/backend/tests/aggregate_device.rs":"107f5c637844cd5ae43d2b42cec4ef3369bb702751586078c0a9d50f039161cd","src/backend/tests/api.rs":"9ce44a867519d7b7a2b43c7f833327c35be38af7ba6fcc3d277ed1d7d8e7c8c2","src/backend/tests/backlog.rs":"3b189a7e036543c467cc242af0ed3332721179ee2b1c8847a6db563546f1ac52","src/backend/tests/device_change.rs":"8261f561f69dabd374ac47d69aa484812b65070a9e9581dfb2605e8404eaad6d","src/backend/tests/device_property.rs":"373f76d3bee83b263db3f02be3b94b408bdf852d84e4b5153273fda34b11a374","src/backend/tests/interfaces.rs":"14943e84a79976a7ef52882edeb9330350705d5190db6647f98b4ffa851a8396","src/backend/tests/manual.rs":"dc707836dab31f83d4b325afbc4dc4c8104ac8036e87f59ade3309ee83fe2d3f","src/backend/tests/mod.rs":"8dba770023d7f9c4228f0e11915347f0e07da5fd818e3ee4478c4b197af9aa2a","src/backend/tests/parallel.rs":"f9e1883660d6146b6e5075806561f5f689810e25c5e7764dfd28c9b939821a49","src/backend/tests/tone.rs":"16150438317ce501986734167b5fb97bfec567228acbcd8f3b4c4484c22f29e0","src/backend/tests/utils.rs":"1bb99ef71d3c18545bca49767e7b6bfffbe11896246994f41be7ed372772fd48","src/backend/utils.rs":"5ce1b753af0ffb654b6b2038d649aea88eebd27390a607a6d5988a9e40a4a717","src/capi.rs":"21b66b70545bf04ec719928004d1d9adb45b24ced51288f5b2993d79aaf78f5f","src/lib.rs":"5e586d45cd6b3722f0a6736d9252593299269817a153eef1930a5fb9bfbb56f5","todo.md":"7323051fa7f0c51eb2eb0d495dcd951502e4cc8ce0088e6e7b3b3a95180f43d4"},"package":null}
|
||||
2
third_party/rust/cubeb-coreaudio/Cargo.toml
vendored
2
third_party/rust/cubeb-coreaudio/Cargo.toml
vendored
|
|
@ -11,7 +11,7 @@ crate-type = ["staticlib", "rlib"]
|
|||
atomic = "0.4"
|
||||
bitflags = "1.0"
|
||||
coreaudio-sys-utils = { path = "coreaudio-sys-utils" }
|
||||
cubeb-backend = "0.6.3"
|
||||
cubeb-backend = "0.7"
|
||||
float-cmp = "0.6"
|
||||
libc = "0.2"
|
||||
lazy_static = "1.2"
|
||||
|
|
|
|||
|
|
@ -344,6 +344,10 @@ extern "C" fn audiounit_input_callback(
|
|||
assert!(!user_ptr.is_null());
|
||||
let stm = unsafe { &mut *(user_ptr as *mut AudioUnitStream) };
|
||||
|
||||
let input_latency_frames = compute_input_latency(&stm, unsafe { (*tstamp).mHostTime });
|
||||
stm.total_input_latency_frames
|
||||
.store(input_latency_frames, Ordering::SeqCst);
|
||||
|
||||
if stm.shutdown.load(Ordering::SeqCst) {
|
||||
cubeb_log!("({:p}) input shutdown", stm as *const AudioUnitStream);
|
||||
return NO_ERR;
|
||||
|
|
@ -501,7 +505,24 @@ fn compute_output_latency(stm: &AudioUnitStream, host_time: u64) -> u32 {
|
|||
// the hardware latency.
|
||||
let out_hw_rate = stm.core_stream_data.output_hw_rate as u64;
|
||||
(output_latency_ns * out_hw_rate / NS2S
|
||||
+ stm.current_latency_frames.load(Ordering::SeqCst) as u64) as u32
|
||||
+ stm.current_output_latency_frames.load(Ordering::SeqCst) as u64) as u32
|
||||
}
|
||||
|
||||
fn compute_input_latency(stm: &AudioUnitStream, host_time: u64) -> u32 {
|
||||
let now = host_time_to_ns(unsafe { mach_absolute_time() });
|
||||
let audio_input_time = host_time_to_ns(host_time);
|
||||
let input_latency_ns = if audio_input_time > now {
|
||||
0
|
||||
} else {
|
||||
now - audio_input_time
|
||||
};
|
||||
|
||||
const NS2S: u64 = 1_000_000_000;
|
||||
// The total input latency is the timestamp difference + the stream latency +
|
||||
// the hardware latency.
|
||||
let input_hw_rate = stm.core_stream_data.input_hw_rate as u64;
|
||||
(input_latency_ns * input_hw_rate / NS2S
|
||||
+ stm.current_input_latency_frames.load(Ordering::SeqCst) as u64) as u32
|
||||
}
|
||||
|
||||
extern "C" fn audiounit_output_callback(
|
||||
|
|
@ -1337,7 +1358,7 @@ fn get_range_of_sample_rates(
|
|||
Ok((min, max))
|
||||
}
|
||||
|
||||
fn get_presentation_latency(devid: AudioObjectID, devtype: DeviceType) -> u32 {
|
||||
fn get_fixed_latency(devid: AudioObjectID, devtype: DeviceType) -> u32 {
|
||||
let device_latency = match get_device_latency(devid, devtype) {
|
||||
Ok(latency) => latency,
|
||||
Err(e) => {
|
||||
|
|
@ -1587,7 +1608,7 @@ fn create_cubeb_device_info(
|
|||
}
|
||||
}
|
||||
|
||||
let latency = get_presentation_latency(devid, devtype);
|
||||
let latency = get_fixed_latency(devid, devtype);
|
||||
|
||||
let (latency_low, latency_high) = match get_device_buffer_frame_size_range(devid, devtype) {
|
||||
Ok(range) => (
|
||||
|
|
@ -2813,6 +2834,11 @@ impl<'ctx> CoreStreamData<'ctx> {
|
|||
cubeb_log!("AudioUnitInitialize/input rv={}", r);
|
||||
return Err(Error::error());
|
||||
}
|
||||
|
||||
stream.current_input_latency_frames.store(
|
||||
get_fixed_latency(self.input_device.id, DeviceType::INPUT),
|
||||
Ordering::SeqCst,
|
||||
);
|
||||
}
|
||||
|
||||
if !self.output_unit.is_null() {
|
||||
|
|
@ -2822,8 +2848,8 @@ impl<'ctx> CoreStreamData<'ctx> {
|
|||
return Err(Error::error());
|
||||
}
|
||||
|
||||
stream.current_latency_frames.store(
|
||||
get_presentation_latency(self.output_device.id, DeviceType::OUTPUT),
|
||||
stream.current_output_latency_frames.store(
|
||||
get_fixed_latency(self.output_device.id, DeviceType::OUTPUT),
|
||||
Ordering::SeqCst,
|
||||
);
|
||||
|
||||
|
|
@ -2838,7 +2864,7 @@ impl<'ctx> CoreStreamData<'ctx> {
|
|||
&mut size,
|
||||
) == NO_ERR
|
||||
{
|
||||
stream.current_latency_frames.fetch_add(
|
||||
stream.current_output_latency_frames.fetch_add(
|
||||
(unit_s * self.output_desc.mSampleRate) as u32,
|
||||
Ordering::SeqCst,
|
||||
);
|
||||
|
|
@ -3115,8 +3141,10 @@ struct AudioUnitStream<'ctx> {
|
|||
destroy_pending: AtomicBool,
|
||||
// Latency requested by the user.
|
||||
latency_frames: u32,
|
||||
current_latency_frames: AtomicU32,
|
||||
current_output_latency_frames: AtomicU32,
|
||||
current_input_latency_frames: AtomicU32,
|
||||
total_output_latency_frames: AtomicU32,
|
||||
total_input_latency_frames: AtomicU32,
|
||||
// This is true if a device change callback is currently running.
|
||||
switching_device: AtomicBool,
|
||||
core_stream_data: CoreStreamData<'ctx>,
|
||||
|
|
@ -3146,8 +3174,10 @@ impl<'ctx> AudioUnitStream<'ctx> {
|
|||
reinit_pending: AtomicBool::new(false),
|
||||
destroy_pending: AtomicBool::new(false),
|
||||
latency_frames,
|
||||
current_latency_frames: AtomicU32::new(0),
|
||||
current_output_latency_frames: AtomicU32::new(0),
|
||||
current_input_latency_frames: AtomicU32::new(0),
|
||||
total_output_latency_frames: AtomicU32::new(0),
|
||||
total_input_latency_frames: AtomicU32::new(0),
|
||||
switching_device: AtomicBool::new(false),
|
||||
core_stream_data: CoreStreamData::default(),
|
||||
}
|
||||
|
|
@ -3428,12 +3458,13 @@ impl<'ctx> StreamOps for AudioUnitStream<'ctx> {
|
|||
Err(Error::not_supported())
|
||||
}
|
||||
fn position(&mut self) -> Result<u64> {
|
||||
let current_latency_frames = u64::from(self.current_latency_frames.load(Ordering::SeqCst));
|
||||
let current_output_latency_frames =
|
||||
u64::from(self.current_output_latency_frames.load(Ordering::SeqCst));
|
||||
let frames_played = self.frames_played.load(Ordering::SeqCst);
|
||||
let position = if current_latency_frames > frames_played {
|
||||
let position = if current_output_latency_frames > frames_played {
|
||||
0
|
||||
} else {
|
||||
frames_played - current_latency_frames
|
||||
frames_played - current_output_latency_frames
|
||||
};
|
||||
Ok(position)
|
||||
}
|
||||
|
|
@ -3445,6 +3476,21 @@ impl<'ctx> StreamOps for AudioUnitStream<'ctx> {
|
|||
fn latency(&mut self) -> Result<u32> {
|
||||
Ok(self.total_output_latency_frames.load(Ordering::SeqCst))
|
||||
}
|
||||
#[cfg(target_os = "ios")]
|
||||
fn input_latency(&mut self) -> Result<u32> {
|
||||
Err(not_supported())
|
||||
}
|
||||
#[cfg(not(target_os = "ios"))]
|
||||
fn input_latency(&mut self) -> Result<u32> {
|
||||
let user_rate = self.core_stream_data.input_stream_params.rate();
|
||||
let hw_rate = self.core_stream_data.input_hw_rate as u32;
|
||||
let frames = self.total_input_latency_frames.load(Ordering::SeqCst);
|
||||
if hw_rate == user_rate {
|
||||
Ok(frames)
|
||||
} else {
|
||||
Ok(frames * (user_rate / hw_rate))
|
||||
}
|
||||
}
|
||||
fn set_volume(&mut self, volume: f32) -> Result<()> {
|
||||
set_volume(self.core_stream_data.output_unit, volume)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1200,7 +1200,7 @@ fn test_get_device_presentation_latency() {
|
|||
fn test_get_device_presentation_latencies_in_scope(scope: Scope) {
|
||||
if let Some(device) = test_get_default_device(scope.clone()) {
|
||||
// TODO: The latencies very from devices to devices. Check nothing here.
|
||||
let latency = get_presentation_latency(device, scope.clone().into());
|
||||
let latency = get_fixed_latency(device, scope.clone().into());
|
||||
println!(
|
||||
"present latency on the device {} in scope {:?}: {}",
|
||||
device, scope, latency
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"files":{".editorconfig":"bf047bd1da10cabb99eea666d1e57c321eba4716dccb3e4ed0e2c5fe3ca53858",".travis.yml":"0394e2adb041175457685cde5ee05ff04bdab8885fd8a62551f2ff43d9e48872","AUTHORS":"0e0ac930a68ce2f6b876126b195add177f0d3886facb9260f4d9b69f1988f0cc","Cargo.toml":"297f0144bbebcbc2f3b5478fed750f39adb3b60f9d6c273dfa72d9736818423d","LICENSE":"44c6b5ae5ec3fe2fbc608b00e6f4896f4d2d5c7e525fcbaa3eaa3cf2f3d5a983","README.md":"e6a98ee5630b9ce1a096a2907d095454f2770e298a5b0976ab552cc53ca96cfc","src/backend/context.rs":"33d9fdf1504fe1ae43d301e288daf6eaeabeb47aa0ef86efa135c6d984425fc4","src/backend/cork_state.rs":"4a0f1afc7d9f333dac89218cc56d7d32fbffb487cd48c1c9a4e03d79cb3b5e28","src/backend/intern.rs":"374a9a3bd79fddc47739dda1dbfc5929aea5a91946794fe65fba3c8d130fbda9","src/backend/mod.rs":"06ce9250865abf0ea461f215b128470636d072a6776821efef3caf5a7b992fb9","src/backend/stream.rs":"8ff67b76b8663a952fe2d2f7d370a05271f156601c929d19caa56fc892a4a4ab","src/capi.rs":"b2c1be8128cadd36caa65c80950440f9d6f2aa0c24cc7bae6a9eaf6347ac454d","src/lib.rs":"7282560d84b134b09acfd8d6282600982e42fb3557f72454c535637cc26c7bf6"},"package":null}
|
||||
{"files":{".editorconfig":"bf047bd1da10cabb99eea666d1e57c321eba4716dccb3e4ed0e2c5fe3ca53858",".travis.yml":"0394e2adb041175457685cde5ee05ff04bdab8885fd8a62551f2ff43d9e48872","AUTHORS":"0e0ac930a68ce2f6b876126b195add177f0d3886facb9260f4d9b69f1988f0cc","Cargo.toml":"603fb672fb83699707559d13a651963321991b9bec6ea208723d3ee37c40694e","LICENSE":"44c6b5ae5ec3fe2fbc608b00e6f4896f4d2d5c7e525fcbaa3eaa3cf2f3d5a983","README.md":"e6a98ee5630b9ce1a096a2907d095454f2770e298a5b0976ab552cc53ca96cfc","src/backend/context.rs":"33d9fdf1504fe1ae43d301e288daf6eaeabeb47aa0ef86efa135c6d984425fc4","src/backend/cork_state.rs":"4a0f1afc7d9f333dac89218cc56d7d32fbffb487cd48c1c9a4e03d79cb3b5e28","src/backend/intern.rs":"374a9a3bd79fddc47739dda1dbfc5929aea5a91946794fe65fba3c8d130fbda9","src/backend/mod.rs":"06ce9250865abf0ea461f215b128470636d072a6776821efef3caf5a7b992fb9","src/backend/stream.rs":"ce537c89dc5329f19d287e547b6c556a836ef42439cffce9d2cf3eaa18a65a0b","src/capi.rs":"b2c1be8128cadd36caa65c80950440f9d6f2aa0c24cc7bae6a9eaf6347ac454d","src/lib.rs":"7282560d84b134b09acfd8d6282600982e42fb3557f72454c535637cc26c7bf6"},"package":null}
|
||||
2
third_party/rust/cubeb-pulse/Cargo.toml
vendored
2
third_party/rust/cubeb-pulse/Cargo.toml
vendored
|
|
@ -12,7 +12,7 @@ pulse-dlopen = ["pulse-ffi/dlopen"]
|
|||
crate-type = ["staticlib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
cubeb-backend = "0.6"
|
||||
cubeb-backend = "0.7"
|
||||
pulse-ffi = { path = "pulse-ffi" }
|
||||
pulse = { path = "pulse-rs" }
|
||||
semver = "^0.9"
|
||||
|
|
|
|||
|
|
@ -642,6 +642,10 @@ impl<'ctx> StreamOps for PulseStream<'ctx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn input_latency(&mut self) -> Result<u32> {
|
||||
Err(Error::error())
|
||||
}
|
||||
|
||||
fn set_volume(&mut self, volume: f32) -> Result<()> {
|
||||
match self.output_stream {
|
||||
None => Err(Error::error()),
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
2
third_party/rust/cubeb-sys/Cargo.toml
vendored
2
third_party/rust/cubeb-sys/Cargo.toml
vendored
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
[package]
|
||||
name = "cubeb-sys"
|
||||
version = "0.6.2"
|
||||
version = "0.7.0"
|
||||
authors = ["Dan Glastonbury <dglastonbury@mozilla.com>"]
|
||||
build = "build.rs"
|
||||
links = "cubeb"
|
||||
|
|
|
|||
12
third_party/rust/cubeb-sys/build.rs
vendored
12
third_party/rust/cubeb-sys/build.rs
vendored
|
|
@ -40,6 +40,7 @@ fn main() {
|
|||
// let host = env::var("HOST").unwrap();
|
||||
let windows = target.contains("windows");
|
||||
let darwin = target.contains("darwin");
|
||||
let freebsd = target.contains("freebsd");
|
||||
let mut cfg = cmake::Config::new("libcubeb");
|
||||
|
||||
let _ = fs::remove_dir_all(env::var("OUT_DIR").unwrap());
|
||||
|
|
@ -48,25 +49,28 @@ fn main() {
|
|||
env::remove_var("DESTDIR");
|
||||
let dst = cfg.define("BUILD_SHARED_LIBS", "OFF")
|
||||
.define("BUILD_TESTS", "OFF")
|
||||
.define("BUILD_TOOLS", "OFF")
|
||||
.build();
|
||||
|
||||
println!("cargo:rustc-link-lib=static=cubeb");
|
||||
if windows {
|
||||
println!("cargo:rustc-link-lib=static=cubeb");
|
||||
println!("cargo:rustc-link-lib=dylib=avrt");
|
||||
println!("cargo:rustc-link-lib=dylib=ole32");
|
||||
println!("cargo:rustc-link-lib=dylib=user32");
|
||||
println!("cargo:rustc-link-lib=dylib=winmm");
|
||||
println!("cargo:rustc-link-search=native={}/lib", dst.display());
|
||||
} else if darwin {
|
||||
println!("cargo:rustc-link-lib=static=cubeb");
|
||||
println!("cargo:rustc-link-lib=framework=AudioUnit");
|
||||
println!("cargo:rustc-link-lib=framework=CoreAudio");
|
||||
println!("cargo:rustc-link-lib=framework=CoreServices");
|
||||
println!("cargo:rustc-link-lib=dylib=c++");
|
||||
println!("cargo:rustc-link-search=native={}/lib", dst.display());
|
||||
} else {
|
||||
println!("cargo:rustc-link-lib=static=cubeb");
|
||||
println!("cargo:rustc-link-lib=dylib=stdc++");
|
||||
if freebsd {
|
||||
println!("cargo:rustc-link-lib=dylib=c++");
|
||||
} else {
|
||||
println!("cargo:rustc-link-lib=dylib=stdc++");
|
||||
}
|
||||
println!("cargo:rustc-link-search=native={}/lib", dst.display());
|
||||
println!("cargo:rustc-link-search=native={}/lib64", dst.display());
|
||||
|
||||
|
|
|
|||
|
|
@ -155,10 +155,7 @@ if(USE_PULSE)
|
|||
target_sources(cubeb PRIVATE
|
||||
src/cubeb_pulse.c)
|
||||
target_compile_definitions(cubeb PRIVATE USE_PULSE)
|
||||
target_link_libraries(cubeb PRIVATE pulse pthread)
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
target_link_libraries(cubeb PRIVATE dl)
|
||||
endif()
|
||||
target_link_libraries(cubeb PRIVATE pthread ${CMAKE_DL_LIBS})
|
||||
endif()
|
||||
|
||||
check_include_files(alsa/asoundlib.h USE_ALSA)
|
||||
|
|
@ -166,10 +163,7 @@ if(USE_ALSA)
|
|||
target_sources(cubeb PRIVATE
|
||||
src/cubeb_alsa.c)
|
||||
target_compile_definitions(cubeb PRIVATE USE_ALSA)
|
||||
target_link_libraries(cubeb PRIVATE asound pthread)
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
target_link_libraries(cubeb PRIVATE dl)
|
||||
endif()
|
||||
target_link_libraries(cubeb PRIVATE pthread ${CMAKE_DL_LIBS})
|
||||
endif()
|
||||
|
||||
check_include_files(jack/jack.h USE_JACK)
|
||||
|
|
@ -177,10 +171,7 @@ if(USE_JACK)
|
|||
target_sources(cubeb PRIVATE
|
||||
src/cubeb_jack.cpp)
|
||||
target_compile_definitions(cubeb PRIVATE USE_JACK)
|
||||
target_link_libraries(cubeb PRIVATE jack pthread)
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
target_link_libraries(cubeb PRIVATE dl)
|
||||
endif()
|
||||
target_link_libraries(cubeb PRIVATE pthread ${CMAKE_DL_LIBS})
|
||||
endif()
|
||||
|
||||
check_include_files(audioclient.h USE_WASAPI)
|
||||
|
|
@ -221,7 +212,7 @@ if(USE_SNDIO)
|
|||
target_sources(cubeb PRIVATE
|
||||
src/cubeb_sndio.c)
|
||||
target_compile_definitions(cubeb PRIVATE USE_SNDIO)
|
||||
target_link_libraries(cubeb PRIVATE sndio)
|
||||
target_link_libraries(cubeb PRIVATE pthread ${CMAKE_DL_LIBS})
|
||||
endif()
|
||||
|
||||
check_include_files(sys/audioio.h USE_SUN)
|
||||
|
|
@ -248,6 +239,7 @@ if(USE_PULSE_RUST)
|
|||
DOWNLOAD_COMMAND ""
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND cargo build COMMAND cargo build --release
|
||||
BUILD_ALWAYS ON
|
||||
BINARY_DIR "${CMAKE_SOURCE_DIR}/src/cubeb-pulse-rs"
|
||||
INSTALL_COMMAND ""
|
||||
LOG_BUILD ON)
|
||||
|
|
@ -266,6 +258,7 @@ if(USE_AUDIOUNIT_RUST)
|
|||
DOWNLOAD_COMMAND ""
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND cargo build COMMAND cargo build --release
|
||||
BUILD_ALWAYS ON
|
||||
BINARY_DIR "${CMAKE_SOURCE_DIR}/src/cubeb-coreaudio-rs"
|
||||
INSTALL_COMMAND ""
|
||||
LOG_BUILD ON)
|
||||
|
|
|
|||
|
|
@ -3,4 +3,6 @@
|
|||
|
||||
See INSTALL.md for build instructions.
|
||||
|
||||
See [Backend Support](https://github.com/kinetiknz/cubeb/wiki/Backend-Support) in the wiki for the support level of each backend.
|
||||
|
||||
Licensed under an ISC-style license. See LICENSE for details.
|
||||
|
|
|
|||
41
third_party/rust/cubeb-sys/libcubeb/TODO
vendored
41
third_party/rust/cubeb-sys/libcubeb/TODO
vendored
|
|
@ -1,41 +0,0 @@
|
|||
TODO:
|
||||
- directsound: incomplete and somewhat broken
|
||||
- osx: understand why AudioQueueGetCurrentTime can return negative mSampleTime
|
||||
- test (and fix) sub-prefill size data playback
|
||||
- report stream delay instead of position; leave position calculation to user
|
||||
- capture support
|
||||
- capture and output enumeration and configuration
|
||||
- also expose default hardware config to allow decisions on speaker layout
|
||||
- prefill occurs at different times in each backend:
|
||||
- pulse prefills async off worker thread after init
|
||||
- coreaudio prefills during init
|
||||
- alsa prefills async after start
|
||||
- expose configured prefill size; may differ from requested latency
|
||||
- solved by exposing stream delay
|
||||
- xruns may occur in user callback but also in audio hardware
|
||||
may need to expose details of hardware xruns to user api
|
||||
- document thread safety
|
||||
- document which calls may block, and when effects take effect
|
||||
- document what's permissible inside callbacks
|
||||
- implement basic channel mapping for surround
|
||||
- vorbis has documented mapping based on channel count (if mapping type ==
|
||||
0) -- http://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-800004.3.9
|
||||
1 -> M
|
||||
2 -> L, R
|
||||
3 -> L, C, R
|
||||
4 -> L, R, RL, RR
|
||||
5 -> L, C, R, RL, RR
|
||||
6 -> L, C, R, RL, RR, LFE
|
||||
7 -> L, C, R, SL, SR, RC, LFE
|
||||
8 -> L, C, R, SL, SR, RL, RR, LFE
|
||||
>8 -> application defined
|
||||
- wave files with channel count only
|
||||
3 -> L, R, C
|
||||
4 -> L, R, RL, RR
|
||||
5 -> L, R, C, RL, RR
|
||||
6 -> L, R, C, LFE, RL, RR
|
||||
7 -> L, R, C, LFE, RC, SL, SR
|
||||
8 -> L, R, C, LFE, RL, RR, SL, SR
|
||||
- wave files with WAVE_FORMAT_EXTENSIBLE have explicitly mappings, can
|
||||
extract these
|
||||
- implement configurable channel mapping
|
||||
|
|
@ -31,19 +31,13 @@ extern "C" {
|
|||
|
||||
@code
|
||||
cubeb * app_ctx;
|
||||
cubeb_init(&app_ctx, "Example Application");
|
||||
cubeb_init(&app_ctx, "Example Application", NULL);
|
||||
int rv;
|
||||
uint32_t rate;
|
||||
uint32_t latency_frames;
|
||||
uint64_t ts;
|
||||
|
||||
rv = cubeb_get_min_latency(app_ctx, &output_params, &latency_frames);
|
||||
if (rv != CUBEB_OK) {
|
||||
fprintf(stderr, "Could not get minimum latency");
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = cubeb_get_preferred_sample_rate(app_ctx, output_params, &rate);
|
||||
rv = cubeb_get_preferred_sample_rate(app_ctx, &rate);
|
||||
if (rv != CUBEB_OK) {
|
||||
fprintf(stderr, "Could not get preferred sample-rate");
|
||||
return rv;
|
||||
|
|
@ -56,6 +50,12 @@ extern "C" {
|
|||
output_params.layout = CUBEB_LAYOUT_UNDEFINED;
|
||||
output_params.prefs = CUBEB_STREAM_PREF_NONE;
|
||||
|
||||
rv = cubeb_get_min_latency(app_ctx, &output_params, &latency_frames);
|
||||
if (rv != CUBEB_OK) {
|
||||
fprintf(stderr, "Could not get minimum latency");
|
||||
return rv;
|
||||
}
|
||||
|
||||
cubeb_stream_params input_params;
|
||||
input_params.format = CUBEB_SAMPLE_FLOAT32NE;
|
||||
input_params.rate = rate;
|
||||
|
|
@ -97,14 +97,14 @@ extern "C" {
|
|||
|
||||
@code
|
||||
long data_cb(cubeb_stream * stm, void * user,
|
||||
void * input_buffer, void * output_buffer, long nframes)
|
||||
const void * input_buffer, void * output_buffer, long nframes)
|
||||
{
|
||||
float * in = input_buffer;
|
||||
const float * in = input_buffer;
|
||||
float * out = output_buffer;
|
||||
|
||||
for (i = 0; i < nframes; ++i) {
|
||||
for (c = 0; c < 2; ++c) {
|
||||
out[i][c] = in[i];
|
||||
for (int i = 0; i < nframes; ++i) {
|
||||
for (int c = 0; c < 2; ++c) {
|
||||
out[2 * i + c] = in[i];
|
||||
}
|
||||
}
|
||||
return nframes;
|
||||
|
|
@ -559,6 +559,16 @@ CUBEB_EXPORT int cubeb_stream_get_position(cubeb_stream * stream, uint64_t * pos
|
|||
@retval CUBEB_ERROR */
|
||||
CUBEB_EXPORT int cubeb_stream_get_latency(cubeb_stream * stream, uint32_t * latency);
|
||||
|
||||
/** Get the input latency for this stream, in frames. This is the number of
|
||||
frames between the time the audio input devices records the data, and they
|
||||
are available in the data callback.
|
||||
This returns CUBEB_ERROR when the stream is output-only.
|
||||
@param stream
|
||||
@param latency Current approximate stream latency in frames.
|
||||
@retval CUBEB_OK
|
||||
@retval CUBEB_ERROR_NOT_SUPPORTED
|
||||
@retval CUBEB_ERROR */
|
||||
CUBEB_EXPORT int cubeb_stream_get_input_latency(cubeb_stream * stream, uint32_t * latency);
|
||||
/** Set the volume for a stream.
|
||||
@param stream the stream for which to adjust the volume.
|
||||
@param volume a float between 0.0 (muted) and 1.0 (maximum volume)
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ cubeb_output_latency_load_method(int version)
|
|||
bool
|
||||
cubeb_output_latency_method_is_loaded(output_latency_function * ol)
|
||||
{
|
||||
assert(ol && (ol->from_jni || ol->from_lib));
|
||||
assert(ol);
|
||||
if (ol->version > ANDROID_JELLY_BEAN_MR1_4_2){
|
||||
return !!ol->from_jni;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ struct cubeb_ops {
|
|||
int (* stream_reset_default_device)(cubeb_stream * stream);
|
||||
int (* stream_get_position)(cubeb_stream * stream, uint64_t * position);
|
||||
int (* stream_get_latency)(cubeb_stream * stream, uint32_t * latency);
|
||||
int (* stream_get_input_latency)(cubeb_stream * stream, uint32_t * latency);
|
||||
int (* stream_set_volume)(cubeb_stream * stream, float volumes);
|
||||
int (* stream_get_current_device)(cubeb_stream * stream,
|
||||
cubeb_device ** const device);
|
||||
|
|
|
|||
22
third_party/rust/cubeb-sys/libcubeb/src/cubeb.c
vendored
22
third_party/rust/cubeb-sys/libcubeb/src/cubeb.c
vendored
|
|
@ -80,7 +80,7 @@ validate_stream_params(cubeb_stream_params * input_stream_params,
|
|||
}
|
||||
if (input_stream_params) {
|
||||
if (input_stream_params->rate < 1000 || input_stream_params->rate > 192000 ||
|
||||
input_stream_params->channels < 1 || input_stream_params->channels > 8) {
|
||||
input_stream_params->channels < 1 || input_stream_params->channels > UINT8_MAX) {
|
||||
return CUBEB_ERROR_INVALID_FORMAT;
|
||||
}
|
||||
}
|
||||
|
|
@ -194,6 +194,9 @@ cubeb_init(cubeb ** context, char const * context_name, char const * backend_nam
|
|||
#if defined(USE_JACK)
|
||||
jack_init,
|
||||
#endif
|
||||
#if defined(USE_SNDIO)
|
||||
sndio_init,
|
||||
#endif
|
||||
#if defined(USE_ALSA)
|
||||
alsa_init,
|
||||
#endif
|
||||
|
|
@ -209,9 +212,6 @@ cubeb_init(cubeb ** context, char const * context_name, char const * backend_nam
|
|||
#if defined(USE_WINMM)
|
||||
winmm_init,
|
||||
#endif
|
||||
#if defined(USE_SNDIO)
|
||||
sndio_init,
|
||||
#endif
|
||||
#if defined(USE_SUN)
|
||||
sun_init,
|
||||
#endif
|
||||
|
|
@ -420,6 +420,20 @@ cubeb_stream_get_latency(cubeb_stream * stream, uint32_t * latency)
|
|||
return stream->context->ops->stream_get_latency(stream, latency);
|
||||
}
|
||||
|
||||
int
|
||||
cubeb_stream_get_input_latency(cubeb_stream * stream, uint32_t * latency)
|
||||
{
|
||||
if (!stream || !latency) {
|
||||
return CUBEB_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!stream->context->ops->stream_get_input_latency) {
|
||||
return CUBEB_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
return stream->context->ops->stream_get_input_latency(stream, latency);
|
||||
}
|
||||
|
||||
int
|
||||
cubeb_stream_set_volume(cubeb_stream * stream, float volume)
|
||||
{
|
||||
|
|
|
|||
197
third_party/rust/cubeb-sys/libcubeb/src/cubeb_alsa.c
vendored
197
third_party/rust/cubeb-sys/libcubeb/src/cubeb_alsa.c
vendored
|
|
@ -14,10 +14,58 @@
|
|||
#include <limits.h>
|
||||
#include <poll.h>
|
||||
#include <unistd.h>
|
||||
#include <dlfcn.h>
|
||||
#include <alsa/asoundlib.h>
|
||||
#include "cubeb/cubeb.h"
|
||||
#include "cubeb-internal.h"
|
||||
|
||||
#ifdef DISABLE_LIBASOUND_DLOPEN
|
||||
#define WRAP(x) x
|
||||
#else
|
||||
#define WRAP(x) cubeb_##x
|
||||
#define LIBASOUND_API_VISIT(X) \
|
||||
X(snd_config) \
|
||||
X(snd_config_add) \
|
||||
X(snd_config_copy) \
|
||||
X(snd_config_delete) \
|
||||
X(snd_config_get_id) \
|
||||
X(snd_config_get_string) \
|
||||
X(snd_config_imake_integer) \
|
||||
X(snd_config_search) \
|
||||
X(snd_config_search_definition) \
|
||||
X(snd_lib_error_set_handler) \
|
||||
X(snd_pcm_avail_update) \
|
||||
X(snd_pcm_close) \
|
||||
X(snd_pcm_delay) \
|
||||
X(snd_pcm_drain) \
|
||||
X(snd_pcm_frames_to_bytes) \
|
||||
X(snd_pcm_get_params) \
|
||||
X(snd_pcm_hw_params_any) \
|
||||
X(snd_pcm_hw_params_get_channels_max) \
|
||||
X(snd_pcm_hw_params_get_rate) \
|
||||
X(snd_pcm_hw_params_set_rate_near) \
|
||||
X(snd_pcm_hw_params_sizeof) \
|
||||
X(snd_pcm_nonblock) \
|
||||
X(snd_pcm_open) \
|
||||
X(snd_pcm_open_lconf) \
|
||||
X(snd_pcm_pause) \
|
||||
X(snd_pcm_poll_descriptors) \
|
||||
X(snd_pcm_poll_descriptors_count) \
|
||||
X(snd_pcm_poll_descriptors_revents) \
|
||||
X(snd_pcm_readi) \
|
||||
X(snd_pcm_recover) \
|
||||
X(snd_pcm_set_params) \
|
||||
X(snd_pcm_start) \
|
||||
X(snd_pcm_state) \
|
||||
X(snd_pcm_writei) \
|
||||
|
||||
#define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x;
|
||||
LIBASOUND_API_VISIT(MAKE_TYPEDEF);
|
||||
#undef MAKE_TYPEDEF
|
||||
/* snd_pcm_hw_params_alloca is actually a macro */
|
||||
#define snd_pcm_hw_params_sizeof cubeb_snd_pcm_hw_params_sizeof
|
||||
#endif
|
||||
|
||||
#define CUBEB_STREAM_MAX 16
|
||||
#define CUBEB_WATCHDOG_MS 10000
|
||||
|
||||
|
|
@ -36,6 +84,7 @@ static struct cubeb_ops const alsa_ops;
|
|||
|
||||
struct cubeb {
|
||||
struct cubeb_ops const * ops;
|
||||
void * libasound;
|
||||
|
||||
pthread_t thread;
|
||||
|
||||
|
|
@ -245,8 +294,8 @@ set_timeout(struct timeval * timeout, unsigned int ms)
|
|||
static void
|
||||
stream_buffer_decrement(cubeb_stream * stm, long count)
|
||||
{
|
||||
char * bufremains = stm->buffer + snd_pcm_frames_to_bytes(stm->pcm, count);
|
||||
memmove(stm->buffer, bufremains, snd_pcm_frames_to_bytes(stm->pcm, stm->bufframes - count));
|
||||
char * bufremains = stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, count);
|
||||
memmove(stm->buffer, bufremains, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes - count));
|
||||
stm->bufframes -= count;
|
||||
}
|
||||
|
||||
|
|
@ -278,9 +327,9 @@ alsa_process_stream(cubeb_stream * stm)
|
|||
/* Call _poll_descriptors_revents() even if we don't use it
|
||||
to let underlying plugins clear null events. Otherwise poll()
|
||||
may wake up again and again, producing unnecessary CPU usage. */
|
||||
snd_pcm_poll_descriptors_revents(stm->pcm, stm->fds, stm->nfds, &revents);
|
||||
WRAP(snd_pcm_poll_descriptors_revents)(stm->pcm, stm->fds, stm->nfds, &revents);
|
||||
|
||||
avail = snd_pcm_avail_update(stm->pcm);
|
||||
avail = WRAP(snd_pcm_avail_update)(stm->pcm);
|
||||
|
||||
/* Got null event? Bail and wait for another wakeup. */
|
||||
if (avail == 0) {
|
||||
|
|
@ -303,7 +352,7 @@ alsa_process_stream(cubeb_stream * stm)
|
|||
// TODO: should it be marked as DRAINING?
|
||||
}
|
||||
|
||||
got = snd_pcm_readi(stm->pcm, stm->buffer+stm->bufframes, avail);
|
||||
got = WRAP(snd_pcm_readi)(stm->pcm, stm->buffer+stm->bufframes, avail);
|
||||
|
||||
if (got < 0) {
|
||||
avail = got; // the error handler below will recover us
|
||||
|
|
@ -347,7 +396,7 @@ alsa_process_stream(cubeb_stream * stm)
|
|||
(!stm->other_stream || stm->other_stream->bufframes > 0)) {
|
||||
long got = avail - stm->bufframes;
|
||||
void * other_buffer = stm->other_stream ? stm->other_stream->buffer : NULL;
|
||||
char * buftail = stm->buffer + snd_pcm_frames_to_bytes(stm->pcm, stm->bufframes);
|
||||
char * buftail = stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes);
|
||||
|
||||
/* Correct read size to the other stream available frames */
|
||||
if (stm->other_stream && got > (snd_pcm_sframes_t) stm->other_stream->bufframes) {
|
||||
|
|
@ -374,8 +423,8 @@ alsa_process_stream(cubeb_stream * stm)
|
|||
long drain_frames = avail - stm->bufframes;
|
||||
double drain_time = (double) drain_frames / stm->params.rate;
|
||||
|
||||
char * buftail = stm->buffer + snd_pcm_frames_to_bytes(stm->pcm, stm->bufframes);
|
||||
memset(buftail, 0, snd_pcm_frames_to_bytes(stm->pcm, drain_frames));
|
||||
char * buftail = stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes);
|
||||
memset(buftail, 0, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, drain_frames));
|
||||
stm->bufframes = avail;
|
||||
|
||||
/* Mark as draining, unless we're waiting for capture */
|
||||
|
|
@ -402,7 +451,7 @@ alsa_process_stream(cubeb_stream * stm)
|
|||
}
|
||||
}
|
||||
|
||||
wrote = snd_pcm_writei(stm->pcm, stm->buffer, avail);
|
||||
wrote = WRAP(snd_pcm_writei)(stm->pcm, stm->buffer, avail);
|
||||
if (wrote < 0) {
|
||||
avail = wrote; // the error handler below will recover us
|
||||
} else {
|
||||
|
|
@ -415,13 +464,13 @@ alsa_process_stream(cubeb_stream * stm)
|
|||
|
||||
/* Got some error? Let's try to recover the stream. */
|
||||
if (avail < 0) {
|
||||
avail = snd_pcm_recover(stm->pcm, avail, 0);
|
||||
avail = WRAP(snd_pcm_recover)(stm->pcm, avail, 0);
|
||||
|
||||
/* Capture pcm must be started after initial setup/recover */
|
||||
if (avail >= 0 &&
|
||||
stm->stream_type == SND_PCM_STREAM_CAPTURE &&
|
||||
snd_pcm_state(stm->pcm) == SND_PCM_STATE_PREPARED) {
|
||||
avail = snd_pcm_start(stm->pcm);
|
||||
WRAP(snd_pcm_state)(stm->pcm) == SND_PCM_STATE_PREPARED) {
|
||||
avail = WRAP(snd_pcm_start)(stm->pcm);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -537,26 +586,26 @@ get_slave_pcm_node(snd_config_t * lconf, snd_config_t * root_pcm)
|
|||
|
||||
slave_def = NULL;
|
||||
|
||||
r = snd_config_search(root_pcm, "slave", &slave_pcm);
|
||||
r = WRAP(snd_config_search)(root_pcm, "slave", &slave_pcm);
|
||||
if (r < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
r = snd_config_get_string(slave_pcm, &string);
|
||||
r = WRAP(snd_config_get_string)(slave_pcm, &string);
|
||||
if (r >= 0) {
|
||||
r = snd_config_search_definition(lconf, "pcm_slave", string, &slave_def);
|
||||
r = WRAP(snd_config_search_definition)(lconf, "pcm_slave", string, &slave_def);
|
||||
if (r < 0) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
r = snd_config_search(slave_def ? slave_def : slave_pcm, "pcm", &pcm);
|
||||
r = WRAP(snd_config_search)(slave_def ? slave_def : slave_pcm, "pcm", &pcm);
|
||||
if (r < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
r = snd_config_get_string(slave_def ? slave_def : slave_pcm, &string);
|
||||
r = WRAP(snd_config_get_string)(slave_def ? slave_def : slave_pcm, &string);
|
||||
if (r < 0) {
|
||||
break;
|
||||
}
|
||||
|
|
@ -565,7 +614,7 @@ get_slave_pcm_node(snd_config_t * lconf, snd_config_t * root_pcm)
|
|||
if (r < 0 || r > (int) sizeof(node_name)) {
|
||||
break;
|
||||
}
|
||||
r = snd_config_search(lconf, node_name, &pcm);
|
||||
r = WRAP(snd_config_search)(lconf, node_name, &pcm);
|
||||
if (r < 0) {
|
||||
break;
|
||||
}
|
||||
|
|
@ -574,7 +623,7 @@ get_slave_pcm_node(snd_config_t * lconf, snd_config_t * root_pcm)
|
|||
} while (0);
|
||||
|
||||
if (slave_def) {
|
||||
snd_config_delete(slave_def);
|
||||
WRAP(snd_config_delete)(slave_def);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
|
@ -597,22 +646,22 @@ init_local_config_with_workaround(char const * pcm_name)
|
|||
|
||||
lconf = NULL;
|
||||
|
||||
if (snd_config == NULL) {
|
||||
if (*WRAP(snd_config) == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
r = snd_config_copy(&lconf, snd_config);
|
||||
r = WRAP(snd_config_copy)(&lconf, *WRAP(snd_config));
|
||||
if (r < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
do {
|
||||
r = snd_config_search_definition(lconf, "pcm", pcm_name, &pcm_node);
|
||||
r = WRAP(snd_config_search_definition)(lconf, "pcm", pcm_name, &pcm_node);
|
||||
if (r < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
r = snd_config_get_id(pcm_node, &string);
|
||||
r = WRAP(snd_config_get_id)(pcm_node, &string);
|
||||
if (r < 0) {
|
||||
break;
|
||||
}
|
||||
|
|
@ -621,7 +670,7 @@ init_local_config_with_workaround(char const * pcm_name)
|
|||
if (r < 0 || r > (int) sizeof(node_name)) {
|
||||
break;
|
||||
}
|
||||
r = snd_config_search(lconf, node_name, &pcm_node);
|
||||
r = WRAP(snd_config_search)(lconf, node_name, &pcm_node);
|
||||
if (r < 0) {
|
||||
break;
|
||||
}
|
||||
|
|
@ -632,12 +681,12 @@ init_local_config_with_workaround(char const * pcm_name)
|
|||
}
|
||||
|
||||
/* Fetch the PCM node's type, and bail out if it's not the PulseAudio plugin. */
|
||||
r = snd_config_search(pcm_node, "type", &node);
|
||||
r = WRAP(snd_config_search)(pcm_node, "type", &node);
|
||||
if (r < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
r = snd_config_get_string(node, &string);
|
||||
r = WRAP(snd_config_get_string)(node, &string);
|
||||
if (r < 0) {
|
||||
break;
|
||||
}
|
||||
|
|
@ -648,18 +697,18 @@ init_local_config_with_workaround(char const * pcm_name)
|
|||
|
||||
/* Don't clobber an explicit existing handle_underrun value, set it only
|
||||
if it doesn't already exist. */
|
||||
r = snd_config_search(pcm_node, "handle_underrun", &node);
|
||||
r = WRAP(snd_config_search)(pcm_node, "handle_underrun", &node);
|
||||
if (r != -ENOENT) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Disable pcm_pulse's asynchronous underrun handling. */
|
||||
r = snd_config_imake_integer(&node, "handle_underrun", 0);
|
||||
r = WRAP(snd_config_imake_integer)(&node, "handle_underrun", 0);
|
||||
if (r < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
r = snd_config_add(pcm_node, node);
|
||||
r = WRAP(snd_config_add)(pcm_node, node);
|
||||
if (r < 0) {
|
||||
break;
|
||||
}
|
||||
|
|
@ -667,7 +716,7 @@ init_local_config_with_workaround(char const * pcm_name)
|
|||
return lconf;
|
||||
} while (0);
|
||||
|
||||
snd_config_delete(lconf);
|
||||
WRAP(snd_config_delete)(lconf);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -679,9 +728,9 @@ alsa_locked_pcm_open(snd_pcm_t ** pcm, char const * pcm_name, snd_pcm_stream_t s
|
|||
|
||||
pthread_mutex_lock(&cubeb_alsa_mutex);
|
||||
if (local_config) {
|
||||
r = snd_pcm_open_lconf(pcm, pcm_name, stream, SND_PCM_NONBLOCK, local_config);
|
||||
r = WRAP(snd_pcm_open_lconf)(pcm, pcm_name, stream, SND_PCM_NONBLOCK, local_config);
|
||||
} else {
|
||||
r = snd_pcm_open(pcm, pcm_name, stream, SND_PCM_NONBLOCK);
|
||||
r = WRAP(snd_pcm_open)(pcm, pcm_name, stream, SND_PCM_NONBLOCK);
|
||||
}
|
||||
pthread_mutex_unlock(&cubeb_alsa_mutex);
|
||||
|
||||
|
|
@ -694,7 +743,7 @@ alsa_locked_pcm_close(snd_pcm_t * pcm)
|
|||
int r;
|
||||
|
||||
pthread_mutex_lock(&cubeb_alsa_mutex);
|
||||
r = snd_pcm_close(pcm);
|
||||
r = WRAP(snd_pcm_close)(pcm);
|
||||
pthread_mutex_unlock(&cubeb_alsa_mutex);
|
||||
|
||||
return r;
|
||||
|
|
@ -750,6 +799,7 @@ silent_error_handler(char const * file, int line, char const * function,
|
|||
alsa_init(cubeb ** context, char const * context_name)
|
||||
{
|
||||
(void)context_name;
|
||||
void * libasound = NULL;
|
||||
cubeb * ctx;
|
||||
int r;
|
||||
int i;
|
||||
|
|
@ -760,9 +810,30 @@ alsa_init(cubeb ** context, char const * context_name)
|
|||
assert(context);
|
||||
*context = NULL;
|
||||
|
||||
#ifndef DISABLE_LIBASOUND_DLOPEN
|
||||
libasound = dlopen("libasound.so.2", RTLD_LAZY);
|
||||
if (!libasound) {
|
||||
libasound = dlopen("libasound.so", RTLD_LAZY);
|
||||
if (!libasound) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
#define LOAD(x) { \
|
||||
cubeb_##x = dlsym(libasound, #x); \
|
||||
if (!cubeb_##x) { \
|
||||
dlclose(libasound); \
|
||||
return CUBEB_ERROR; \
|
||||
} \
|
||||
}
|
||||
|
||||
LIBASOUND_API_VISIT(LOAD);
|
||||
#undef LOAD
|
||||
#endif
|
||||
|
||||
pthread_mutex_lock(&cubeb_alsa_mutex);
|
||||
if (!cubeb_alsa_error_handler_set) {
|
||||
snd_lib_error_set_handler(silent_error_handler);
|
||||
WRAP(snd_lib_error_set_handler)(silent_error_handler);
|
||||
cubeb_alsa_error_handler_set = 1;
|
||||
}
|
||||
pthread_mutex_unlock(&cubeb_alsa_mutex);
|
||||
|
|
@ -771,6 +842,7 @@ alsa_init(cubeb ** context, char const * context_name)
|
|||
assert(ctx);
|
||||
|
||||
ctx->ops = &alsa_ops;
|
||||
ctx->libasound = libasound;
|
||||
|
||||
r = pthread_mutex_init(&ctx->mutex, NULL);
|
||||
assert(r == 0);
|
||||
|
|
@ -819,7 +891,7 @@ alsa_init(cubeb ** context, char const * context_name)
|
|||
config fails with EINVAL, the PA PCM is too old for this workaround. */
|
||||
if (r == -EINVAL) {
|
||||
pthread_mutex_lock(&cubeb_alsa_mutex);
|
||||
snd_config_delete(ctx->local_config);
|
||||
WRAP(snd_config_delete)(ctx->local_config);
|
||||
pthread_mutex_unlock(&cubeb_alsa_mutex);
|
||||
ctx->local_config = NULL;
|
||||
} else if (r >= 0) {
|
||||
|
|
@ -861,10 +933,14 @@ alsa_destroy(cubeb * ctx)
|
|||
|
||||
if (ctx->local_config) {
|
||||
pthread_mutex_lock(&cubeb_alsa_mutex);
|
||||
snd_config_delete(ctx->local_config);
|
||||
WRAP(snd_config_delete)(ctx->local_config);
|
||||
pthread_mutex_unlock(&cubeb_alsa_mutex);
|
||||
}
|
||||
|
||||
if (ctx->libasound) {
|
||||
dlclose(ctx->libasound);
|
||||
}
|
||||
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
|
|
@ -948,7 +1024,7 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * stream
|
|||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
r = snd_pcm_nonblock(stm->pcm, 1);
|
||||
r = WRAP(snd_pcm_nonblock)(stm->pcm, 1);
|
||||
assert(r == 0);
|
||||
|
||||
latency_us = latency_frames * 1e6 / stm->params.rate;
|
||||
|
|
@ -961,7 +1037,7 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * stream
|
|||
latency_us = latency_us < min_latency ? min_latency: latency_us;
|
||||
}
|
||||
|
||||
r = snd_pcm_set_params(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED,
|
||||
r = WRAP(snd_pcm_set_params)(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED,
|
||||
stm->params.channels, stm->params.rate, 1,
|
||||
latency_us);
|
||||
if (r < 0) {
|
||||
|
|
@ -969,20 +1045,20 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * stream
|
|||
return CUBEB_ERROR_INVALID_FORMAT;
|
||||
}
|
||||
|
||||
r = snd_pcm_get_params(stm->pcm, &stm->buffer_size, &period_size);
|
||||
r = WRAP(snd_pcm_get_params)(stm->pcm, &stm->buffer_size, &period_size);
|
||||
assert(r == 0);
|
||||
|
||||
/* Double internal buffer size to have enough space when waiting for the other side of duplex connection */
|
||||
stm->buffer_size *= 2;
|
||||
stm->buffer = calloc(1, snd_pcm_frames_to_bytes(stm->pcm, stm->buffer_size));
|
||||
stm->buffer = calloc(1, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->buffer_size));
|
||||
assert(stm->buffer);
|
||||
|
||||
stm->nfds = snd_pcm_poll_descriptors_count(stm->pcm);
|
||||
stm->nfds = WRAP(snd_pcm_poll_descriptors_count)(stm->pcm);
|
||||
assert(stm->nfds > 0);
|
||||
|
||||
stm->saved_fds = calloc(stm->nfds, sizeof(struct pollfd));
|
||||
assert(stm->saved_fds);
|
||||
r = snd_pcm_poll_descriptors(stm->pcm, stm->saved_fds, stm->nfds);
|
||||
r = WRAP(snd_pcm_poll_descriptors)(stm->pcm, stm->saved_fds, stm->nfds);
|
||||
assert((nfds_t) r == stm->nfds);
|
||||
|
||||
if (alsa_register_stream(ctx, stm) != 0) {
|
||||
|
|
@ -1054,7 +1130,7 @@ alsa_stream_destroy(cubeb_stream * stm)
|
|||
pthread_mutex_lock(&stm->mutex);
|
||||
if (stm->pcm) {
|
||||
if (stm->state == DRAINING) {
|
||||
snd_pcm_drain(stm->pcm);
|
||||
WRAP(snd_pcm_drain)(stm->pcm);
|
||||
}
|
||||
alsa_locked_pcm_close(stm->pcm);
|
||||
stm->pcm = NULL;
|
||||
|
|
@ -1100,12 +1176,12 @@ alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
|
|||
|
||||
assert(stm);
|
||||
|
||||
r = snd_pcm_hw_params_any(stm->pcm, hw_params);
|
||||
r = WRAP(snd_pcm_hw_params_any)(stm->pcm, hw_params);
|
||||
if (r < 0) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
r = snd_pcm_hw_params_get_channels_max(hw_params, max_channels);
|
||||
r = WRAP(snd_pcm_hw_params_get_channels_max)(hw_params, max_channels);
|
||||
if (r < 0) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
|
@ -1126,34 +1202,34 @@ alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) {
|
|||
|
||||
/* get a pcm, disabling resampling, so we get a rate the
|
||||
* hardware/dmix/pulse/etc. supports. */
|
||||
r = snd_pcm_open(&pcm, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK, SND_PCM_NO_AUTO_RESAMPLE);
|
||||
r = WRAP(snd_pcm_open)(&pcm, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK, SND_PCM_NO_AUTO_RESAMPLE);
|
||||
if (r < 0) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
r = snd_pcm_hw_params_any(pcm, hw_params);
|
||||
r = WRAP(snd_pcm_hw_params_any)(pcm, hw_params);
|
||||
if (r < 0) {
|
||||
snd_pcm_close(pcm);
|
||||
WRAP(snd_pcm_close)(pcm);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
r = snd_pcm_hw_params_get_rate(hw_params, rate, &dir);
|
||||
r = WRAP(snd_pcm_hw_params_get_rate)(hw_params, rate, &dir);
|
||||
if (r >= 0) {
|
||||
/* There is a default rate: use it. */
|
||||
snd_pcm_close(pcm);
|
||||
WRAP(snd_pcm_close)(pcm);
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
/* Use a common rate, alsa may adjust it based on hw/etc. capabilities. */
|
||||
*rate = 44100;
|
||||
|
||||
r = snd_pcm_hw_params_set_rate_near(pcm, hw_params, rate, NULL);
|
||||
r = WRAP(snd_pcm_hw_params_set_rate_near)(pcm, hw_params, rate, NULL);
|
||||
if (r < 0) {
|
||||
snd_pcm_close(pcm);
|
||||
WRAP(snd_pcm_close)(pcm);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
snd_pcm_close(pcm);
|
||||
WRAP(snd_pcm_close)(pcm);
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
|
@ -1186,10 +1262,10 @@ alsa_stream_start(cubeb_stream * stm)
|
|||
pthread_mutex_lock(&stm->mutex);
|
||||
/* Capture pcm must be started after initial setup/recover */
|
||||
if (stm->stream_type == SND_PCM_STREAM_CAPTURE &&
|
||||
snd_pcm_state(stm->pcm) == SND_PCM_STATE_PREPARED) {
|
||||
snd_pcm_start(stm->pcm);
|
||||
WRAP(snd_pcm_state)(stm->pcm) == SND_PCM_STATE_PREPARED) {
|
||||
WRAP(snd_pcm_start)(stm->pcm);
|
||||
}
|
||||
snd_pcm_pause(stm->pcm, 0);
|
||||
WRAP(snd_pcm_pause)(stm->pcm, 0);
|
||||
gettimeofday(&stm->last_activity, NULL);
|
||||
pthread_mutex_unlock(&stm->mutex);
|
||||
|
||||
|
|
@ -1229,7 +1305,7 @@ alsa_stream_stop(cubeb_stream * stm)
|
|||
pthread_mutex_unlock(&ctx->mutex);
|
||||
|
||||
pthread_mutex_lock(&stm->mutex);
|
||||
snd_pcm_pause(stm->pcm, 1);
|
||||
WRAP(snd_pcm_pause)(stm->pcm, 1);
|
||||
pthread_mutex_unlock(&stm->mutex);
|
||||
|
||||
return CUBEB_OK;
|
||||
|
|
@ -1245,8 +1321,8 @@ alsa_stream_get_position(cubeb_stream * stm, uint64_t * position)
|
|||
pthread_mutex_lock(&stm->mutex);
|
||||
|
||||
delay = -1;
|
||||
if (snd_pcm_state(stm->pcm) != SND_PCM_STATE_RUNNING ||
|
||||
snd_pcm_delay(stm->pcm, &delay) != 0) {
|
||||
if (WRAP(snd_pcm_state)(stm->pcm) != SND_PCM_STATE_RUNNING ||
|
||||
WRAP(snd_pcm_delay)(stm->pcm, &delay) != 0) {
|
||||
*position = stm->last_position;
|
||||
pthread_mutex_unlock(&stm->mutex);
|
||||
return CUBEB_OK;
|
||||
|
|
@ -1271,7 +1347,7 @@ alsa_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
|
|||
snd_pcm_sframes_t delay;
|
||||
/* This function returns the delay in frames until a frame written using
|
||||
snd_pcm_writei is sent to the DAC. The DAC delay should be < 1ms anyways. */
|
||||
if (snd_pcm_delay(stm->pcm, &delay)) {
|
||||
if (WRAP(snd_pcm_delay)(stm->pcm, &delay)) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
|
|
@ -1368,6 +1444,7 @@ static struct cubeb_ops const alsa_ops = {
|
|||
.stream_reset_default_device = NULL,
|
||||
.stream_get_position = alsa_stream_get_position,
|
||||
.stream_get_latency = alsa_stream_get_latency,
|
||||
.stream_get_input_latency = NULL,
|
||||
.stream_set_volume = alsa_stream_set_volume,
|
||||
.stream_get_current_device = NULL,
|
||||
.stream_device_destroy = NULL,
|
||||
|
|
|
|||
|
|
@ -433,6 +433,7 @@ static struct cubeb_ops const audiotrack_ops = {
|
|||
.stream_reset_default_device = NULL,
|
||||
.stream_get_position = audiotrack_stream_get_position,
|
||||
.stream_get_latency = audiotrack_stream_get_latency,
|
||||
.stream_get_input_latency = NULL,
|
||||
.stream_set_volume = audiotrack_stream_set_volume,
|
||||
.stream_get_current_device = NULL,
|
||||
.stream_device_destroy = NULL,
|
||||
|
|
|
|||
|
|
@ -501,6 +501,17 @@ audiounit_input_callback(void * user_ptr,
|
|||
return noErr;
|
||||
}
|
||||
|
||||
if (stm->draining) {
|
||||
OSStatus r = AudioOutputUnitStop(stm->input_unit);
|
||||
assert(r == 0);
|
||||
// Only fire state callback in input-only stream. For duplex stream,
|
||||
// the state callback will be fired in output callback.
|
||||
if (stm->output_unit == NULL) {
|
||||
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
|
||||
}
|
||||
return noErr;
|
||||
}
|
||||
|
||||
OSStatus r = audiounit_render_input(stm, flags, tstamp, bus, input_frames);
|
||||
if (r != noErr) {
|
||||
return r;
|
||||
|
|
@ -520,12 +531,7 @@ audiounit_input_callback(void * user_ptr,
|
|||
&total_input_frames,
|
||||
NULL,
|
||||
0);
|
||||
if (outframes < total_input_frames) {
|
||||
OSStatus r = AudioOutputUnitStop(stm->input_unit);
|
||||
assert(r == 0);
|
||||
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
|
||||
return noErr;
|
||||
}
|
||||
stm->draining = outframes < total_input_frames;
|
||||
|
||||
// Reset input buffer
|
||||
stm->input_linear_buffer->clear();
|
||||
|
|
@ -612,10 +618,6 @@ audiounit_output_callback(void * user_ptr,
|
|||
if (stm->draining) {
|
||||
OSStatus r = AudioOutputUnitStop(stm->output_unit);
|
||||
assert(r == 0);
|
||||
if (stm->input_unit) {
|
||||
r = AudioOutputUnitStop(stm->input_unit);
|
||||
assert(r == 0);
|
||||
}
|
||||
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
|
||||
audiounit_make_silent(&outBufferList->mBuffers[0]);
|
||||
return noErr;
|
||||
|
|
@ -692,9 +694,14 @@ audiounit_output_callback(void * user_ptr,
|
|||
|
||||
/* Post process output samples. */
|
||||
if (stm->draining) {
|
||||
size_t outbpf = cubeb_sample_size(stm->output_stream_params.format);
|
||||
/* Clear missing frames (silence) */
|
||||
memset((uint8_t*)output_buffer + outframes * outbpf, 0, (output_frames - outframes) * outbpf);
|
||||
size_t channels = stm->output_stream_params.channels;
|
||||
size_t missing_samples = (output_frames - outframes) * channels;
|
||||
size_t size_sample = cubeb_sample_size(stm->output_stream_params.format);
|
||||
/* number of bytes that have been filled with valid audio by the callback. */
|
||||
size_t audio_byte_count = outframes * channels * size_sample;
|
||||
PodZero((uint8_t*)output_buffer + audio_byte_count,
|
||||
missing_samples * size_sample);
|
||||
}
|
||||
|
||||
/* Mixing */
|
||||
|
|
@ -2864,6 +2871,15 @@ audiounit_stream_destroy_internal(cubeb_stream *stm)
|
|||
static void
|
||||
audiounit_stream_destroy(cubeb_stream * stm)
|
||||
{
|
||||
int r = audiounit_uninstall_system_changed_callback(stm);
|
||||
if (r != CUBEB_OK) {
|
||||
LOG("(%p) Could not uninstall the device changed callback", stm);
|
||||
}
|
||||
r = audiounit_uninstall_device_changed_callback(stm);
|
||||
if (r != CUBEB_OK) {
|
||||
LOG("(%p) Could not uninstall all device change listeners", stm);
|
||||
}
|
||||
|
||||
if (!stm->shutdown.load()){
|
||||
auto_lock context_lock(stm->context->mutex);
|
||||
audiounit_stream_stop_internal(stm);
|
||||
|
|
@ -3605,6 +3621,7 @@ cubeb_ops const audiounit_ops = {
|
|||
/*.stream_reset_default_device =*/ nullptr,
|
||||
/*.stream_get_position =*/ audiounit_stream_get_position,
|
||||
/*.stream_get_latency =*/ audiounit_stream_get_latency,
|
||||
/*.stream_get_input_latency =*/ NULL,
|
||||
/*.stream_set_volume =*/ audiounit_stream_set_volume,
|
||||
/*.stream_get_current_device =*/ audiounit_stream_get_current_device,
|
||||
/*.stream_device_destroy =*/ audiounit_stream_device_destroy,
|
||||
|
|
|
|||
|
|
@ -50,6 +50,9 @@
|
|||
#define IMPORT_FUNC(x) static decltype(x) * api_##x;
|
||||
JACK_API_VISIT(IMPORT_FUNC);
|
||||
|
||||
#define JACK_DEFAULT_IN "JACK capture"
|
||||
#define JACK_DEFAULT_OUT "JACK playback"
|
||||
|
||||
static const int MAX_STREAMS = 16;
|
||||
static const int MAX_CHANNELS = 8;
|
||||
static const int FIFO_SIZE = 4096 * sizeof(float);
|
||||
|
|
@ -129,6 +132,7 @@ static struct cubeb_ops const cbjack_ops = {
|
|||
.stream_reset_default_device = NULL,
|
||||
.stream_get_position = cbjack_stream_get_position,
|
||||
.stream_get_latency = cbjack_get_latency,
|
||||
.stream_get_input_latency = NULL,
|
||||
.stream_set_volume = cbjack_stream_set_volume,
|
||||
.stream_get_current_device = cbjack_stream_get_current_device,
|
||||
.stream_device_destroy = cbjack_stream_device_destroy,
|
||||
|
|
@ -211,6 +215,9 @@ load_jack_lib(cubeb * context)
|
|||
# endif
|
||||
#else
|
||||
context->libjack = dlopen("libjack.so.0", RTLD_LAZY);
|
||||
if (!context->libjack) {
|
||||
context->libjack = dlopen("libjack.so", RTLD_LAZY);
|
||||
}
|
||||
#endif
|
||||
if (!context->libjack) {
|
||||
return CUBEB_ERROR;
|
||||
|
|
@ -740,8 +747,10 @@ cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_
|
|||
return CUBEB_ERROR_INVALID_FORMAT;
|
||||
}
|
||||
|
||||
if (input_device || output_device)
|
||||
if ((input_device && input_device != JACK_DEFAULT_IN) ||
|
||||
(output_device && output_device != JACK_DEFAULT_OUT)) {
|
||||
return CUBEB_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
// Loopback is unsupported
|
||||
if ((input_stream_params && (input_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK)) ||
|
||||
|
|
@ -964,8 +973,8 @@ cbjack_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const devic
|
|||
if (*device == NULL)
|
||||
return CUBEB_ERROR;
|
||||
|
||||
const char * j_in = "JACK capture";
|
||||
const char * j_out = "JACK playback";
|
||||
const char * j_in = JACK_DEFAULT_IN;
|
||||
const char * j_out = JACK_DEFAULT_OUT;
|
||||
const char * empty = "";
|
||||
|
||||
if (stm->devs == DUPLEX) {
|
||||
|
|
@ -994,9 +1003,6 @@ cbjack_stream_device_destroy(cubeb_stream * /*stream*/,
|
|||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
#define JACK_DEFAULT_IN "JACK capture"
|
||||
#define JACK_DEFAULT_OUT "JACK playback"
|
||||
|
||||
static int
|
||||
cbjack_enumerate_devices(cubeb * context, cubeb_device_type type,
|
||||
cubeb_device_collection * collection)
|
||||
|
|
|
|||
|
|
@ -361,6 +361,7 @@ static struct cubeb_ops const kai_ops = {
|
|||
/*.stream_reset_default_device =*/ NULL,
|
||||
/*.stream_get_position =*/ kai_stream_get_position,
|
||||
/*.stream_get_latency = */ kai_stream_get_latency,
|
||||
/*.stream_get_input_latency = */ NULL,
|
||||
/*.stream_set_volume =*/ kai_stream_set_volume,
|
||||
/*.stream_get_current_device =*/ NULL,
|
||||
/*.stream_device_destroy =*/ NULL,
|
||||
|
|
|
|||
|
|
@ -16,8 +16,15 @@ extern "C" {
|
|||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define PRINTF_FORMAT(fmt, args) __attribute__((format(printf, fmt, args)))
|
||||
#if defined(__FILE_NAME__)
|
||||
#define __FILENAME__ __FILE_NAME__
|
||||
#else
|
||||
#define __FILENAME__ (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : __FILE__)
|
||||
#endif
|
||||
#else
|
||||
#define PRINTF_FORMAT(fmt, args)
|
||||
#include <string.h>
|
||||
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
|
||||
#endif
|
||||
|
||||
extern cubeb_log_level g_cubeb_log_level;
|
||||
|
|
@ -32,10 +39,10 @@ void cubeb_async_log_reset_threads();
|
|||
#define LOGV(msg, ...) LOG_INTERNAL(CUBEB_LOG_VERBOSE, msg, ##__VA_ARGS__)
|
||||
#define LOG(msg, ...) LOG_INTERNAL(CUBEB_LOG_NORMAL, msg, ##__VA_ARGS__)
|
||||
|
||||
#define LOG_INTERNAL(level, fmt, ...) do { \
|
||||
if (g_cubeb_log_callback && level <= g_cubeb_log_level) { \
|
||||
g_cubeb_log_callback("%s:%d: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__); \
|
||||
} \
|
||||
#define LOG_INTERNAL(level, fmt, ...) do { \
|
||||
if (g_cubeb_log_callback && level <= g_cubeb_log_level) { \
|
||||
g_cubeb_log_callback("%s:%d: " fmt "\n", __FILENAME__, __LINE__, ##__VA_ARGS__); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
/* Asynchronous verbose logging, to log in real-time callbacks. */
|
||||
|
|
|
|||
|
|
@ -752,7 +752,7 @@ opensl_init(cubeb ** context, char const * context_name)
|
|||
}
|
||||
|
||||
ctx->p_output_latency_function = cubeb_output_latency_load_method(android_version);
|
||||
if (!ctx->p_output_latency_function) {
|
||||
if (!cubeb_output_latency_method_is_loaded(ctx->p_output_latency_function)) {
|
||||
LOG("Warning: output latency is not available, cubeb_stream_get_position() is not supported");
|
||||
}
|
||||
|
||||
|
|
@ -794,6 +794,53 @@ opensl_destroy(cubeb * ctx)
|
|||
|
||||
static void opensl_stream_destroy(cubeb_stream * stm);
|
||||
|
||||
#if defined(__ANDROID__) && (__ANDROID_API__ >= ANDROID_VERSION_LOLLIPOP)
|
||||
static int
|
||||
opensl_set_format_ext(SLAndroidDataFormat_PCM_EX * format, cubeb_stream_params * params)
|
||||
{
|
||||
assert(format);
|
||||
assert(params);
|
||||
|
||||
format->formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
|
||||
format->numChannels = params->channels;
|
||||
// sampleRate is in milliHertz
|
||||
format->sampleRate = params->rate * 1000;
|
||||
format->channelMask = params->channels == 1 ?
|
||||
SL_SPEAKER_FRONT_CENTER :
|
||||
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
|
||||
|
||||
switch (params->format) {
|
||||
case CUBEB_SAMPLE_S16LE:
|
||||
format->bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
|
||||
format->containerSize = SL_PCMSAMPLEFORMAT_FIXED_16;
|
||||
format->representation = SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT;
|
||||
format->endianness = SL_BYTEORDER_LITTLEENDIAN;
|
||||
break;
|
||||
case CUBEB_SAMPLE_S16BE:
|
||||
format->bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
|
||||
format->containerSize = SL_PCMSAMPLEFORMAT_FIXED_16;
|
||||
format->representation = SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT;
|
||||
format->endianness = SL_BYTEORDER_BIGENDIAN;
|
||||
break;
|
||||
case CUBEB_SAMPLE_FLOAT32LE:
|
||||
format->bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_32;
|
||||
format->containerSize = SL_PCMSAMPLEFORMAT_FIXED_32;
|
||||
format->representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
|
||||
format->endianness = SL_BYTEORDER_LITTLEENDIAN;
|
||||
break;
|
||||
case CUBEB_SAMPLE_FLOAT32BE:
|
||||
format->bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_32;
|
||||
format->containerSize = SL_PCMSAMPLEFORMAT_FIXED_32;
|
||||
format->representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
|
||||
format->endianness = SL_BYTEORDER_BIGENDIAN;
|
||||
break;
|
||||
default:
|
||||
return CUBEB_ERROR_INVALID_FORMAT;
|
||||
}
|
||||
return CUBEB_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
opensl_set_format(SLDataFormat_PCM * format, cubeb_stream_params * params)
|
||||
{
|
||||
|
|
@ -1020,15 +1067,36 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
|
|||
assert(params);
|
||||
|
||||
stm->user_output_rate = params->rate;
|
||||
stm->framesize = params->channels * sizeof(int16_t);
|
||||
if(params->format == CUBEB_SAMPLE_S16NE || params->format == CUBEB_SAMPLE_S16BE) {
|
||||
stm->framesize = params->channels * sizeof(int16_t);
|
||||
} else if(params->format == CUBEB_SAMPLE_FLOAT32NE || params->format == CUBEB_SAMPLE_FLOAT32BE) {
|
||||
stm->framesize = params->channels * sizeof(float);
|
||||
}
|
||||
stm->lastPosition = -1;
|
||||
stm->lastPositionTimeStamp = 0;
|
||||
stm->lastCompensativePosition = -1;
|
||||
|
||||
SLDataFormat_PCM format;
|
||||
int r = opensl_set_format(&format, params);
|
||||
if (r != CUBEB_OK) {
|
||||
return CUBEB_ERROR_INVALID_FORMAT;
|
||||
void* format = NULL;
|
||||
SLuint32* format_sample_rate = NULL;
|
||||
|
||||
#if defined(__ANDROID__) && (__ANDROID_API__ >= ANDROID_VERSION_LOLLIPOP)
|
||||
SLAndroidDataFormat_PCM_EX pcm_ext_format;
|
||||
if (get_android_version() >= ANDROID_VERSION_LOLLIPOP) {
|
||||
if (opensl_set_format_ext(&pcm_ext_format, params) != CUBEB_OK) {
|
||||
return CUBEB_ERROR_INVALID_FORMAT;
|
||||
}
|
||||
format = &pcm_ext_format;
|
||||
format_sample_rate = &pcm_ext_format.sampleRate;
|
||||
}
|
||||
#endif
|
||||
|
||||
SLDataFormat_PCM pcm_format;
|
||||
if(!format) {
|
||||
if(opensl_set_format(&pcm_format, params) != CUBEB_OK) {
|
||||
return CUBEB_ERROR_INVALID_FORMAT;
|
||||
}
|
||||
format = &pcm_format;
|
||||
format_sample_rate = &pcm_format.samplesPerSec;
|
||||
}
|
||||
|
||||
SLDataLocator_BufferQueue loc_bufq;
|
||||
|
|
@ -1036,7 +1104,7 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
|
|||
loc_bufq.numBuffers = NBUFS;
|
||||
SLDataSource source;
|
||||
source.pLocator = &loc_bufq;
|
||||
source.pFormat = &format;
|
||||
source.pFormat = format;
|
||||
|
||||
SLDataLocator_OutputMix loc_outmix;
|
||||
loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
|
||||
|
|
@ -1072,7 +1140,7 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
|
|||
if (res == SL_RESULT_CONTENT_UNSUPPORTED &&
|
||||
preferred_sampling_rate != DEFAULT_SAMPLE_RATE) {
|
||||
preferred_sampling_rate = DEFAULT_SAMPLE_RATE;
|
||||
format.samplesPerSec = preferred_sampling_rate * 1000;
|
||||
*format_sample_rate = preferred_sampling_rate * 1000;
|
||||
res = (*stm->context->eng)->CreateAudioPlayer(stm->context->eng,
|
||||
&stm->playerObj,
|
||||
&source,
|
||||
|
|
@ -1625,6 +1693,18 @@ opensl_stream_get_position(cubeb_stream * stm, uint64_t * position)
|
|||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
opensl_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
|
||||
{
|
||||
assert(stm);
|
||||
assert(latency);
|
||||
|
||||
uint32_t stream_latency_frames =
|
||||
stm->user_output_rate * (stm->output_latency_ms / 1000);
|
||||
|
||||
return stream_latency_frames + cubeb_resampler_latency(stm->resampler);
|
||||
}
|
||||
|
||||
int
|
||||
opensl_stream_set_volume(cubeb_stream * stm, float volume)
|
||||
{
|
||||
|
|
@ -1671,7 +1751,8 @@ static struct cubeb_ops const opensl_ops = {
|
|||
.stream_stop = opensl_stream_stop,
|
||||
.stream_reset_default_device = NULL,
|
||||
.stream_get_position = opensl_stream_get_position,
|
||||
.stream_get_latency = NULL,
|
||||
.stream_get_latency = opensl_stream_get_latency,
|
||||
.stream_get_input_latency = NULL,
|
||||
.stream_set_volume = opensl_stream_set_volume,
|
||||
.stream_get_current_device = NULL,
|
||||
.stream_device_destroy = NULL,
|
||||
|
|
|
|||
|
|
@ -576,6 +576,11 @@ layout_to_channel_map(cubeb_channel_layout layout, pa_channel_map * cm)
|
|||
unsigned int channels_from_layout = cubeb_channel_layout_nb_channels(layout);
|
||||
assert(channels_from_layout <= UINT8_MAX);
|
||||
cm->channels = (uint8_t) channels_from_layout;
|
||||
|
||||
// Special case single channel center mapping as mono.
|
||||
if (cm->channels == 1 && cm->map[0] == PA_CHANNEL_POSITION_FRONT_CENTER) {
|
||||
cm->map[0] = PA_CHANNEL_POSITION_MONO;
|
||||
}
|
||||
}
|
||||
|
||||
static void pulse_context_destroy(cubeb * ctx);
|
||||
|
|
@ -615,6 +620,9 @@ pulse_context_init(cubeb * ctx)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int pulse_subscribe_notifications(cubeb * context,
|
||||
pa_subscription_mask_t mask);
|
||||
|
||||
/*static*/ int
|
||||
pulse_init(cubeb ** context, char const * context_name)
|
||||
{
|
||||
|
|
@ -627,7 +635,10 @@ pulse_init(cubeb ** context, char const * context_name)
|
|||
#ifndef DISABLE_LIBPULSE_DLOPEN
|
||||
libpulse = dlopen("libpulse.so.0", RTLD_LAZY);
|
||||
if (!libpulse) {
|
||||
return CUBEB_ERROR;
|
||||
libpulse = dlopen("libpulse.so", RTLD_LAZY);
|
||||
if (!libpulse) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
#define LOAD(x) { \
|
||||
|
|
@ -679,6 +690,9 @@ pulse_init(cubeb ** context, char const * context_name)
|
|||
}
|
||||
WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop);
|
||||
|
||||
/* Update `default_sink_info` when the default device changes. */
|
||||
pulse_subscribe_notifications(ctx, PA_SUBSCRIPTION_MASK_SERVER);
|
||||
|
||||
*context = ctx;
|
||||
|
||||
return CUBEB_OK;
|
||||
|
|
@ -1169,21 +1183,6 @@ pulse_stream_set_volume(cubeb_stream * stm, float volume)
|
|||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
struct sink_input_info_result {
|
||||
pa_cvolume * cvol;
|
||||
pa_threaded_mainloop * mainloop;
|
||||
};
|
||||
|
||||
static void
|
||||
sink_input_info_cb(pa_context * c, pa_sink_input_info const * i, int eol, void * u)
|
||||
{
|
||||
struct sink_input_info_result * r = u;
|
||||
if (!eol) {
|
||||
*r->cvol = i->volume;
|
||||
}
|
||||
WRAP(pa_threaded_mainloop_signal)(r->mainloop, 0);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
char * default_sink_name;
|
||||
char * default_source_name;
|
||||
|
|
@ -1482,6 +1481,12 @@ pulse_subscribe_callback(pa_context * ctx,
|
|||
cubeb * context = userdata;
|
||||
|
||||
switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
|
||||
case PA_SUBSCRIPTION_EVENT_SERVER:
|
||||
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) {
|
||||
LOG("Server changed %d", index);
|
||||
WRAP(pa_context_get_server_info)(context->context, server_info_callback, context);
|
||||
}
|
||||
break;
|
||||
case PA_SUBSCRIPTION_EVENT_SOURCE:
|
||||
case PA_SUBSCRIPTION_EVENT_SINK:
|
||||
|
||||
|
|
@ -1525,38 +1530,10 @@ subscribe_success(pa_context *c, int success, void *userdata)
|
|||
}
|
||||
|
||||
static int
|
||||
pulse_register_device_collection_changed(cubeb * context,
|
||||
cubeb_device_type devtype,
|
||||
cubeb_device_collection_changed_callback collection_changed_callback,
|
||||
void * user_ptr)
|
||||
{
|
||||
if (devtype & CUBEB_DEVICE_TYPE_INPUT) {
|
||||
context->input_collection_changed_callback = collection_changed_callback;
|
||||
context->input_collection_changed_user_ptr = user_ptr;
|
||||
}
|
||||
if (devtype & CUBEB_DEVICE_TYPE_OUTPUT) {
|
||||
context->output_collection_changed_callback = collection_changed_callback;
|
||||
context->output_collection_changed_user_ptr = user_ptr;
|
||||
}
|
||||
|
||||
pulse_subscribe_notifications(cubeb * context, pa_subscription_mask_t mask) {
|
||||
WRAP(pa_threaded_mainloop_lock)(context->mainloop);
|
||||
|
||||
pa_subscription_mask_t mask = PA_SUBSCRIPTION_MASK_NULL;
|
||||
if (context->input_collection_changed_callback) {
|
||||
mask |= PA_SUBSCRIPTION_MASK_SOURCE;
|
||||
}
|
||||
if (context->output_collection_changed_callback) {
|
||||
mask |= PA_SUBSCRIPTION_MASK_SINK;
|
||||
}
|
||||
|
||||
if (collection_changed_callback == NULL) {
|
||||
// Unregister subscription.
|
||||
if (mask == PA_SUBSCRIPTION_MASK_NULL) {
|
||||
WRAP(pa_context_set_subscribe_callback)(context->context, NULL, NULL);
|
||||
}
|
||||
} else {
|
||||
WRAP(pa_context_set_subscribe_callback)(context->context, pulse_subscribe_callback, context);
|
||||
}
|
||||
WRAP(pa_context_set_subscribe_callback)(context->context, pulse_subscribe_callback, context);
|
||||
|
||||
pa_operation * o;
|
||||
o = WRAP(pa_context_subscribe)(context->context, mask, subscribe_success, context);
|
||||
|
|
@ -1573,6 +1550,37 @@ pulse_register_device_collection_changed(cubeb * context,
|
|||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
pulse_register_device_collection_changed(cubeb * context,
|
||||
cubeb_device_type devtype,
|
||||
cubeb_device_collection_changed_callback collection_changed_callback,
|
||||
void * user_ptr)
|
||||
{
|
||||
if (devtype & CUBEB_DEVICE_TYPE_INPUT) {
|
||||
context->input_collection_changed_callback = collection_changed_callback;
|
||||
context->input_collection_changed_user_ptr = user_ptr;
|
||||
}
|
||||
if (devtype & CUBEB_DEVICE_TYPE_OUTPUT) {
|
||||
context->output_collection_changed_callback = collection_changed_callback;
|
||||
context->output_collection_changed_user_ptr = user_ptr;
|
||||
}
|
||||
|
||||
pa_subscription_mask_t mask = PA_SUBSCRIPTION_MASK_NULL;
|
||||
if (context->input_collection_changed_callback) {
|
||||
/* Input added or removed */
|
||||
mask |= PA_SUBSCRIPTION_MASK_SOURCE;
|
||||
}
|
||||
if (context->output_collection_changed_callback) {
|
||||
/* Output added or removed */
|
||||
mask |= PA_SUBSCRIPTION_MASK_SINK;
|
||||
}
|
||||
/* Default device changed, this is always registered in order to update the
|
||||
* `default_sink_info` when the default device changes. */
|
||||
mask |= PA_SUBSCRIPTION_MASK_SERVER;
|
||||
|
||||
return pulse_subscribe_notifications(context, mask);
|
||||
}
|
||||
|
||||
static struct cubeb_ops const pulse_ops = {
|
||||
.init = pulse_init,
|
||||
.get_backend_id = pulse_get_backend_id,
|
||||
|
|
@ -1589,6 +1597,7 @@ static struct cubeb_ops const pulse_ops = {
|
|||
.stream_reset_default_device = NULL,
|
||||
.stream_get_position = pulse_stream_get_position,
|
||||
.stream_get_latency = pulse_stream_get_latency,
|
||||
.stream_get_input_latency = NULL,
|
||||
.stream_set_volume = pulse_stream_set_volume,
|
||||
.stream_get_current_device = pulse_stream_get_current_device,
|
||||
.stream_device_destroy = pulse_stream_device_destroy,
|
||||
|
|
|
|||
|
|
@ -61,31 +61,69 @@ long passthrough_resampler<T>::fill(void * input_buffer, long * input_frames_cou
|
|||
if (input_buffer) {
|
||||
assert(input_frames_count);
|
||||
}
|
||||
assert((input_buffer && output_buffer &&
|
||||
*input_frames_count + static_cast<int>(samples_to_frames(internal_input_buffer.length())) >= output_frames) ||
|
||||
assert((input_buffer && output_buffer) ||
|
||||
(output_buffer && !input_buffer && (!input_frames_count || *input_frames_count == 0)) ||
|
||||
(input_buffer && !output_buffer && output_frames == 0));
|
||||
|
||||
if (input_buffer) {
|
||||
if (!output_buffer) {
|
||||
// When we have no pending input data and exactly as much input
|
||||
// as output data, we don't need to copy it into the internal buffer
|
||||
// and can directly forward it to the callback.
|
||||
void * in_buf = input_buffer;
|
||||
unsigned long pop_input_count = 0u;
|
||||
if (input_buffer && !output_buffer) {
|
||||
output_frames = *input_frames_count;
|
||||
} else if(input_buffer) {
|
||||
if (internal_input_buffer.length() != 0 ||
|
||||
*input_frames_count < output_frames) {
|
||||
// If we have pending input data left and have to first append the input
|
||||
// so we can pass it as one pointer to the callback. Or this is a glitch.
|
||||
// It can happen when system's performance is poor. Audible silence is
|
||||
// being pushed at the end of the short input buffer. An improvement for
|
||||
// the future is to resample to the output number of frames, when that happens.
|
||||
internal_input_buffer.push(static_cast<T*>(input_buffer),
|
||||
frames_to_samples(*input_frames_count));
|
||||
if (internal_input_buffer.length() < frames_to_samples(output_frames)) {
|
||||
// This is unxpected but it can happen when a glitch occurs. Fill the
|
||||
// buffer with silence. First keep the actual number of input samples
|
||||
// used without the silence.
|
||||
pop_input_count = internal_input_buffer.length();
|
||||
internal_input_buffer.push_silence(
|
||||
frames_to_samples(output_frames) - internal_input_buffer.length());
|
||||
} else {
|
||||
pop_input_count = frames_to_samples(output_frames);
|
||||
}
|
||||
in_buf = internal_input_buffer.data();
|
||||
} else if(*input_frames_count > output_frames) {
|
||||
// In this case we have more input that we need output and
|
||||
// fill the overflowing input into internal_input_buffer
|
||||
// Since we have no other pending data, we can nonetheless
|
||||
// pass the current input data directly to the callback
|
||||
assert(pop_input_count == 0);
|
||||
unsigned long samples_off = frames_to_samples(output_frames);
|
||||
internal_input_buffer.push(static_cast<T*>(input_buffer) + samples_off,
|
||||
frames_to_samples(*input_frames_count - output_frames));
|
||||
}
|
||||
internal_input_buffer.push(static_cast<T*>(input_buffer),
|
||||
frames_to_samples(*input_frames_count));
|
||||
}
|
||||
|
||||
long rv = data_callback(stream, user_ptr, internal_input_buffer.data(),
|
||||
output_buffer, output_frames);
|
||||
long rv = data_callback(stream, user_ptr, in_buf, output_buffer, output_frames);
|
||||
|
||||
if (input_buffer) {
|
||||
internal_input_buffer.pop(nullptr, frames_to_samples(output_frames));
|
||||
*input_frames_count = output_frames;
|
||||
if (pop_input_count) {
|
||||
internal_input_buffer.pop(nullptr, pop_input_count);
|
||||
*input_frames_count = samples_to_frames(pop_input_count);
|
||||
} else {
|
||||
*input_frames_count = output_frames;
|
||||
}
|
||||
drop_audio_if_needed();
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Explicit instantiation of template class.
|
||||
template class passthrough_resampler<float>;
|
||||
template class passthrough_resampler<short>;
|
||||
|
||||
template<typename T, typename InputProcessor, typename OutputProcessor>
|
||||
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
|
||||
::cubeb_resampler_speex(InputProcessor * input_processor,
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <dlfcn.h>
|
||||
#include <assert.h>
|
||||
#include "cubeb/cubeb.h"
|
||||
#include "cubeb-internal.h"
|
||||
|
|
@ -22,10 +23,36 @@
|
|||
#define DPR(...) do {} while(0)
|
||||
#endif
|
||||
|
||||
#ifdef DISABLE_LIBSNDIO_DLOPEN
|
||||
#define WRAP(x) x
|
||||
#else
|
||||
#define WRAP(x) cubeb_##x
|
||||
#define LIBSNDIO_API_VISIT(X) \
|
||||
X(sio_close) \
|
||||
X(sio_eof) \
|
||||
X(sio_getpar) \
|
||||
X(sio_initpar) \
|
||||
X(sio_nfds) \
|
||||
X(sio_onmove) \
|
||||
X(sio_open) \
|
||||
X(sio_pollfd) \
|
||||
X(sio_read) \
|
||||
X(sio_revents) \
|
||||
X(sio_setpar) \
|
||||
X(sio_start) \
|
||||
X(sio_stop) \
|
||||
X(sio_write) \
|
||||
|
||||
#define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x;
|
||||
LIBSNDIO_API_VISIT(MAKE_TYPEDEF);
|
||||
#undef MAKE_TYPEDEF
|
||||
#endif
|
||||
|
||||
static struct cubeb_ops const sndio_ops;
|
||||
|
||||
struct cubeb {
|
||||
struct cubeb_ops const * ops;
|
||||
void * libsndio;
|
||||
};
|
||||
|
||||
struct cubeb_stream {
|
||||
|
|
@ -98,6 +125,23 @@ s16_to_float(void *ptr, long nsamp)
|
|||
*(--dst) = (1. / 32768) * *(--src);
|
||||
}
|
||||
|
||||
static const char *
|
||||
sndio_get_device()
|
||||
{
|
||||
#ifdef __linux__
|
||||
/*
|
||||
* On other platforms default to sndio devices,
|
||||
* so cubebs other backends can be used instead.
|
||||
*/
|
||||
const char *dev = getenv("AUDIODEVICE");
|
||||
if (dev == NULL || *dev == '\0')
|
||||
return "snd/0";
|
||||
return dev;
|
||||
#else
|
||||
return SIO_DEVANY;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
sndio_onmove(void *arg, int delta)
|
||||
{
|
||||
|
|
@ -109,18 +153,23 @@ sndio_onmove(void *arg, int delta)
|
|||
static void *
|
||||
sndio_mainloop(void *arg)
|
||||
{
|
||||
#define MAXFDS 8
|
||||
struct pollfd pfds[MAXFDS];
|
||||
struct pollfd *pfds;
|
||||
cubeb_stream *s = arg;
|
||||
int n, eof = 0, prime, nfds, events, revents, state = CUBEB_STATE_STARTED;
|
||||
size_t pstart = 0, pend = 0, rstart = 0, rend = 0;
|
||||
long nfr;
|
||||
|
||||
nfds = WRAP(sio_nfds)(s->hdl);
|
||||
pfds = calloc(nfds, sizeof (struct pollfd));
|
||||
if (pfds == NULL)
|
||||
return NULL;
|
||||
|
||||
DPR("sndio_mainloop()\n");
|
||||
s->state_cb(s, s->arg, CUBEB_STATE_STARTED);
|
||||
pthread_mutex_lock(&s->mtx);
|
||||
if (!sio_start(s->hdl)) {
|
||||
if (!WRAP(sio_start)(s->hdl)) {
|
||||
pthread_mutex_unlock(&s->mtx);
|
||||
free(pfds);
|
||||
return NULL;
|
||||
}
|
||||
DPR("sndio_mainloop(), started\n");
|
||||
|
|
@ -203,7 +252,7 @@ sndio_mainloop(void *arg)
|
|||
events |= POLLIN;
|
||||
if ((s->mode & SIO_PLAY) && pstart < pend)
|
||||
events |= POLLOUT;
|
||||
nfds = sio_pollfd(s->hdl, pfds, events);
|
||||
nfds = WRAP(sio_pollfd)(s->hdl, pfds, events);
|
||||
|
||||
if (nfds > 0) {
|
||||
pthread_mutex_unlock(&s->mtx);
|
||||
|
|
@ -213,7 +262,7 @@ sndio_mainloop(void *arg)
|
|||
continue;
|
||||
}
|
||||
|
||||
revents = sio_revents(s->hdl, pfds);
|
||||
revents = WRAP(sio_revents)(s->hdl, pfds);
|
||||
|
||||
if (revents & POLLHUP) {
|
||||
state = CUBEB_STATE_ERROR;
|
||||
|
|
@ -221,8 +270,8 @@ sndio_mainloop(void *arg)
|
|||
}
|
||||
|
||||
if (revents & POLLOUT) {
|
||||
n = sio_write(s->hdl, s->pbuf + pstart, pend - pstart);
|
||||
if (n == 0 && sio_eof(s->hdl)) {
|
||||
n = WRAP(sio_write)(s->hdl, s->pbuf + pstart, pend - pstart);
|
||||
if (n == 0 && WRAP(sio_eof)(s->hdl)) {
|
||||
DPR("sndio_mainloop() werr\n");
|
||||
state = CUBEB_STATE_ERROR;
|
||||
break;
|
||||
|
|
@ -231,8 +280,8 @@ sndio_mainloop(void *arg)
|
|||
}
|
||||
|
||||
if (revents & POLLIN) {
|
||||
n = sio_read(s->hdl, s->rbuf + rstart, rend - rstart);
|
||||
if (n == 0 && sio_eof(s->hdl)) {
|
||||
n = WRAP(sio_read)(s->hdl, s->rbuf + rstart, rend - rstart);
|
||||
if (n == 0 && WRAP(sio_eof)(s->hdl)) {
|
||||
DPR("sndio_mainloop() rerr\n");
|
||||
state = CUBEB_STATE_ERROR;
|
||||
break;
|
||||
|
|
@ -244,18 +293,57 @@ sndio_mainloop(void *arg)
|
|||
if (prime > 0 && (s->mode & SIO_REC))
|
||||
rstart = rend;
|
||||
}
|
||||
sio_stop(s->hdl);
|
||||
WRAP(sio_stop)(s->hdl);
|
||||
s->hwpos = s->swpos;
|
||||
pthread_mutex_unlock(&s->mtx);
|
||||
s->state_cb(s, s->arg, state);
|
||||
free(pfds);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*static*/ int
|
||||
sndio_init(cubeb **context, char const *context_name)
|
||||
{
|
||||
void * libsndio = NULL;
|
||||
struct sio_hdl *hdl;
|
||||
|
||||
assert(context);
|
||||
|
||||
#ifndef DISABLE_LIBSNDIO_DLOPEN
|
||||
libsndio = dlopen("libsndio.so.7.0", RTLD_LAZY);
|
||||
if (!libsndio) {
|
||||
libsndio = dlopen("libsndio.so", RTLD_LAZY);
|
||||
if (!libsndio) {
|
||||
DPR("sndio_init(%s) failed dlopen(libsndio.so)\n", context_name);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
#define LOAD(x) { \
|
||||
cubeb_##x = dlsym(libsndio, #x); \
|
||||
if (!cubeb_##x) { \
|
||||
DPR("sndio_init(%s) failed dlsym(%s)\n", context_name, #x); \
|
||||
dlclose(libsndio); \
|
||||
return CUBEB_ERROR; \
|
||||
} \
|
||||
}
|
||||
|
||||
LIBSNDIO_API_VISIT(LOAD);
|
||||
#undef LOAD
|
||||
#endif
|
||||
|
||||
/* test if sndio works */
|
||||
hdl = WRAP(sio_open)(sndio_get_device(), SIO_PLAY, 1);
|
||||
if (hdl == NULL) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
WRAP(sio_close)(hdl);
|
||||
|
||||
DPR("sndio_init(%s)\n", context_name);
|
||||
*context = malloc(sizeof(*context));
|
||||
*context = malloc(sizeof(**context));
|
||||
if (*context == NULL)
|
||||
return CUBEB_ERROR;
|
||||
(*context)->libsndio = libsndio;
|
||||
(*context)->ops = &sndio_ops;
|
||||
(void)context_name;
|
||||
return CUBEB_OK;
|
||||
|
|
@ -271,6 +359,8 @@ static void
|
|||
sndio_destroy(cubeb *context)
|
||||
{
|
||||
DPR("sndio_destroy()\n");
|
||||
if (context->libsndio)
|
||||
dlclose(context->libsndio);
|
||||
free(context);
|
||||
}
|
||||
|
||||
|
|
@ -323,12 +413,12 @@ sndio_stream_init(cubeb * context,
|
|||
goto err;
|
||||
}
|
||||
s->context = context;
|
||||
s->hdl = sio_open(NULL, s->mode, 1);
|
||||
s->hdl = WRAP(sio_open)(sndio_get_device(), s->mode, 1);
|
||||
if (s->hdl == NULL) {
|
||||
DPR("sndio_stream_init(), sio_open() failed\n");
|
||||
goto err;
|
||||
}
|
||||
sio_initpar(&wpar);
|
||||
WRAP(sio_initpar)(&wpar);
|
||||
wpar.sig = 1;
|
||||
wpar.bits = 16;
|
||||
switch (format) {
|
||||
|
|
@ -351,7 +441,7 @@ sndio_stream_init(cubeb * context,
|
|||
if (s->mode & SIO_PLAY)
|
||||
wpar.pchan = output_stream_params->channels;
|
||||
wpar.appbufsz = latency_frames;
|
||||
if (!sio_setpar(s->hdl, &wpar) || !sio_getpar(s->hdl, &rpar)) {
|
||||
if (!WRAP(sio_setpar)(s->hdl, &wpar) || !WRAP(sio_getpar)(s->hdl, &rpar)) {
|
||||
DPR("sndio_stream_init(), sio_setpar() failed\n");
|
||||
goto err;
|
||||
}
|
||||
|
|
@ -362,7 +452,7 @@ sndio_stream_init(cubeb * context,
|
|||
DPR("sndio_stream_init() unsupported params\n");
|
||||
goto err;
|
||||
}
|
||||
sio_onmove(s->hdl, sndio_onmove, s);
|
||||
WRAP(sio_onmove)(s->hdl, sndio_onmove, s);
|
||||
s->active = 0;
|
||||
s->nfr = rpar.round;
|
||||
s->rbpf = rpar.bps * rpar.rchan;
|
||||
|
|
@ -400,7 +490,7 @@ sndio_stream_init(cubeb * context,
|
|||
return CUBEB_OK;
|
||||
err:
|
||||
if (s->hdl)
|
||||
sio_close(s->hdl);
|
||||
WRAP(sio_close)(s->hdl);
|
||||
if (s->pbuf)
|
||||
free(s->pbuf);
|
||||
if (s->rbuf)
|
||||
|
|
@ -446,7 +536,7 @@ static void
|
|||
sndio_stream_destroy(cubeb_stream *s)
|
||||
{
|
||||
DPR("sndio_stream_destroy()\n");
|
||||
sio_close(s->hdl);
|
||||
WRAP(sio_close)(s->hdl);
|
||||
if (s->mode & SIO_PLAY)
|
||||
free(s->pbuf);
|
||||
if (s->mode & SIO_REC)
|
||||
|
|
|
|||
260
third_party/rust/cubeb-sys/libcubeb/src/cubeb_sun.c
vendored
260
third_party/rust/cubeb-sys/libcubeb/src/cubeb_sun.c
vendored
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright © 2019 Nia Alarie
|
||||
* Copyright © 2019-2020 Nia Alarie <nia@NetBSD.org>
|
||||
*
|
||||
* This program is made available under an ISC-style license. See the
|
||||
* accompanying file LICENSE for details.
|
||||
|
|
@ -9,19 +9,14 @@
|
|||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
#include "cubeb/cubeb.h"
|
||||
#include "cubeb-internal.h"
|
||||
|
||||
#define BYTES_TO_FRAMES(bytes, channels) \
|
||||
(bytes / (channels * sizeof(int16_t)))
|
||||
|
||||
#define FRAMES_TO_BYTES(frames, channels) \
|
||||
(frames * (channels * sizeof(int16_t)))
|
||||
|
||||
/* Default to 4 + 1 for the default device. */
|
||||
#ifndef SUN_DEVICE_COUNT
|
||||
#define SUN_DEVICE_COUNT (5)
|
||||
|
|
@ -41,10 +36,6 @@
|
|||
#define SUN_DEFAULT_DEVICE "/dev/audio"
|
||||
#endif
|
||||
|
||||
#ifndef SUN_POLL_TIMEOUT
|
||||
#define SUN_POLL_TIMEOUT (1000)
|
||||
#endif
|
||||
|
||||
#ifndef SUN_BUFFER_FRAMES
|
||||
#define SUN_BUFFER_FRAMES (32)
|
||||
#endif
|
||||
|
|
@ -75,26 +66,26 @@ struct cubeb {
|
|||
struct cubeb_ops const * ops;
|
||||
};
|
||||
|
||||
struct sun_stream {
|
||||
char name[32];
|
||||
int fd;
|
||||
void * buf;
|
||||
struct audio_info info;
|
||||
unsigned frame_size; /* precision in bytes * channels */
|
||||
bool floating;
|
||||
};
|
||||
|
||||
struct cubeb_stream {
|
||||
struct cubeb * context;
|
||||
void * user_ptr;
|
||||
pthread_t thread;
|
||||
pthread_mutex_t mutex; /* protects running, volume, frames_written */
|
||||
int floating;
|
||||
int running;
|
||||
int play_fd;
|
||||
int record_fd;
|
||||
bool running;
|
||||
float volume;
|
||||
struct audio_info p_info; /* info for the play fd */
|
||||
struct audio_info r_info; /* info for the record fd */
|
||||
struct sun_stream play;
|
||||
struct sun_stream record;
|
||||
cubeb_data_callback data_cb;
|
||||
cubeb_state_callback state_cb;
|
||||
int16_t * play_buf;
|
||||
int16_t * record_buf;
|
||||
float * f_play_buf;
|
||||
float * f_record_buf;
|
||||
char input_name[32];
|
||||
char output_name[32];
|
||||
uint64_t frames_written;
|
||||
uint64_t blocks_written;
|
||||
};
|
||||
|
|
@ -312,18 +303,19 @@ sun_copy_params(int fd, cubeb_stream * stream, cubeb_stream_params * params,
|
|||
{
|
||||
prinfo->channels = params->channels;
|
||||
prinfo->sample_rate = params->rate;
|
||||
prinfo->precision = 16;
|
||||
#ifdef AUDIO_ENCODING_SLINEAR_LE
|
||||
switch (params->format) {
|
||||
case CUBEB_SAMPLE_S16LE:
|
||||
prinfo->encoding = AUDIO_ENCODING_SLINEAR_LE;
|
||||
prinfo->precision = 16;
|
||||
break;
|
||||
case CUBEB_SAMPLE_S16BE:
|
||||
prinfo->encoding = AUDIO_ENCODING_SLINEAR_BE;
|
||||
prinfo->precision = 16;
|
||||
break;
|
||||
case CUBEB_SAMPLE_FLOAT32NE:
|
||||
stream->floating = 1;
|
||||
prinfo->encoding = AUDIO_ENCODING_SLINEAR;
|
||||
prinfo->precision = 32;
|
||||
break;
|
||||
default:
|
||||
LOG("Unsupported format");
|
||||
|
|
@ -333,10 +325,11 @@ sun_copy_params(int fd, cubeb_stream * stream, cubeb_stream_params * params,
|
|||
switch (params->format) {
|
||||
case CUBEB_SAMPLE_S16NE:
|
||||
prinfo->encoding = AUDIO_ENCODING_LINEAR;
|
||||
prinfo->precision = 16;
|
||||
break;
|
||||
case CUBEB_SAMPLE_FLOAT32NE:
|
||||
stream->floating = 1;
|
||||
prinfo->encoding = AUDIO_ENCODING_LINEAR;
|
||||
prinfo->precision = 32;
|
||||
break;
|
||||
default:
|
||||
LOG("Unsupported format");
|
||||
|
|
@ -357,7 +350,7 @@ sun_stream_stop(cubeb_stream * s)
|
|||
{
|
||||
pthread_mutex_lock(&s->mutex);
|
||||
if (s->running) {
|
||||
s->running = 0;
|
||||
s->running = false;
|
||||
pthread_mutex_unlock(&s->mutex);
|
||||
pthread_join(s->thread, NULL);
|
||||
} else {
|
||||
|
|
@ -371,53 +364,50 @@ sun_stream_destroy(cubeb_stream * s)
|
|||
{
|
||||
pthread_mutex_destroy(&s->mutex);
|
||||
sun_stream_stop(s);
|
||||
if (s->play_fd != -1) {
|
||||
close(s->play_fd);
|
||||
if (s->play.fd != -1) {
|
||||
close(s->play.fd);
|
||||
}
|
||||
if (s->record_fd != -1) {
|
||||
close(s->record_fd);
|
||||
if (s->record.fd != -1) {
|
||||
close(s->record.fd);
|
||||
}
|
||||
free(s->f_play_buf);
|
||||
free(s->f_record_buf);
|
||||
free(s->play_buf);
|
||||
free(s->record_buf);
|
||||
free(s->play.buf);
|
||||
free(s->record.buf);
|
||||
free(s);
|
||||
}
|
||||
|
||||
static void
|
||||
sun_float_to_linear(float * in, int16_t * out,
|
||||
unsigned channels, long frames, float vol)
|
||||
sun_float_to_linear32(void * buf, unsigned sample_count, float vol)
|
||||
{
|
||||
unsigned i, sample_count = frames * channels;
|
||||
float multiplier = vol * 0x8000;
|
||||
float * in = buf;
|
||||
int32_t * out = buf;
|
||||
int32_t * tail = out + sample_count;
|
||||
|
||||
for (i = 0; i < sample_count; ++i) {
|
||||
int32_t sample = lrintf(in[i] * multiplier);
|
||||
if (sample < -0x8000) {
|
||||
out[i] = -0x8000;
|
||||
} else if (sample > 0x7fff) {
|
||||
out[i] = 0x7fff;
|
||||
} else {
|
||||
out[i] = sample;
|
||||
}
|
||||
while (out < tail) {
|
||||
float f = *(in++) * vol;
|
||||
if (f < -1.0)
|
||||
f = -1.0;
|
||||
else if (f > 1.0)
|
||||
f = 1.0;
|
||||
*(out++) = f * (float)INT32_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sun_linear_to_float(int16_t * in, float * out,
|
||||
unsigned channels, long frames)
|
||||
sun_linear32_to_float(void * buf, unsigned sample_count)
|
||||
{
|
||||
unsigned i, sample_count = frames * channels;
|
||||
int32_t * in = buf;
|
||||
float * out = buf;
|
||||
float * tail = out + sample_count;
|
||||
|
||||
for (i = 0; i < sample_count; ++i) {
|
||||
out[i] = (1.0 / 0x8000) * in[i];
|
||||
while (out < tail) {
|
||||
*(out++) = (1.0 / 0x80000000) * *(in++);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sun_linear_set_vol(int16_t * buf, unsigned channels, long frames, float vol)
|
||||
sun_linear16_set_vol(int16_t * buf, unsigned sample_count, float vol)
|
||||
{
|
||||
unsigned i, sample_count = frames * channels;
|
||||
unsigned i;
|
||||
int32_t multiplier = vol * 0x8000;
|
||||
|
||||
for (i = 0; i < sample_count; ++i) {
|
||||
|
|
@ -445,41 +435,36 @@ sun_io_routine(void * arg)
|
|||
break;
|
||||
}
|
||||
pthread_mutex_unlock(&s->mutex);
|
||||
if (s->floating) {
|
||||
if (s->record_fd != -1) {
|
||||
sun_linear_to_float(s->record_buf, s->f_record_buf,
|
||||
s->r_info.record.channels, SUN_BUFFER_FRAMES);
|
||||
}
|
||||
to_write = s->data_cb(s, s->user_ptr,
|
||||
s->f_record_buf, s->f_play_buf, SUN_BUFFER_FRAMES);
|
||||
if (to_write == CUBEB_ERROR) {
|
||||
state = CUBEB_STATE_ERROR;
|
||||
break;
|
||||
}
|
||||
if (s->play_fd != -1) {
|
||||
pthread_mutex_lock(&s->mutex);
|
||||
sun_float_to_linear(s->f_play_buf, s->play_buf,
|
||||
s->p_info.play.channels, to_write, s->volume);
|
||||
pthread_mutex_unlock(&s->mutex);
|
||||
}
|
||||
} else {
|
||||
to_write = s->data_cb(s, s->user_ptr,
|
||||
s->record_buf, s->play_buf, SUN_BUFFER_FRAMES);
|
||||
if (to_write == CUBEB_ERROR) {
|
||||
state = CUBEB_STATE_ERROR;
|
||||
break;
|
||||
}
|
||||
if (s->play_fd != -1) {
|
||||
pthread_mutex_lock(&s->mutex);
|
||||
sun_linear_set_vol(s->play_buf, s->p_info.play.channels, to_write, s->volume);
|
||||
pthread_mutex_unlock(&s->mutex);
|
||||
if (s->record.fd != -1 && s->record.floating) {
|
||||
sun_linear32_to_float(s->record.buf,
|
||||
s->record.info.record.channels * SUN_BUFFER_FRAMES);
|
||||
}
|
||||
to_write = s->data_cb(s, s->user_ptr,
|
||||
s->record.buf, s->play.buf, SUN_BUFFER_FRAMES);
|
||||
if (to_write == CUBEB_ERROR) {
|
||||
state = CUBEB_STATE_ERROR;
|
||||
break;
|
||||
}
|
||||
if (s->play.fd != -1) {
|
||||
float vol;
|
||||
|
||||
pthread_mutex_lock(&s->mutex);
|
||||
vol = s->volume;
|
||||
pthread_mutex_unlock(&s->mutex);
|
||||
|
||||
if (s->play.floating) {
|
||||
sun_float_to_linear32(s->play.buf,
|
||||
s->play.info.play.channels * to_write, vol);
|
||||
} else {
|
||||
sun_linear16_set_vol(s->play.buf,
|
||||
s->play.info.play.channels * to_write, vol);
|
||||
}
|
||||
}
|
||||
if (to_write < SUN_BUFFER_FRAMES) {
|
||||
drain = 1;
|
||||
}
|
||||
to_write = s->play_fd != -1 ? to_write : 0;
|
||||
to_read = s->record_fd != -1 ? SUN_BUFFER_FRAMES : 0;
|
||||
to_write = s->play.fd != -1 ? to_write : 0;
|
||||
to_read = s->record.fd != -1 ? SUN_BUFFER_FRAMES : 0;
|
||||
write_ofs = 0;
|
||||
read_ofs = 0;
|
||||
while (to_write > 0 || to_read > 0) {
|
||||
|
|
@ -487,12 +472,12 @@ sun_io_routine(void * arg)
|
|||
ssize_t n, frames;
|
||||
|
||||
if (to_write > 0) {
|
||||
bytes = FRAMES_TO_BYTES(to_write, s->p_info.play.channels);
|
||||
if ((n = write(s->play_fd, s->play_buf + write_ofs, bytes)) < 0) {
|
||||
bytes = to_write * s->play.frame_size;
|
||||
if ((n = write(s->play.fd, s->play.buf + write_ofs, bytes)) < 0) {
|
||||
state = CUBEB_STATE_ERROR;
|
||||
break;
|
||||
}
|
||||
frames = BYTES_TO_FRAMES(n, s->p_info.play.channels);
|
||||
frames = n / s->play.frame_size;
|
||||
pthread_mutex_lock(&s->mutex);
|
||||
s->frames_written += frames;
|
||||
pthread_mutex_unlock(&s->mutex);
|
||||
|
|
@ -500,12 +485,12 @@ sun_io_routine(void * arg)
|
|||
write_ofs += frames;
|
||||
}
|
||||
if (to_read > 0) {
|
||||
bytes = FRAMES_TO_BYTES(to_read, s->r_info.record.channels);
|
||||
if ((n = read(s->record_fd, s->record_buf + read_ofs, bytes)) < 0) {
|
||||
bytes = to_read * s->record.frame_size;
|
||||
if ((n = read(s->record.fd, s->record.buf + read_ofs, bytes)) < 0) {
|
||||
state = CUBEB_STATE_ERROR;
|
||||
break;
|
||||
}
|
||||
frames = BYTES_TO_FRAMES(n, s->r_info.record.channels);
|
||||
frames = n / s->record.frame_size;
|
||||
to_read -= frames;
|
||||
read_ofs += frames;
|
||||
}
|
||||
|
|
@ -541,19 +526,19 @@ sun_stream_init(cubeb * context,
|
|||
ret = CUBEB_ERROR;
|
||||
goto error;
|
||||
}
|
||||
s->record_fd = -1;
|
||||
s->play_fd = -1;
|
||||
s->record.fd = -1;
|
||||
s->play.fd = -1;
|
||||
if (input_device != 0) {
|
||||
snprintf(s->input_name, sizeof(s->input_name),
|
||||
snprintf(s->record.name, sizeof(s->record.name),
|
||||
"/dev/audio%zu", (uintptr_t)input_device - 1);
|
||||
} else {
|
||||
snprintf(s->input_name, sizeof(s->input_name), "%s", SUN_DEFAULT_DEVICE);
|
||||
snprintf(s->record.name, sizeof(s->record.name), "%s", SUN_DEFAULT_DEVICE);
|
||||
}
|
||||
if (output_device != 0) {
|
||||
snprintf(s->output_name, sizeof(s->output_name),
|
||||
snprintf(s->play.name, sizeof(s->play.name),
|
||||
"/dev/audio%zu", (uintptr_t)output_device - 1);
|
||||
} else {
|
||||
snprintf(s->output_name, sizeof(s->output_name), "%s", SUN_DEFAULT_DEVICE);
|
||||
snprintf(s->play.name, sizeof(s->play.name), "%s", SUN_DEFAULT_DEVICE);
|
||||
}
|
||||
if (input_stream_params != NULL) {
|
||||
if (input_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
|
||||
|
|
@ -561,22 +546,23 @@ sun_stream_init(cubeb * context,
|
|||
ret = CUBEB_ERROR_NOT_SUPPORTED;
|
||||
goto error;
|
||||
}
|
||||
if (s->record_fd == -1) {
|
||||
if ((s->record_fd = open(s->input_name, O_RDONLY)) == -1) {
|
||||
LOG("Audio device cannot be opened as read-only");
|
||||
if (s->record.fd == -1) {
|
||||
if ((s->record.fd = open(s->record.name, O_RDONLY)) == -1) {
|
||||
LOG("Audio device could not be opened as read-only");
|
||||
ret = CUBEB_ERROR_DEVICE_UNAVAILABLE;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
AUDIO_INITINFO(&s->r_info);
|
||||
AUDIO_INITINFO(&s->record.info);
|
||||
#ifdef AUMODE_RECORD
|
||||
s->r_info.mode = AUMODE_RECORD;
|
||||
s->record.info.mode = AUMODE_RECORD;
|
||||
#endif
|
||||
if ((ret = sun_copy_params(s->record_fd, s, input_stream_params,
|
||||
&s->r_info, &s->r_info.record)) != CUBEB_OK) {
|
||||
if ((ret = sun_copy_params(s->record.fd, s, input_stream_params,
|
||||
&s->record.info, &s->record.info.record)) != CUBEB_OK) {
|
||||
LOG("Setting record params failed");
|
||||
goto error;
|
||||
}
|
||||
s->record.floating = (input_stream_params->format == CUBEB_SAMPLE_FLOAT32NE);
|
||||
}
|
||||
if (output_stream_params != NULL) {
|
||||
if (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
|
||||
|
|
@ -584,22 +570,23 @@ sun_stream_init(cubeb * context,
|
|||
ret = CUBEB_ERROR_NOT_SUPPORTED;
|
||||
goto error;
|
||||
}
|
||||
if (s->play_fd == -1) {
|
||||
if ((s->play_fd = open(s->output_name, O_WRONLY)) == -1) {
|
||||
LOG("Audio device cannot be opened as write-only");
|
||||
if (s->play.fd == -1) {
|
||||
if ((s->play.fd = open(s->play.name, O_WRONLY)) == -1) {
|
||||
LOG("Audio device could not be opened as write-only");
|
||||
ret = CUBEB_ERROR_DEVICE_UNAVAILABLE;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
AUDIO_INITINFO(&s->p_info);
|
||||
AUDIO_INITINFO(&s->play.info);
|
||||
#ifdef AUMODE_PLAY
|
||||
s->p_info.mode = AUMODE_PLAY;
|
||||
s->play.info.mode = AUMODE_PLAY;
|
||||
#endif
|
||||
if ((ret = sun_copy_params(s->play_fd, s, output_stream_params,
|
||||
&s->p_info, &s->p_info.play)) != CUBEB_OK) {
|
||||
if ((ret = sun_copy_params(s->play.fd, s, output_stream_params,
|
||||
&s->play.info, &s->play.info.play)) != CUBEB_OK) {
|
||||
LOG("Setting play params failed");
|
||||
goto error;
|
||||
}
|
||||
s->play.floating = (output_stream_params->format == CUBEB_SAMPLE_FLOAT32NE);
|
||||
}
|
||||
s->context = context;
|
||||
s->volume = 1.0;
|
||||
|
|
@ -610,28 +597,20 @@ sun_stream_init(cubeb * context,
|
|||
LOG("Failed to create mutex");
|
||||
goto error;
|
||||
}
|
||||
if (s->play_fd != -1 && (s->play_buf = calloc(SUN_BUFFER_FRAMES,
|
||||
s->p_info.play.channels * sizeof(int16_t))) == NULL) {
|
||||
s->play.frame_size = s->play.info.play.channels *
|
||||
(s->play.info.play.precision / 8);
|
||||
if (s->play.fd != -1 &&
|
||||
(s->play.buf = calloc(SUN_BUFFER_FRAMES, s->play.frame_size)) == NULL) {
|
||||
ret = CUBEB_ERROR;
|
||||
goto error;
|
||||
}
|
||||
if (s->record_fd != -1 && (s->record_buf = calloc(SUN_BUFFER_FRAMES,
|
||||
s->r_info.record.channels * sizeof(int16_t))) == NULL) {
|
||||
s->record.frame_size = s->record.info.record.channels *
|
||||
(s->record.info.record.precision / 8);
|
||||
if (s->record.fd != -1 &&
|
||||
(s->record.buf = calloc(SUN_BUFFER_FRAMES, s->record.frame_size)) == NULL) {
|
||||
ret = CUBEB_ERROR;
|
||||
goto error;
|
||||
}
|
||||
if (s->floating) {
|
||||
if (s->play_fd != -1 && (s->f_play_buf = calloc(SUN_BUFFER_FRAMES,
|
||||
s->p_info.play.channels * sizeof(float))) == NULL) {
|
||||
ret = CUBEB_ERROR;
|
||||
goto error;
|
||||
}
|
||||
if (s->record_fd != -1 && (s->f_record_buf = calloc(SUN_BUFFER_FRAMES,
|
||||
s->r_info.record.channels * sizeof(float))) == NULL) {
|
||||
ret = CUBEB_ERROR;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
*stream = s;
|
||||
return CUBEB_OK;
|
||||
error:
|
||||
|
|
@ -644,7 +623,7 @@ error:
|
|||
static int
|
||||
sun_stream_start(cubeb_stream * s)
|
||||
{
|
||||
s->running = 1;
|
||||
s->running = true;
|
||||
if (pthread_create(&s->thread, NULL, sun_io_routine, s) != 0) {
|
||||
LOG("Couldn't create thread");
|
||||
return CUBEB_ERROR;
|
||||
|
|
@ -658,12 +637,11 @@ sun_stream_get_position(cubeb_stream * s, uint64_t * position)
|
|||
#ifdef AUDIO_GETOOFFS
|
||||
struct audio_offset offset;
|
||||
|
||||
if (ioctl(s->play_fd, AUDIO_GETOOFFS, &offset) == -1) {
|
||||
if (ioctl(s->play.fd, AUDIO_GETOOFFS, &offset) == -1) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
s->blocks_written += offset.deltablks;
|
||||
*position = BYTES_TO_FRAMES(s->blocks_written * s->p_info.blocksize,
|
||||
s->p_info.play.channels);
|
||||
*position = (s->blocks_written * s->play.info.blocksize) / s->play.frame_size;
|
||||
return CUBEB_OK;
|
||||
#else
|
||||
pthread_mutex_lock(&s->mutex);
|
||||
|
|
@ -674,22 +652,21 @@ sun_stream_get_position(cubeb_stream * s, uint64_t * position)
|
|||
}
|
||||
|
||||
static int
|
||||
sun_stream_get_latency(cubeb_stream * stream, uint32_t * latency)
|
||||
sun_stream_get_latency(cubeb_stream * s, uint32_t * latency)
|
||||
{
|
||||
#ifdef AUDIO_GETBUFINFO
|
||||
struct audio_info info;
|
||||
|
||||
if (ioctl(stream->play_fd, AUDIO_GETBUFINFO, &info) == -1) {
|
||||
if (ioctl(s->play.fd, AUDIO_GETBUFINFO, &info) == -1) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
*latency = BYTES_TO_FRAMES(info.play.seek + info.blocksize,
|
||||
info.play.channels);
|
||||
*latency = (info.play.seek + info.blocksize) / s->play.frame_size;
|
||||
return CUBEB_OK;
|
||||
#else
|
||||
cubeb_stream_params params;
|
||||
|
||||
params.rate = stream->p_info.play.sample_rate;
|
||||
params.rate = stream->play.info.play.sample_rate;
|
||||
|
||||
return sun_get_min_latency(NULL, params, latency);
|
||||
#endif
|
||||
|
|
@ -711,10 +688,10 @@ sun_get_current_device(cubeb_stream * stream, cubeb_device ** const device)
|
|||
if (*device == NULL) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
(*device)->input_name = stream->record_fd != -1 ?
|
||||
strdup(stream->input_name) : NULL;
|
||||
(*device)->output_name = stream->play_fd != -1 ?
|
||||
strdup(stream->output_name) : NULL;
|
||||
(*device)->input_name = stream->record.fd != -1 ?
|
||||
strdup(stream->record.name) : NULL;
|
||||
(*device)->output_name = stream->play.fd != -1 ?
|
||||
strdup(stream->play.name) : NULL;
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
|
|
@ -744,6 +721,7 @@ static struct cubeb_ops const sun_ops = {
|
|||
.stream_reset_default_device = NULL,
|
||||
.stream_get_position = sun_stream_get_position,
|
||||
.stream_get_latency = sun_stream_get_latency,
|
||||
.stream_get_input_latency = NULL,
|
||||
.stream_set_volume = sun_stream_set_volume,
|
||||
.stream_get_current_device = sun_get_current_device,
|
||||
.stream_device_destroy = sun_stream_device_destroy,
|
||||
|
|
|
|||
|
|
@ -19,5 +19,6 @@ size_t cubeb_sample_size(cubeb_sample_format format)
|
|||
default:
|
||||
// should never happen as all cases are handled above.
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,6 +89,9 @@ DEFINE_PROPERTYKEY(PKEY_Device_InstanceId, 0x78c34fc8, 0x104a, 0x4aca, 0x9e
|
|||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
const int64_t LATENCY_NOT_AVAILABLE_YET = -1;
|
||||
|
||||
struct com_heap_ptr_deleter {
|
||||
void operator()(void * ptr) const noexcept {
|
||||
CoTaskMemFree(ptr);
|
||||
|
|
@ -211,6 +214,7 @@ struct cubeb {
|
|||
/* Collection changed for output (render) devices. */
|
||||
cubeb_device_collection_changed_callback output_collection_changed_callback = nullptr;
|
||||
void * output_collection_changed_user_ptr = nullptr;
|
||||
UINT64 performance_counter_frequency;
|
||||
};
|
||||
|
||||
class wasapi_endpoint_notification_client;
|
||||
|
|
@ -331,7 +335,12 @@ struct cubeb_stream {
|
|||
bool draining = false;
|
||||
/* True when we've destroyed the stream. This pointer is leaked on stream
|
||||
* destruction if we could not join the thread. */
|
||||
std::atomic<std::atomic<bool>*> emergency_bailout;
|
||||
std::atomic<std::atomic<bool>*> emergency_bailout { nullptr };
|
||||
/* Synchronizes render thread start to ensure safe access to emergency_bailout. */
|
||||
HANDLE thread_ready_event = 0;
|
||||
/* This needs an active audio input stream to be known, and is updated in the
|
||||
* first audio input callback. */
|
||||
std::atomic<int64_t> input_latency_hns { LATENCY_NOT_AVAILABLE_YET };
|
||||
};
|
||||
|
||||
class monitor_device_notifications {
|
||||
|
|
@ -344,13 +353,14 @@ public:
|
|||
|
||||
~monitor_device_notifications()
|
||||
{
|
||||
SetEvent(shutdown);
|
||||
WaitForSingleObject(thread, 5000);
|
||||
SetEvent(begin_shutdown);
|
||||
WaitForSingleObject(shutdown_complete, INFINITE);
|
||||
CloseHandle(thread);
|
||||
|
||||
CloseHandle(input_changed);
|
||||
CloseHandle(output_changed);
|
||||
CloseHandle(shutdown);
|
||||
CloseHandle(begin_shutdown);
|
||||
CloseHandle(shutdown_complete);
|
||||
}
|
||||
|
||||
void notify(EDataFlow flow)
|
||||
|
|
@ -375,8 +385,9 @@ private:
|
|||
thread_proc(LPVOID args)
|
||||
{
|
||||
XASSERT(args);
|
||||
static_cast<monitor_device_notifications*>(args)
|
||||
->notification_thread_loop();
|
||||
auto mdn = static_cast<monitor_device_notifications*>(args);
|
||||
mdn->notification_thread_loop();
|
||||
SetEvent(mdn->shutdown_complete);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -395,7 +406,7 @@ private:
|
|||
HANDLE wait_array[3] = {
|
||||
input_changed,
|
||||
output_changed,
|
||||
shutdown,
|
||||
begin_shutdown,
|
||||
};
|
||||
|
||||
while (true) {
|
||||
|
|
@ -433,9 +444,15 @@ private:
|
|||
return;
|
||||
}
|
||||
|
||||
shutdown = CreateEvent(nullptr, 0, 0, nullptr);
|
||||
if (!shutdown) {
|
||||
LOG("Failed to create shutdown event.");
|
||||
begin_shutdown = CreateEvent(nullptr, 0, 0, nullptr);
|
||||
if (!begin_shutdown) {
|
||||
LOG("Failed to create begin_shutdown event.");
|
||||
return;
|
||||
}
|
||||
|
||||
shutdown_complete = CreateEvent(nullptr, 0, 0, nullptr);
|
||||
if (!shutdown_complete) {
|
||||
LOG("Failed to create shutdown_complete event.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -454,7 +471,8 @@ private:
|
|||
HANDLE thread = INVALID_HANDLE_VALUE;
|
||||
HANDLE output_changed = INVALID_HANDLE_VALUE;
|
||||
HANDLE input_changed = INVALID_HANDLE_VALUE;
|
||||
HANDLE shutdown = INVALID_HANDLE_VALUE;
|
||||
HANDLE begin_shutdown = INVALID_HANDLE_VALUE;
|
||||
HANDLE shutdown_complete = INVALID_HANDLE_VALUE;
|
||||
|
||||
cubeb * cubeb_context = nullptr;
|
||||
};
|
||||
|
|
@ -845,6 +863,7 @@ bool get_input_buffer(cubeb_stream * stm)
|
|||
BYTE * input_packet = NULL;
|
||||
DWORD flags;
|
||||
UINT64 dev_pos;
|
||||
UINT64 pc_position;
|
||||
UINT32 next;
|
||||
/* Get input packets until we have captured enough frames, and put them in a
|
||||
* contiguous buffer. */
|
||||
|
|
@ -874,13 +893,25 @@ bool get_input_buffer(cubeb_stream * stm)
|
|||
&frames,
|
||||
&flags,
|
||||
&dev_pos,
|
||||
NULL);
|
||||
&pc_position);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
LOG("GetBuffer failed for capture: %lx", hr);
|
||||
return false;
|
||||
}
|
||||
XASSERT(frames == next);
|
||||
|
||||
if (stm->context->performance_counter_frequency) {
|
||||
LARGE_INTEGER now;
|
||||
UINT64 now_hns;
|
||||
// See https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nf-audioclient-iaudiocaptureclient-getbuffer, section "Remarks".
|
||||
QueryPerformanceCounter(&now);
|
||||
now_hns = 10000000 * now.QuadPart / stm->context->performance_counter_frequency;
|
||||
if (now_hns >= pc_position) {
|
||||
stm->input_latency_hns = now_hns - pc_position;
|
||||
}
|
||||
}
|
||||
|
||||
UINT32 input_stream_samples = frames * stm->input_stream_params.channels;
|
||||
// We do not explicitly handle the AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY
|
||||
// flag. There a two primary (non exhaustive) scenarios we anticipate this
|
||||
|
|
@ -1143,6 +1174,13 @@ wasapi_stream_render_loop(LPVOID stream)
|
|||
cubeb_stream * stm = static_cast<cubeb_stream *>(stream);
|
||||
std::atomic<bool> * emergency_bailout = stm->emergency_bailout;
|
||||
|
||||
// Signal wasapi_stream_start that we've copied emergency_bailout.
|
||||
BOOL ok = SetEvent(stm->thread_ready_event);
|
||||
if (!ok) {
|
||||
LOG("thread_ready SetEvent failed: %lx", GetLastError());
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool is_playing = true;
|
||||
HANDLE wait_array[4] = {
|
||||
stm->shutdown_event,
|
||||
|
|
@ -1171,11 +1209,6 @@ wasapi_stream_render_loop(LPVOID stream)
|
|||
LOG("Unable to use mmcss to bump the render thread priority: %lx", GetLastError());
|
||||
}
|
||||
|
||||
// This has already been nulled out, simply exit.
|
||||
if (!emergency_bailout) {
|
||||
is_playing = false;
|
||||
}
|
||||
|
||||
/* WaitForMultipleObjects timeout can trigger in cases where we don't want to
|
||||
treat it as a timeout, such as across a system sleep/wake cycle. Trigger
|
||||
the timeout error handling only when the timeout_limit is reached, which is
|
||||
|
|
@ -1514,6 +1547,14 @@ int wasapi_init(cubeb ** context, char const * context_name)
|
|||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
LARGE_INTEGER frequency;
|
||||
if (QueryPerformanceFrequency(&frequency)) {
|
||||
LOG("Failed getting performance counter frequency, latency reporting will be inacurate");
|
||||
ctx->performance_counter_frequency = 0;
|
||||
} else {
|
||||
ctx->performance_counter_frequency = frequency.QuadPart;
|
||||
}
|
||||
|
||||
*context = ctx;
|
||||
|
||||
return CUBEB_OK;
|
||||
|
|
@ -1544,24 +1585,15 @@ bool stop_and_join_render_thread(cubeb_stream * stm)
|
|||
/* Wait five seconds for the rendering thread to return. It's supposed to
|
||||
* check its event loop very often, five seconds is rather conservative. */
|
||||
DWORD r = WaitForSingleObject(stm->thread, 5000);
|
||||
if (r == WAIT_TIMEOUT) {
|
||||
if (r != WAIT_OBJECT_0) {
|
||||
/* Something weird happened, leak the thread and continue the shutdown
|
||||
* process. */
|
||||
*(stm->emergency_bailout) = true;
|
||||
// We give the ownership to the rendering thread.
|
||||
stm->emergency_bailout = nullptr;
|
||||
LOG("Destroy WaitForSingleObject on thread timed out,"
|
||||
" leaking the thread: %lx", GetLastError());
|
||||
LOG("Destroy WaitForSingleObject on thread failed: %lx, %lx", r, GetLastError());
|
||||
rv = false;
|
||||
}
|
||||
if (r == WAIT_FAILED) {
|
||||
*(stm->emergency_bailout) = true;
|
||||
// We give the ownership to the rendering thread.
|
||||
stm->emergency_bailout = nullptr;
|
||||
LOG("Destroy WaitForSingleObject on thread failed: %lx", GetLastError());
|
||||
rv = false;
|
||||
}
|
||||
|
||||
|
||||
// Only attempts to close and null out the thread and event if the
|
||||
// WaitForSingleObject above succeeded, so that calling this function again
|
||||
|
|
@ -1926,14 +1958,18 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm,
|
|||
|
||||
/* Get a client. We will get all other interfaces we need from
|
||||
* this pointer. */
|
||||
#if 0 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1590902
|
||||
hr = device->Activate(__uuidof(IAudioClient3),
|
||||
CLSCTX_INPROC_SERVER,
|
||||
NULL, audio_client.receive_vpp());
|
||||
if (hr == E_NOINTERFACE) {
|
||||
#endif
|
||||
hr = device->Activate(__uuidof(IAudioClient),
|
||||
CLSCTX_INPROC_SERVER,
|
||||
NULL, audio_client.receive_vpp());
|
||||
#if 0
|
||||
}
|
||||
#endif
|
||||
|
||||
if (FAILED(hr)) {
|
||||
LOG("Could not activate the device to get an audio"
|
||||
|
|
@ -1999,16 +2035,20 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm,
|
|||
flags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
|
||||
}
|
||||
|
||||
#if 0 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1590902
|
||||
if (initialize_iaudioclient3(audio_client, stm, mix_format, flags, direction)) {
|
||||
LOG("Initialized with IAudioClient3");
|
||||
} else {
|
||||
#endif
|
||||
hr = audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED,
|
||||
flags,
|
||||
frames_to_hns(stm, stm->latency),
|
||||
0,
|
||||
mix_format.get(),
|
||||
NULL);
|
||||
#if 0
|
||||
}
|
||||
#endif
|
||||
if (FAILED(hr)) {
|
||||
LOG("Unable to initialize audio client for %s: %lx.", DIRECTION_NAME, hr);
|
||||
return CUBEB_ERROR;
|
||||
|
|
@ -2471,6 +2511,12 @@ int wasapi_stream_start(cubeb_stream * stm)
|
|||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
stm->thread_ready_event = CreateEvent(NULL, 0, 0, NULL);
|
||||
if (!stm->thread_ready_event) {
|
||||
LOG("Can't create the thread_ready event, error: %lx", GetLastError());
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
cubeb_async_log_reset_threads();
|
||||
stm->thread = (HANDLE) _beginthreadex(NULL, 512 * 1024, wasapi_stream_render_loop, stm, STACK_SIZE_PARAM_IS_A_RESERVATION, NULL);
|
||||
if (stm->thread == NULL) {
|
||||
|
|
@ -2478,6 +2524,14 @@ int wasapi_stream_start(cubeb_stream * stm)
|
|||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
// Wait for wasapi_stream_render_loop to signal that emergency_bailout has
|
||||
// been read, avoiding a bailout situation where we could free `stm`
|
||||
// before wasapi_stream_render_loop had a chance to run.
|
||||
HRESULT hr = WaitForSingleObject(stm->thread_ready_event, INFINITE);
|
||||
XASSERT(hr == WAIT_OBJECT_0);
|
||||
CloseHandle(stm->thread_ready_event);
|
||||
stm->thread_ready_event = 0;
|
||||
|
||||
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
|
||||
|
||||
return CUBEB_OK;
|
||||
|
|
@ -2511,11 +2565,8 @@ int wasapi_stream_stop(cubeb_stream * stm)
|
|||
}
|
||||
|
||||
if (stop_and_join_render_thread(stm)) {
|
||||
// This is null if we've given the pointer to the other thread
|
||||
if (stm->emergency_bailout.load()) {
|
||||
delete stm->emergency_bailout.load();
|
||||
stm->emergency_bailout = nullptr;
|
||||
}
|
||||
delete stm->emergency_bailout.load();
|
||||
stm->emergency_bailout = nullptr;
|
||||
} else {
|
||||
// If we could not join the thread, put the stream in error.
|
||||
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
|
||||
|
|
@ -2591,6 +2642,26 @@ int wasapi_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
|
|||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
|
||||
int wasapi_stream_get_input_latency(cubeb_stream * stm, uint32_t * latency)
|
||||
{
|
||||
XASSERT(stm && latency);
|
||||
|
||||
if (!has_input(stm)) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
auto_lock lock(stm->stream_reset_lock);
|
||||
|
||||
if (stm->input_latency_hns == LATENCY_NOT_AVAILABLE_YET) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
*latency = hns_to_frames(stm, stm->input_latency_hns);
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
int wasapi_stream_set_volume(cubeb_stream * stm, float volume)
|
||||
{
|
||||
auto_lock lock(stm->stream_reset_lock);
|
||||
|
|
@ -2950,6 +3021,7 @@ cubeb_ops const wasapi_ops = {
|
|||
/*.stream_reset_default_device =*/ wasapi_stream_reset_default_device,
|
||||
/*.stream_get_position =*/ wasapi_stream_get_position,
|
||||
/*.stream_get_latency =*/ wasapi_stream_get_latency,
|
||||
/*.stream_get_input_latency =*/ wasapi_stream_get_input_latency,
|
||||
/*.stream_set_volume =*/ wasapi_stream_set_volume,
|
||||
/*.stream_get_current_device =*/ NULL,
|
||||
/*.stream_device_destroy =*/ NULL,
|
||||
|
|
|
|||
|
|
@ -1059,6 +1059,7 @@ static struct cubeb_ops const winmm_ops = {
|
|||
/*.stream_reset_default_device =*/ NULL,
|
||||
/*.stream_get_position =*/ winmm_stream_get_position,
|
||||
/*.stream_get_latency = */ winmm_stream_get_latency,
|
||||
/*.stream_get_input_latency = */ NULL,
|
||||
/*.stream_set_volume =*/ winmm_stream_set_volume,
|
||||
/*.stream_get_current_device =*/ NULL,
|
||||
/*.stream_device_destroy =*/ NULL,
|
||||
|
|
|
|||
|
|
@ -66,22 +66,6 @@ void test_registering_and_unregistering_callback(cubeb_stream * stream)
|
|||
ASSERT_EQ(r, CUBEB_OK) << "Error unregistering device changed callback";
|
||||
}
|
||||
|
||||
void test_registering_second_callbacks(cubeb_stream * stream)
|
||||
{
|
||||
int r = cubeb_stream_register_device_changed_callback(stream, device_changed_callback);
|
||||
if (r == CUBEB_ERROR_NOT_SUPPORTED) {
|
||||
return;
|
||||
}
|
||||
ASSERT_EQ(r, CUBEB_OK) << "Error registering device changed callback";
|
||||
|
||||
// Get an assertion fails when registering a callback
|
||||
// without unregistering the original one.
|
||||
ASSERT_DEATH(
|
||||
cubeb_stream_register_device_changed_callback(stream, device_changed_callback),
|
||||
""
|
||||
);
|
||||
}
|
||||
|
||||
TEST(cubeb, device_changed_callbacks)
|
||||
{
|
||||
cubeb * ctx;
|
||||
|
|
@ -121,7 +105,5 @@ TEST(cubeb, device_changed_callbacks)
|
|||
|
||||
test_registering_and_unregistering_callback(stream);
|
||||
|
||||
test_registering_second_callbacks(stream);
|
||||
|
||||
cubeb_stream_destroy(stream);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ TEST(cubeb, enumerate_devices)
|
|||
if (r == CUBEB_ERROR_NOT_SUPPORTED) {
|
||||
fprintf(stderr, "Device enumeration not supported"
|
||||
" for this backend, skipping this test.\n");
|
||||
r = CUBEB_OK;
|
||||
return;
|
||||
}
|
||||
ASSERT_EQ(r, CUBEB_OK) << "Error enumerating devices " << r;
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
const uint32_t SAMPLE_FREQUENCY = 48000;
|
||||
const uint32_t TONE_FREQUENCY = 440;
|
||||
const double OUTPUT_AMPLITUDE = 0.25;
|
||||
const uint32_t NUM_FRAMES_TO_OUTPUT = SAMPLE_FREQUENCY / 20; /* play ~50ms of samples */
|
||||
const int32_t NUM_FRAMES_TO_OUTPUT = SAMPLE_FREQUENCY / 20; /* play ~50ms of samples */
|
||||
|
||||
template<typename T> T ConvertSampleToOutput(double input);
|
||||
template<> float ConvertSampleToOutput(double input) { return float(input); }
|
||||
|
|
|
|||
|
|
@ -889,3 +889,175 @@ TEST(cubeb, resampler_drift_drop_data)
|
|||
}
|
||||
}
|
||||
|
||||
static long
|
||||
passthrough_resampler_fill_eq_input(cubeb_stream * stream,
|
||||
void * user_ptr,
|
||||
void const * input_buffer,
|
||||
void * output_buffer,
|
||||
long nframes) {
|
||||
// gtest does not support using ASSERT_EQ and friends in a
|
||||
// function that returns a value.
|
||||
[nframes, input_buffer]() {
|
||||
ASSERT_EQ(nframes, 32);
|
||||
const float* input = static_cast<const float*>(input_buffer);
|
||||
for (int i = 0; i < 64; ++i) {
|
||||
ASSERT_FLOAT_EQ(input[i], 0.01 * i);
|
||||
}
|
||||
}();
|
||||
return nframes;
|
||||
}
|
||||
|
||||
TEST(cubeb, passthrough_resampler_fill_eq_input) {
|
||||
uint32_t channels = 2;
|
||||
uint32_t sample_rate = 44100;
|
||||
passthrough_resampler<float> resampler =
|
||||
passthrough_resampler<float>(nullptr, passthrough_resampler_fill_eq_input,
|
||||
nullptr, channels, sample_rate);
|
||||
|
||||
long input_frame_count = 32;
|
||||
long output_frame_count = 32;
|
||||
float input[64] = {};
|
||||
float output[64] = {};
|
||||
for (uint32_t i = 0; i < input_frame_count * channels; ++i) {
|
||||
input[i] = 0.01 * i;
|
||||
}
|
||||
long got = resampler.fill(input, &input_frame_count, output, output_frame_count);
|
||||
ASSERT_EQ(got, output_frame_count);
|
||||
// Input frames used must be equal to output frames.
|
||||
ASSERT_EQ(input_frame_count, output_frame_count);
|
||||
}
|
||||
|
||||
static long
|
||||
passthrough_resampler_fill_short_input(cubeb_stream * stream,
|
||||
void * user_ptr,
|
||||
void const * input_buffer,
|
||||
void * output_buffer,
|
||||
long nframes) {
|
||||
// gtest does not support using ASSERT_EQ and friends in a
|
||||
// function that returns a value.
|
||||
[nframes, input_buffer]() {
|
||||
ASSERT_EQ(nframes, 32);
|
||||
const float* input = static_cast<const float*>(input_buffer);
|
||||
// First part contains the input
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
ASSERT_FLOAT_EQ(input[i], 0.01 * i);
|
||||
}
|
||||
// missing part contains silence
|
||||
for (int i = 32; i < 64; ++i) {
|
||||
ASSERT_FLOAT_EQ(input[i], 0.0);
|
||||
}
|
||||
}();
|
||||
return nframes;
|
||||
}
|
||||
|
||||
TEST(cubeb, passthrough_resampler_fill_short_input) {
|
||||
uint32_t channels = 2;
|
||||
uint32_t sample_rate = 44100;
|
||||
passthrough_resampler<float> resampler =
|
||||
passthrough_resampler<float>(nullptr, passthrough_resampler_fill_short_input,
|
||||
nullptr, channels, sample_rate);
|
||||
|
||||
long input_frame_count = 16;
|
||||
long output_frame_count = 32;
|
||||
float input[64] = {};
|
||||
float output[64] = {};
|
||||
for (uint32_t i = 0; i < input_frame_count * channels; ++i) {
|
||||
input[i] = 0.01 * i;
|
||||
}
|
||||
long got = resampler.fill(input, &input_frame_count, output, output_frame_count);
|
||||
ASSERT_EQ(got, output_frame_count);
|
||||
// Input frames used are less than the output frames due to glitch.
|
||||
ASSERT_EQ(input_frame_count, output_frame_count - 16);
|
||||
}
|
||||
|
||||
static long
|
||||
passthrough_resampler_fill_input_left(cubeb_stream * stream,
|
||||
void * user_ptr,
|
||||
void const * input_buffer,
|
||||
void * output_buffer,
|
||||
long nframes) {
|
||||
// gtest does not support using ASSERT_EQ and friends in a
|
||||
// function that returns a value.
|
||||
int iteration = *static_cast<int*>(user_ptr);
|
||||
if (iteration == 1) {
|
||||
[nframes, input_buffer]() {
|
||||
ASSERT_EQ(nframes, 32);
|
||||
const float* input = static_cast<const float*>(input_buffer);
|
||||
for (int i = 0; i < 64; ++i) {
|
||||
ASSERT_FLOAT_EQ(input[i], 0.01 * i);
|
||||
}
|
||||
}();
|
||||
} else if (iteration == 2) {
|
||||
[nframes, input_buffer]() {
|
||||
ASSERT_EQ(nframes, 32);
|
||||
const float* input = static_cast<const float*>(input_buffer);
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
// First part contains the reamaining input samples from previous
|
||||
// iteration (since they were more).
|
||||
ASSERT_FLOAT_EQ(input[i], 0.01 * (i + 64));
|
||||
// next part contains the new buffer
|
||||
ASSERT_FLOAT_EQ(input[i + 32], 0.01 * i);
|
||||
}
|
||||
}();
|
||||
} else if (iteration == 3) {
|
||||
[nframes, input_buffer]() {
|
||||
ASSERT_EQ(nframes, 32);
|
||||
const float* input = static_cast<const float*>(input_buffer);
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
// First part (16 frames) contains the reamaining input samples
|
||||
// from previous iteration (since they were more).
|
||||
ASSERT_FLOAT_EQ(input[i], 0.01 * (i + 32));
|
||||
}
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
// next part (8 frames) contains the new input buffer.
|
||||
ASSERT_FLOAT_EQ(input[i + 32], 0.01 * i);
|
||||
// last part (8 frames) contains silence.
|
||||
ASSERT_FLOAT_EQ(input[i + 32 + 16], 0.0);
|
||||
}
|
||||
}();
|
||||
}
|
||||
return nframes;
|
||||
}
|
||||
|
||||
TEST(cubeb, passthrough_resampler_fill_input_left) {
|
||||
const uint32_t channels = 2;
|
||||
const uint32_t sample_rate = 44100;
|
||||
int iteration = 0;
|
||||
passthrough_resampler<float> resampler =
|
||||
passthrough_resampler<float>(nullptr, passthrough_resampler_fill_input_left,
|
||||
&iteration, channels, sample_rate);
|
||||
|
||||
long input_frame_count = 48; // 32 + 16
|
||||
const long output_frame_count = 32;
|
||||
float input[96] = {};
|
||||
float output[64] = {};
|
||||
for (uint32_t i = 0; i < input_frame_count * channels; ++i) {
|
||||
input[i] = 0.01 * i;
|
||||
}
|
||||
|
||||
// 1st iteration, add the extra input.
|
||||
iteration = 1;
|
||||
long got = resampler.fill(input, &input_frame_count, output, output_frame_count);
|
||||
ASSERT_EQ(got, output_frame_count);
|
||||
// Input frames used must be equal to output frames.
|
||||
ASSERT_EQ(input_frame_count, output_frame_count);
|
||||
|
||||
// 2st iteration, use the extra input from previous iteration,
|
||||
// 16 frames are remaining in the input buffer.
|
||||
input_frame_count = 32; // we need 16 input frames but we get more;
|
||||
iteration = 2;
|
||||
got = resampler.fill(input, &input_frame_count, output, output_frame_count);
|
||||
ASSERT_EQ(got, output_frame_count);
|
||||
// Input frames used must be equal to output frames.
|
||||
ASSERT_EQ(input_frame_count, output_frame_count);
|
||||
|
||||
// 3rd iteration, use the extra input from previous iteration.
|
||||
// 16 frames are remaining in the input buffer.
|
||||
input_frame_count = 16 - 8; // We need 16 more input frames but we only get 8.
|
||||
iteration = 3;
|
||||
got = resampler.fill(input, &input_frame_count, output, output_frame_count);
|
||||
ASSERT_EQ(got, output_frame_count);
|
||||
// Input frames used are less than the output frames due to glitch.
|
||||
ASSERT_EQ(input_frame_count, output_frame_count - 8);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ void test_ring_multi(lock_free_audio_ring_buffer<T>& buf, int channels, int capa
|
|||
sequence_generator<T> gen(channels);
|
||||
|
||||
while(iterations--) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(10));
|
||||
std::this_thread::yield();
|
||||
gen.get(in_buffer.get(), block_size);
|
||||
int rv = buf.enqueue(in_buffer.get(), block_size);
|
||||
ASSERT_TRUE(rv <= block_size);
|
||||
|
|
@ -115,7 +115,7 @@ void test_ring_multi(lock_free_audio_ring_buffer<T>& buf, int channels, int capa
|
|||
int remaining = 1002;
|
||||
|
||||
while(remaining--) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(10));
|
||||
std::this_thread::yield();
|
||||
int rv = buf.dequeue(out_buffer.get(), block_size);
|
||||
ASSERT_TRUE(rv <= block_size);
|
||||
checker.check(out_buffer.get(), rv);
|
||||
|
|
|
|||
|
|
@ -49,8 +49,8 @@ public:
|
|||
|
||||
bool init(char const * backend_name = nullptr);
|
||||
bool init_stream();
|
||||
bool start_stream() const;
|
||||
bool stop_stream() const;
|
||||
bool start_stream();
|
||||
bool stop_stream();
|
||||
bool destroy_stream() const;
|
||||
bool destroy();
|
||||
bool activate_log(cubeb_log_level log_level) const;
|
||||
|
|
@ -58,6 +58,7 @@ public:
|
|||
void set_latency_frames(uint32_t latency_frames);
|
||||
uint64_t get_stream_position() const;
|
||||
uint32_t get_stream_latency() const;
|
||||
uint32_t get_max_channel_count() const;
|
||||
|
||||
long user_data_cb(cubeb_stream* stm, void* user, const void* input_buffer,
|
||||
void* output_buffer, long nframes);
|
||||
|
|
@ -70,6 +71,8 @@ public:
|
|||
cubeb_stream_params output_params = {};
|
||||
cubeb_stream_params input_params = {};
|
||||
|
||||
void force_drain() { _force_drain = true; }
|
||||
|
||||
private:
|
||||
bool has_input() { return input_params.rate != 0; }
|
||||
bool has_output() { return output_params.rate != 0; }
|
||||
|
|
@ -85,6 +88,7 @@ private:
|
|||
std::atomic<uint32_t> _channels = {0};
|
||||
std::atomic<bool> _latency_testing = {false};
|
||||
std::atomic<uint32_t> _latency_frames = {0}; // if !0, override. Else, use min.
|
||||
std::atomic<bool> _force_drain = {false};
|
||||
|
||||
|
||||
/* Accessed only from audio thread. */
|
||||
|
|
@ -92,11 +96,12 @@ private:
|
|||
};
|
||||
|
||||
bool cubeb_client::init(char const * backend_name) {
|
||||
int rv = cubeb_init(&context, "Cubeb Test Application", nullptr);
|
||||
int rv = cubeb_init(&context, "Cubeb Test Application", backend_name);
|
||||
if (rv != CUBEB_OK) {
|
||||
fprintf(stderr, "Could not init cubeb\n");
|
||||
return false;
|
||||
}
|
||||
fprintf(stderr, "Init cubeb backend: %s\n", cubeb_get_backend_id(context));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -159,7 +164,8 @@ bool cubeb_client::init_stream() {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool cubeb_client::start_stream() const {
|
||||
bool cubeb_client::start_stream() {
|
||||
_force_drain = false;
|
||||
int rv = cubeb_stream_start(stream);
|
||||
if (rv != CUBEB_OK) {
|
||||
fprintf(stderr, "Could not start the stream\n");
|
||||
|
|
@ -168,7 +174,8 @@ bool cubeb_client::start_stream() const {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool cubeb_client::stop_stream() const {
|
||||
bool cubeb_client::stop_stream() {
|
||||
_force_drain = false;
|
||||
int rv = cubeb_stream_stop(stream);
|
||||
if (rv != CUBEB_OK) {
|
||||
fprintf(stderr, "Could not stop the stream\n");
|
||||
|
|
@ -197,6 +204,16 @@ uint32_t cubeb_client::get_stream_latency() const {
|
|||
return latency;
|
||||
}
|
||||
|
||||
uint32_t cubeb_client::get_max_channel_count() const {
|
||||
uint32_t channels = 0;
|
||||
int rv = cubeb_get_max_channel_count(context, &channels);
|
||||
if (rv != CUBEB_OK) {
|
||||
fprintf(stderr, "Could not get max channel count\n");
|
||||
return 0;
|
||||
}
|
||||
return channels;
|
||||
}
|
||||
|
||||
bool cubeb_client::destroy_stream() const {
|
||||
cubeb_stream_destroy(stream);
|
||||
return true;
|
||||
|
|
@ -248,7 +265,7 @@ long cubeb_client::user_data_cb(cubeb_stream* stm, void* user,
|
|||
const float* in = static_cast<const float*>(input_buffer);
|
||||
float* out = static_cast<float*>(output_buffer);
|
||||
if (_latency_testing) {
|
||||
for (uint32_t i = 0; i < nframes; i++) {
|
||||
for (int32_t i = 0; i < nframes; i++) {
|
||||
// Impulses every second, mixed with the input signal fed back at half
|
||||
// gain, to measure the input-to-output latency via feedback.
|
||||
uint32_t clock = ((_total_frames + i) % _rate);
|
||||
|
|
@ -263,7 +280,7 @@ long cubeb_client::user_data_cb(cubeb_stream* stm, void* user,
|
|||
}
|
||||
}
|
||||
} else {
|
||||
for (uint32_t i = 0; i < nframes; i++) {
|
||||
for (int32_t i = 0; i < nframes; i++) {
|
||||
for (uint32_t j = 0; j < _channels; j++) {
|
||||
out[i * _channels + j] = in[i];
|
||||
}
|
||||
|
|
@ -275,6 +292,11 @@ long cubeb_client::user_data_cb(cubeb_stream* stm, void* user,
|
|||
}
|
||||
|
||||
_total_frames += nframes;
|
||||
|
||||
if (_force_drain) {
|
||||
return nframes - 1;
|
||||
}
|
||||
|
||||
return nframes;
|
||||
}
|
||||
|
||||
|
|
@ -331,9 +353,12 @@ void print_help() {
|
|||
"0: change log level to disabled\n"
|
||||
"1: change log level to normal\n"
|
||||
"2: change log level to verbose\n"
|
||||
"c: get max number of channels\n"
|
||||
"p: start a initialized stream\n"
|
||||
"s: stop a started stream\n"
|
||||
"c: get stream position (client thread)\n"
|
||||
"d: destroy stream\n"
|
||||
"e: force stream to drain\n"
|
||||
"f: get stream position (client thread)\n"
|
||||
"i: change device type to input\n"
|
||||
"o: change device type to output\n"
|
||||
"a: change device type to input and output\n"
|
||||
|
|
@ -345,7 +370,7 @@ void print_help() {
|
|||
fprintf(stderr, "%s\n", msg);
|
||||
}
|
||||
|
||||
bool choose_action(const cubeb_client& cl, operation_data * op, int c) {
|
||||
bool choose_action(cubeb_client& cl, operation_data * op, int c) {
|
||||
// Consume "enter" and "space"
|
||||
while (c == 10 || c == 32) {
|
||||
c = getchar();
|
||||
|
|
@ -398,7 +423,19 @@ bool choose_action(const cubeb_client& cl, operation_data * op, int c) {
|
|||
} else {
|
||||
fprintf(stderr, "stop_stream failed\n");
|
||||
}
|
||||
} else if (c == 'd') {
|
||||
bool res = cl.destroy_stream();
|
||||
if (res) {
|
||||
fprintf(stderr, "destroy_stream succeed\n");
|
||||
} else {
|
||||
fprintf(stderr, "destroy_stream failed\n");
|
||||
}
|
||||
} else if (c == 'e') {
|
||||
cl.force_drain();
|
||||
} else if (c == 'c') {
|
||||
uint32_t channel_count = cl.get_max_channel_count();
|
||||
fprintf(stderr, "max channel count (default output device): %u\n", channel_count);
|
||||
} else if (c == 'f') {
|
||||
uint64_t pos = cl.get_stream_position();
|
||||
uint64_t latency = cl.get_stream_latency();
|
||||
fprintf(stderr, "stream position %" PRIu64 " (latency %" PRIu64 ")\n", pos, latency);
|
||||
|
|
@ -468,7 +505,7 @@ int main(int argc, char* argv[]) {
|
|||
cubeb_client cl;
|
||||
cl.activate_log(CUBEB_LOG_DISABLED);
|
||||
fprintf(stderr, "Log level is DISABLED\n");
|
||||
cl.init();
|
||||
cl.init(/* default backend */);
|
||||
|
||||
op.collection_device_type = CUBEB_DEVICE_TYPE_UNKNOWN;
|
||||
fprintf(stderr, "collection device type is UNKNOWN\n");
|
||||
|
|
|
|||
6
third_party/rust/cubeb-sys/src/device.rs
vendored
6
third_party/rust/cubeb-sys/src/device.rs
vendored
|
|
@ -27,11 +27,11 @@ pub const CUBEB_DEVICE_FMT_S16NE: cubeb_device_fmt = CUBEB_DEVICE_FMT_S16LE;
|
|||
pub const CUBEB_DEVICE_FMT_F32NE: cubeb_device_fmt = CUBEB_DEVICE_FMT_F32LE;
|
||||
|
||||
pub const CUBEB_DEVICE_FMT_S16_MASK: cubeb_device_fmt =
|
||||
(CUBEB_DEVICE_FMT_S16LE | CUBEB_DEVICE_FMT_S16BE);
|
||||
CUBEB_DEVICE_FMT_S16LE | CUBEB_DEVICE_FMT_S16BE;
|
||||
pub const CUBEB_DEVICE_FMT_F32_MASK: cubeb_device_fmt =
|
||||
(CUBEB_DEVICE_FMT_F32LE | CUBEB_DEVICE_FMT_F32BE);
|
||||
CUBEB_DEVICE_FMT_F32LE | CUBEB_DEVICE_FMT_F32BE;
|
||||
pub const CUBEB_DEVICE_FMT_ALL: cubeb_device_fmt =
|
||||
(CUBEB_DEVICE_FMT_S16_MASK | CUBEB_DEVICE_FMT_F32_MASK);
|
||||
CUBEB_DEVICE_FMT_S16_MASK | CUBEB_DEVICE_FMT_F32_MASK;
|
||||
|
||||
cubeb_enum! {
|
||||
pub enum cubeb_device_pref {
|
||||
|
|
|
|||
1
third_party/rust/cubeb-sys/src/stream.rs
vendored
1
third_party/rust/cubeb-sys/src/stream.rs
vendored
|
|
@ -66,6 +66,7 @@ extern "C" {
|
|||
pub fn cubeb_stream_reset_default_device(stream: *mut cubeb_stream) -> c_int;
|
||||
pub fn cubeb_stream_get_position(stream: *mut cubeb_stream, position: *mut u64) -> c_int;
|
||||
pub fn cubeb_stream_get_latency(stream: *mut cubeb_stream, latency: *mut c_uint) -> c_int;
|
||||
pub fn cubeb_stream_get_input_latency(stream: *mut cubeb_stream, latency: *mut c_uint) -> c_int;
|
||||
pub fn cubeb_stream_set_volume(stream: *mut cubeb_stream, volume: c_float) -> c_int;
|
||||
pub fn cubeb_stream_get_current_device(
|
||||
stream: *mut cubeb_stream,
|
||||
|
|
|
|||
2
third_party/rust/cubeb/.cargo-checksum.json
vendored
2
third_party/rust/cubeb/.cargo-checksum.json
vendored
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"3f16c9c35795e9a05783518797fba3fd031ae46b61232ce0c05e13e5203506d7","LICENSE":"8c044baa5d883274736eeece0b955249076c2697b826e576fce59496235b2cf5","README.md":"408c573ec240927cf5b9c036098e94e374ec41f71991415422586f450586b214","examples/common/mod.rs":"a5e1b79fc2b4addff1e442879ba3dbcb1cf5973e76b9a62d97dd0042597480db","examples/devices.rs":"89e13542853995d1ae4a49d6829156efb29dd25c6caffdf22496c28c8263ffeb","examples/tone.rs":"8f5f9851b6d99f6f16c597fcb9312e3ef81769cbfb89341d2ea2522ca2e2214e","src/context.rs":"03511fa960a411728163e700edc2fd6cfbfcf09766ffe62ee82a2cbd08fdf243","src/frame.rs":"ed1e8f4576022d0c23106bb115125e5a2967b0375a10d0c54bbe99f04a70cc3f","src/lib.rs":"98e9280890551ac9305f2f808e315b6aa6bcd5781b8e96a078787ded0ef91e2a","src/log.rs":"704faeb31934dad6bc6d02e01caa85118754209bd559d30d03fcfa5cb8c1603c","src/sample.rs":"e23be3b691052001916f920ce9c1a0051bd097e39c9d34cbcb80ab8120265f45","src/stream.rs":"3ee0432f655cd42959cd5d8e75cb4fe2322e1f88fa5d9cc83e615ae229cdeb8a"},"package":"3cbcdfde9ea319160af6eff068ffaa96aad3532e1b5c0ebc134614cfacacae24"}
|
||||
{"files":{"Cargo.lock":"8768f2709f7b520aa3bd0973a03333a1880eb65625af912514d1a26b712326fe","Cargo.toml":"28be23f743402b5e1b63a04246aeb186cdae205bf253617ef4bab42408ee1263","LICENSE":"8c044baa5d883274736eeece0b955249076c2697b826e576fce59496235b2cf5","README.md":"408c573ec240927cf5b9c036098e94e374ec41f71991415422586f450586b214","examples/common/mod.rs":"a5e1b79fc2b4addff1e442879ba3dbcb1cf5973e76b9a62d97dd0042597480db","examples/devices.rs":"ff5dcd588e7036165c4b4c20ec355d036e0ae90cf88b3b0f5cd86621fe2ce61d","examples/tone.rs":"8f5f9851b6d99f6f16c597fcb9312e3ef81769cbfb89341d2ea2522ca2e2214e","src/context.rs":"72507f5338a2f520fef9e2eface0638afba4c0d9e87fde92a0aaade643ba1335","src/frame.rs":"ed1e8f4576022d0c23106bb115125e5a2967b0375a10d0c54bbe99f04a70cc3f","src/lib.rs":"98e9280890551ac9305f2f808e315b6aa6bcd5781b8e96a078787ded0ef91e2a","src/log.rs":"704faeb31934dad6bc6d02e01caa85118754209bd559d30d03fcfa5cb8c1603c","src/sample.rs":"e23be3b691052001916f920ce9c1a0051bd097e39c9d34cbcb80ab8120265f45","src/stream.rs":"b3babf86252cd19cfbc98ffbc8f48bb033284f89db9cbdc46836611893356eff"},"package":"1116606d6045c9199f6a1e082f3cf63383ba6f9961339701faa6370dcf73135f"}
|
||||
55
third_party/rust/cubeb/Cargo.lock
generated
vendored
Normal file
55
third_party/rust/cubeb/Cargo.lock
generated
vendored
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.52"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d87b23d6a92cd03af510a5ade527033f6aa6fa92161e2d5863a907d4c5e31d"
|
||||
|
||||
[[package]]
|
||||
name = "cmake"
|
||||
version = "0.1.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81fb25b677f8bf1eb325017cb6bb8452f87969db0fedb4f757b297bee78a7c62"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cubeb"
|
||||
version = "0.7.0"
|
||||
dependencies = [
|
||||
"cubeb-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cubeb-core"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7c55529b8f47926e4242e1fc01d31b08a5a4847967c5c250644e33fe237cfe5"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cubeb-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cubeb-sys"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcbc562eb6ccf62abacf9e3eebce992e5c36b230ca313ebd7c2d7d0e99deae90"
|
||||
dependencies = [
|
||||
"cmake",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677"
|
||||
4
third_party/rust/cubeb/Cargo.toml
vendored
4
third_party/rust/cubeb/Cargo.toml
vendored
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
[package]
|
||||
name = "cubeb"
|
||||
version = "0.6.2"
|
||||
version = "0.7.0"
|
||||
authors = ["Dan Glastonbury <dglastonbury@mozilla.com>"]
|
||||
description = "Bindings to libcubeb for interacting with system audio from rust.\n"
|
||||
homepage = "https://github.com/djg/cubeb-rs"
|
||||
|
|
@ -22,7 +22,7 @@ categories = ["api-bindings"]
|
|||
license = "ISC"
|
||||
repository = "https://github.com/djg/cubeb-rs"
|
||||
[dependencies.cubeb-core]
|
||||
version = "0.6.2"
|
||||
version = "0.7.0"
|
||||
|
||||
[features]
|
||||
gecko-in-tree = ["cubeb-core/gecko-in-tree"]
|
||||
|
|
|
|||
5
third_party/rust/cubeb/examples/devices.rs
vendored
5
third_party/rust/cubeb/examples/devices.rs
vendored
|
|
@ -11,7 +11,6 @@ extern crate cubeb;
|
|||
mod common;
|
||||
|
||||
use cubeb::{DeviceFormat, DeviceType};
|
||||
use std::error::Error;
|
||||
|
||||
fn print_device_info(info: &cubeb::DeviceInfo) {
|
||||
let devtype = if info.device_type().contains(DeviceType::INPUT) {
|
||||
|
|
@ -101,7 +100,7 @@ fn main() {
|
|||
return;
|
||||
}
|
||||
Err(e) => {
|
||||
println!("Error enumerating devices: {}", e.description());
|
||||
println!("Error enumerating devices: {}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
|
@ -119,7 +118,7 @@ fn main() {
|
|||
let devices = match ctx.enumerate_devices(DeviceType::OUTPUT) {
|
||||
Ok(devices) => devices,
|
||||
Err(e) => {
|
||||
println!("Error enumerating devices: {}", e.description());
|
||||
println!("Error enumerating devices: {}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
2
third_party/rust/cubeb/src/context.rs
vendored
2
third_party/rust/cubeb/src/context.rs
vendored
|
|
@ -2,7 +2,7 @@ use {Context, Result};
|
|||
use std::ffi::CString;
|
||||
|
||||
pub fn init<T: Into<Vec<u8>>>(name: T) -> Result<Context> {
|
||||
let name = try!(CString::new(name));
|
||||
let name = CString::new(name)?;
|
||||
|
||||
Context::init(Some(name.as_c_str()), None)
|
||||
}
|
||||
|
|
|
|||
12
third_party/rust/cubeb/src/stream.rs
vendored
12
third_party/rust/cubeb/src/stream.rs
vendored
|
|
@ -68,9 +68,9 @@ use std::mem::ManuallyDrop;
|
|||
use std::os::raw::{c_long, c_void};
|
||||
use std::slice::{from_raw_parts, from_raw_parts_mut};
|
||||
|
||||
pub type DataCallback<F> = FnMut(&[F], &mut [F]) -> isize + Send + Sync + 'static;
|
||||
pub type StateCallback = FnMut(State) + Send + Sync + 'static;
|
||||
pub type DeviceChangedCallback = FnMut() + Send + Sync + 'static;
|
||||
pub type DataCallback<F> = dyn FnMut(&[F], &mut [F]) -> isize + Send + Sync + 'static;
|
||||
pub type StateCallback = dyn FnMut(State) + Send + Sync + 'static;
|
||||
pub type DeviceChangedCallback = dyn FnMut() + Send + Sync + 'static;
|
||||
|
||||
pub struct StreamCallbacks<F> {
|
||||
pub(crate) data: Box<DataCallback<F>>,
|
||||
|
|
@ -201,7 +201,7 @@ impl<'a, F> StreamBuilder<'a, F> {
|
|||
let state_callback: ffi::cubeb_state_callback = Some(state_cb_c::<F>);
|
||||
|
||||
let stream = unsafe {
|
||||
try!(ctx.stream_init(
|
||||
ctx.stream_init(
|
||||
stream_name,
|
||||
input_device,
|
||||
input_stream_params,
|
||||
|
|
@ -211,12 +211,12 @@ impl<'a, F> StreamBuilder<'a, F> {
|
|||
data_callback,
|
||||
state_callback,
|
||||
cbs as *mut _
|
||||
))
|
||||
)?
|
||||
};
|
||||
if has_device_changed {
|
||||
let device_changed_callback: ffi::cubeb_device_changed_callback =
|
||||
Some(device_changed_cb_c::<F>);
|
||||
try!(stream.register_device_changed_callback(device_changed_callback));
|
||||
stream.register_device_changed_callback(device_changed_callback)?;
|
||||
}
|
||||
Ok(Stream::new(stream))
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue