Merge pull request #365 from mitchmindtree/rustfmt
Remove old `.rustfmt.toml` config. Run default `cargo fmt` on repo. Add formatting check github action.
This commit is contained in:
commit
5390c01641
|
@ -24,6 +24,23 @@ jobs:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
args: --all --all-features
|
args: --all --all-features
|
||||||
|
|
||||||
|
rustfmt-check:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
- name: Install stable
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: stable
|
||||||
|
override: true
|
||||||
|
components: rustfmt
|
||||||
|
- name: Run rustfmt
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: fmt
|
||||||
|
args: --all -- --check
|
||||||
|
|
||||||
cargo-publish:
|
cargo-publish:
|
||||||
|
|
||||||
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
|
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
fn_args_density = "Compressed"
|
|
||||||
fn_args_layout = "Visual"
|
|
||||||
fn_brace_style = "SameLineWhere"
|
|
||||||
fn_call_style = "Visual"
|
|
||||||
fn_empty_single_line = false
|
|
||||||
format_strings = true
|
|
||||||
generics_indent = "Visual"
|
|
||||||
impl_empty_single_line = false
|
|
||||||
match_block_trailing_comma = true
|
|
||||||
reorder_imported_names = true
|
|
||||||
reorder_imports = true
|
|
||||||
reorder_imports_in_group = true
|
|
||||||
spaces_around_ranges = true
|
|
||||||
use_try_shorthand = true
|
|
||||||
where_density = "Tall"
|
|
||||||
where_style = "Legacy"
|
|
||||||
wrap_match_arms = false
|
|
||||||
write_mode = "Overwrite"
|
|
3323
alsa-sys/src/lib.rs
3323
alsa-sys/src/lib.rs
File diff suppressed because it is too large
Load Diff
|
@ -64,8 +64,8 @@ fn create_lib(cpal_asio_dir: &PathBuf) {
|
||||||
let entry = match entry {
|
let entry = match entry {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("error: {}", e);
|
println!("error: {}", e);
|
||||||
continue
|
continue;
|
||||||
},
|
}
|
||||||
Ok(entry) => entry,
|
Ok(entry) => entry,
|
||||||
};
|
};
|
||||||
match entry.path().extension().and_then(|s| s.to_str()) {
|
match entry.path().extension().and_then(|s| s.to_str()) {
|
||||||
|
@ -127,8 +127,13 @@ fn create_bindings(cpal_asio_dir: &PathBuf) {
|
||||||
($opt_header:expr, $FILE_NAME:expr) => {
|
($opt_header:expr, $FILE_NAME:expr) => {
|
||||||
match $opt_header.as_ref() {
|
match $opt_header.as_ref() {
|
||||||
None => {
|
None => {
|
||||||
panic!("Could not find {} in {}: {}", $FILE_NAME, CPAL_ASIO_DIR, cpal_asio_dir.display());
|
panic!(
|
||||||
},
|
"Could not find {} in {}: {}",
|
||||||
|
$FILE_NAME,
|
||||||
|
CPAL_ASIO_DIR,
|
||||||
|
cpal_asio_dir.display()
|
||||||
|
);
|
||||||
|
}
|
||||||
Some(path) => path.to_str().expect("Could not convert path to str"),
|
Some(path) => path.to_str().expect("Could not convert path to str"),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -152,9 +157,9 @@ fn create_bindings(cpal_asio_dir: &PathBuf) {
|
||||||
.clang_arg("-x")
|
.clang_arg("-x")
|
||||||
.clang_arg("c++")
|
.clang_arg("c++")
|
||||||
.clang_arg("-std=c++14")
|
.clang_arg("-std=c++14")
|
||||||
.clang_arg( format!("-I{}/{}", cpal_asio_dir.display(), "host/pc") )
|
.clang_arg(format!("-I{}/{}", cpal_asio_dir.display(), "host/pc"))
|
||||||
.clang_arg( format!("-I{}/{}", cpal_asio_dir.display(), "host") )
|
.clang_arg(format!("-I{}/{}", cpal_asio_dir.display(), "host"))
|
||||||
.clang_arg( format!("-I{}/{}", cpal_asio_dir.display(), "common") )
|
.clang_arg(format!("-I{}/{}", cpal_asio_dir.display(), "common"))
|
||||||
// Need to whitelist to avoid binding tp c++ std::*
|
// Need to whitelist to avoid binding tp c++ std::*
|
||||||
.whitelist_type("AsioDrivers")
|
.whitelist_type("AsioDrivers")
|
||||||
.whitelist_type("AsioDriver")
|
.whitelist_type("AsioDriver")
|
||||||
|
|
|
@ -38,8 +38,12 @@ fn main() {
|
||||||
for name in asio.driver_names() {
|
for name in asio.driver_names() {
|
||||||
println!("Driver: {:?}", name);
|
println!("Driver: {:?}", name);
|
||||||
let driver = asio.load_driver(&name).expect("failed to load driver");
|
let driver = asio.load_driver(&name).expect("failed to load driver");
|
||||||
let channels = driver.channels().expect("failed to retrieve channel counts");
|
let channels = driver
|
||||||
let sample_rate = driver.sample_rate().expect("failed to retrieve sample rate");
|
.channels()
|
||||||
|
.expect("failed to retrieve channel counts");
|
||||||
|
let sample_rate = driver
|
||||||
|
.sample_rate()
|
||||||
|
.expect("failed to retrieve sample rate");
|
||||||
let in_fmt = Format {
|
let in_fmt = Format {
|
||||||
channels: channels.ins as _,
|
channels: channels.ins as _,
|
||||||
sample_rate: SampleRate(sample_rate as _),
|
sample_rate: SampleRate(sample_rate as _),
|
||||||
|
|
|
@ -5,7 +5,13 @@ fn main() {
|
||||||
for driver in asio.driver_names() {
|
for driver in asio.driver_names() {
|
||||||
println!("Driver: {}", driver);
|
println!("Driver: {}", driver);
|
||||||
let driver = asio.load_driver(&driver).expect("failed to load drivers");
|
let driver = asio.load_driver(&driver).expect("failed to load drivers");
|
||||||
println!(" Channels: {:?}", driver.channels().expect("failed to get channels"));
|
println!(
|
||||||
println!(" Sample rate: {:?}", driver.sample_rate().expect("failed to get sample rate"));
|
" Channels: {:?}",
|
||||||
|
driver.channels().expect("failed to get channels")
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
" Sample rate: {:?}",
|
||||||
|
driver.sample_rate().expect("failed to get sample rate")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,7 +112,9 @@ macro_rules! asio_result {
|
||||||
r if r == AsioErrorWrapper::ASE_OK as i32 => Ok(()),
|
r if r == AsioErrorWrapper::ASE_OK as i32 => Ok(()),
|
||||||
r if r == AsioErrorWrapper::ASE_SUCCESS as i32 => Ok(()),
|
r if r == AsioErrorWrapper::ASE_SUCCESS as i32 => Ok(()),
|
||||||
r if r == AsioErrorWrapper::ASE_NotPresent as i32 => Err(AsioError::NoDrivers),
|
r if r == AsioErrorWrapper::ASE_NotPresent as i32 => Err(AsioError::NoDrivers),
|
||||||
r if r == AsioErrorWrapper::ASE_HWMalfunction as i32 => Err(AsioError::HardwareMalfunction),
|
r if r == AsioErrorWrapper::ASE_HWMalfunction as i32 => {
|
||||||
|
Err(AsioError::HardwareMalfunction)
|
||||||
|
}
|
||||||
r if r == AsioErrorWrapper::ASE_InvalidParameter as i32 => Err(AsioError::InvalidInput),
|
r if r == AsioErrorWrapper::ASE_InvalidParameter as i32 => Err(AsioError::InvalidInput),
|
||||||
r if r == AsioErrorWrapper::ASE_InvalidMode as i32 => Err(AsioError::BadMode),
|
r if r == AsioErrorWrapper::ASE_InvalidMode as i32 => Err(AsioError::BadMode),
|
||||||
r if r == AsioErrorWrapper::ASE_SPNotAdvancing as i32 => Err(AsioError::HardwareStuck),
|
r if r == AsioErrorWrapper::ASE_SPNotAdvancing as i32 => Err(AsioError::HardwareStuck),
|
||||||
|
|
|
@ -2,8 +2,8 @@ pub mod asio_import;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
|
|
||||||
use num_traits::FromPrimitive;
|
|
||||||
use self::errors::{AsioError, AsioErrorWrapper, LoadDriverError};
|
use self::errors::{AsioError, AsioErrorWrapper, LoadDriverError};
|
||||||
|
use num_traits::FromPrimitive;
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::os::raw::{c_char, c_double, c_long, c_void};
|
use std::os::raw::{c_char, c_double, c_long, c_void};
|
||||||
|
@ -165,9 +165,12 @@ pub struct AsioBufferInfo {
|
||||||
struct AsioCallbacks {
|
struct AsioCallbacks {
|
||||||
buffer_switch: extern "C" fn(double_buffer_index: c_long, direct_process: c_long) -> (),
|
buffer_switch: extern "C" fn(double_buffer_index: c_long, direct_process: c_long) -> (),
|
||||||
sample_rate_did_change: extern "C" fn(s_rate: c_double) -> (),
|
sample_rate_did_change: extern "C" fn(s_rate: c_double) -> (),
|
||||||
asio_message:
|
asio_message: extern "C" fn(
|
||||||
extern "C" fn(selector: c_long, value: c_long, message: *mut (), opt: *mut c_double)
|
selector: c_long,
|
||||||
-> c_long,
|
value: c_long,
|
||||||
|
message: *mut (),
|
||||||
|
opt: *mut c_double,
|
||||||
|
) -> c_long,
|
||||||
buffer_switch_time_info: extern "C" fn(
|
buffer_switch_time_info: extern "C" fn(
|
||||||
params: *mut ai::ASIOTime,
|
params: *mut ai::ASIOTime,
|
||||||
double_buffer_index: c_long,
|
double_buffer_index: c_long,
|
||||||
|
@ -276,8 +279,9 @@ impl Asio {
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let num_drivers = ai::get_driver_names(driver_name_ptrs.as_mut_ptr(), MAX_DRIVERS as i32);
|
let num_drivers =
|
||||||
(0 .. num_drivers)
|
ai::get_driver_names(driver_name_ptrs.as_mut_ptr(), MAX_DRIVERS as i32);
|
||||||
|
(0..num_drivers)
|
||||||
.map(|i| driver_name_to_utf8(&driver_names[i as usize]).to_string())
|
.map(|i| driver_name_to_utf8(&driver_names[i as usize]).to_string())
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
@ -317,8 +321,8 @@ impl Asio {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make owned CString to send to load driver
|
// Make owned CString to send to load driver
|
||||||
let driver_name_cstring = CString::new(driver_name)
|
let driver_name_cstring =
|
||||||
.expect("failed to create `CString` from driver name");
|
CString::new(driver_name).expect("failed to create `CString` from driver name");
|
||||||
let mut driver_info = std::mem::MaybeUninit::<ai::ASIODriverInfo>::uninit();
|
let mut driver_info = std::mem::MaybeUninit::<ai::ASIODriverInfo>::uninit();
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -332,9 +336,15 @@ impl Asio {
|
||||||
let state = Mutex::new(DriverState::Initialized);
|
let state = Mutex::new(DriverState::Initialized);
|
||||||
let name = driver_name.to_string();
|
let name = driver_name.to_string();
|
||||||
let destroyed = false;
|
let destroyed = false;
|
||||||
let inner = Arc::new(DriverInner { name, state, destroyed });
|
let inner = Arc::new(DriverInner {
|
||||||
*self.loaded_driver.lock().expect("failed to acquire loaded driver lock") =
|
name,
|
||||||
Arc::downgrade(&inner);
|
state,
|
||||||
|
destroyed,
|
||||||
|
});
|
||||||
|
*self
|
||||||
|
.loaded_driver
|
||||||
|
.lock()
|
||||||
|
.expect("failed to acquire loaded driver lock") = Arc::downgrade(&inner);
|
||||||
let driver = Driver { inner };
|
let driver = Driver { inner };
|
||||||
Ok(driver)
|
Ok(driver)
|
||||||
}
|
}
|
||||||
|
@ -461,7 +471,10 @@ impl Driver {
|
||||||
mut input_buffer_infos: Vec<AsioBufferInfo>,
|
mut input_buffer_infos: Vec<AsioBufferInfo>,
|
||||||
mut output_buffer_infos: Vec<AsioBufferInfo>,
|
mut output_buffer_infos: Vec<AsioBufferInfo>,
|
||||||
) -> Result<AsioStreams, AsioError> {
|
) -> Result<AsioStreams, AsioError> {
|
||||||
let (input, output) = match (input_buffer_infos.is_empty(), output_buffer_infos.is_empty()) {
|
let (input, output) = match (
|
||||||
|
input_buffer_infos.is_empty(),
|
||||||
|
output_buffer_infos.is_empty(),
|
||||||
|
) {
|
||||||
// Both stream exist.
|
// Both stream exist.
|
||||||
(false, false) => {
|
(false, false) => {
|
||||||
// Create one continuous slice of buffers.
|
// Create one continuous slice of buffers.
|
||||||
|
@ -481,7 +494,7 @@ impl Driver {
|
||||||
buffer_size,
|
buffer_size,
|
||||||
});
|
});
|
||||||
(input, output)
|
(input, output)
|
||||||
},
|
}
|
||||||
// Just input
|
// Just input
|
||||||
(false, true) => {
|
(false, true) => {
|
||||||
let buffer_size = self.create_buffers(&mut input_buffer_infos)?;
|
let buffer_size = self.create_buffers(&mut input_buffer_infos)?;
|
||||||
|
@ -491,7 +504,7 @@ impl Driver {
|
||||||
});
|
});
|
||||||
let output = None;
|
let output = None;
|
||||||
(input, output)
|
(input, output)
|
||||||
},
|
}
|
||||||
// Just output
|
// Just output
|
||||||
(true, false) => {
|
(true, false) => {
|
||||||
let buffer_size = self.create_buffers(&mut output_buffer_infos)?;
|
let buffer_size = self.create_buffers(&mut output_buffer_infos)?;
|
||||||
|
@ -501,7 +514,7 @@ impl Driver {
|
||||||
buffer_size,
|
buffer_size,
|
||||||
});
|
});
|
||||||
(input, output)
|
(input, output)
|
||||||
},
|
}
|
||||||
// Impossible
|
// Impossible
|
||||||
(true, true) => unreachable!("Trying to create streams without preparing"),
|
(true, true) => unreachable!("Trying to create streams without preparing"),
|
||||||
};
|
};
|
||||||
|
@ -729,7 +742,11 @@ fn prepare_buffer_infos(is_input: bool, n_channels: usize) -> Vec<AsioBufferInfo
|
||||||
let channel_num = ch as c_long;
|
let channel_num = ch as c_long;
|
||||||
// To be filled by ASIOCreateBuffers.
|
// To be filled by ASIOCreateBuffers.
|
||||||
let buffers = [std::ptr::null_mut(); 2];
|
let buffers = [std::ptr::null_mut(); 2];
|
||||||
AsioBufferInfo { is_input, channel_num, buffers }
|
AsioBufferInfo {
|
||||||
|
is_input,
|
||||||
|
channel_num,
|
||||||
|
buffers,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
@ -788,18 +805,14 @@ fn stream_data_type(is_input: bool) -> Result<AsioSampleType, AsioError> {
|
||||||
///
|
///
|
||||||
/// This converts to utf8.
|
/// This converts to utf8.
|
||||||
fn driver_name_to_utf8(bytes: &[c_char]) -> std::borrow::Cow<str> {
|
fn driver_name_to_utf8(bytes: &[c_char]) -> std::borrow::Cow<str> {
|
||||||
unsafe {
|
unsafe { CStr::from_ptr(bytes.as_ptr()).to_string_lossy() }
|
||||||
CStr::from_ptr(bytes.as_ptr()).to_string_lossy()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ASIO uses null terminated c strings for channel names.
|
/// ASIO uses null terminated c strings for channel names.
|
||||||
///
|
///
|
||||||
/// This converts to utf8.
|
/// This converts to utf8.
|
||||||
fn _channel_name_to_utf8(bytes: &[c_char]) -> std::borrow::Cow<str> {
|
fn _channel_name_to_utf8(bytes: &[c_char]) -> std::borrow::Cow<str> {
|
||||||
unsafe {
|
unsafe { CStr::from_ptr(bytes.as_ptr()).to_string_lossy() }
|
||||||
CStr::from_ptr(bytes.as_ptr()).to_string_lossy()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Indicates the stream sample rate has changed.
|
/// Indicates the stream sample rate has changed.
|
||||||
|
@ -917,8 +930,9 @@ extern "C" fn buffer_switch(double_buffer_index: c_long, direct_process: c_long)
|
||||||
&mut time.time_info.system_time,
|
&mut time.time_info.system_time,
|
||||||
);
|
);
|
||||||
if let Ok(()) = asio_result!(res) {
|
if let Ok(()) = asio_result!(res) {
|
||||||
time.time_info.flags =
|
time.time_info.flags = (ai::AsioTimeInfoFlags::kSystemTimeValid
|
||||||
(ai::AsioTimeInfoFlags::kSystemTimeValid | ai::AsioTimeInfoFlags::kSamplePositionValid).0;
|
| ai::AsioTimeInfoFlags::kSamplePositionValid)
|
||||||
|
.0;
|
||||||
}
|
}
|
||||||
time
|
time
|
||||||
};
|
};
|
||||||
|
@ -930,7 +944,16 @@ extern "C" fn buffer_switch(double_buffer_index: c_long, direct_process: c_long)
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_type_sizes() {
|
fn check_type_sizes() {
|
||||||
assert_eq!(std::mem::size_of::<AsioSampleRate>(), std::mem::size_of::<ai::ASIOSampleRate>());
|
assert_eq!(
|
||||||
assert_eq!(std::mem::size_of::<AsioTimeCode>(), std::mem::size_of::<ai::ASIOTimeCode>());
|
std::mem::size_of::<AsioSampleRate>(),
|
||||||
assert_eq!(std::mem::size_of::<AsioTime>(), std::mem::size_of::<ai::ASIOTime>());
|
std::mem::size_of::<ai::ASIOSampleRate>()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
std::mem::size_of::<AsioTimeCode>(),
|
||||||
|
std::mem::size_of::<ai::ASIOTimeCode>()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
std::mem::size_of::<AsioTime>(),
|
||||||
|
std::mem::size_of::<ai::ASIOTime>()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,6 @@ extern crate num_traits;
|
||||||
#[cfg(asio)]
|
#[cfg(asio)]
|
||||||
pub mod bindings;
|
pub mod bindings;
|
||||||
#[cfg(asio)]
|
#[cfg(asio)]
|
||||||
pub use bindings::*;
|
|
||||||
#[cfg(asio)]
|
|
||||||
pub use bindings::errors::{AsioError, LoadDriverError};
|
pub use bindings::errors::{AsioError, LoadDriverError};
|
||||||
|
#[cfg(asio)]
|
||||||
|
pub use bindings::*;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
extern crate cpal;
|
|
||||||
extern crate anyhow;
|
extern crate anyhow;
|
||||||
|
extern crate cpal;
|
||||||
|
|
||||||
use cpal::traits::{DeviceTrait, HostTrait};
|
use cpal::traits::{DeviceTrait, HostTrait};
|
||||||
|
|
||||||
|
@ -30,12 +30,17 @@ fn main() -> Result<(), anyhow::Error> {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Error: {:?}", e);
|
println!("Error: {:?}", e);
|
||||||
continue;
|
continue;
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
if input_formats.peek().is_some() {
|
if input_formats.peek().is_some() {
|
||||||
println!(" All supported input stream formats:");
|
println!(" All supported input stream formats:");
|
||||||
for (format_index, format) in input_formats.enumerate() {
|
for (format_index, format) in input_formats.enumerate() {
|
||||||
println!(" {}.{}. {:?}", device_index + 1, format_index + 1, format);
|
println!(
|
||||||
|
" {}.{}. {:?}",
|
||||||
|
device_index + 1,
|
||||||
|
format_index + 1,
|
||||||
|
format
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,12 +53,17 @@ fn main() -> Result<(), anyhow::Error> {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Error: {:?}", e);
|
println!("Error: {:?}", e);
|
||||||
continue;
|
continue;
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
if output_formats.peek().is_some() {
|
if output_formats.peek().is_some() {
|
||||||
println!(" All supported output stream formats:");
|
println!(" All supported output stream formats:");
|
||||||
for (format_index, format) in output_formats.enumerate() {
|
for (format_index, format) in output_formats.enumerate() {
|
||||||
println!(" {}.{}. {:?}", device_index + 1, format_index + 1, format);
|
println!(
|
||||||
|
" {}.{}. {:?}",
|
||||||
|
device_index + 1,
|
||||||
|
format_index + 1,
|
||||||
|
format
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,11 +69,14 @@ fn main() -> Result<(), anyhow::Error> {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
input_fell_behind = Some(err);
|
input_fell_behind = Some(err);
|
||||||
0.0
|
0.0
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if let Some(err) = input_fell_behind {
|
if let Some(err) = input_fell_behind {
|
||||||
eprintln!("input stream fell behind: {:?}: try increasing latency", err);
|
eprintln!(
|
||||||
|
"input stream fell behind: {:?}: try increasing latency",
|
||||||
|
err
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,9 +7,9 @@ extern crate cpal;
|
||||||
extern crate hound;
|
extern crate hound;
|
||||||
|
|
||||||
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
|
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::BufWriter;
|
use std::io::BufWriter;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
fn main() -> Result<(), anyhow::Error> {
|
fn main() -> Result<(), anyhow::Error> {
|
||||||
// Use the default host for working with audio devices.
|
// Use the default host for working with audio devices.
|
||||||
|
@ -40,12 +40,10 @@ fn main() -> Result<(), anyhow::Error> {
|
||||||
eprintln!("an error occurred on stream: {}", err);
|
eprintln!("an error occurred on stream: {}", err);
|
||||||
};
|
};
|
||||||
|
|
||||||
let data_fn = move |data: &cpal::Data| {
|
let data_fn = move |data: &cpal::Data| match data.sample_format() {
|
||||||
match data.sample_format() {
|
cpal::SampleFormat::F32 => write_input_data::<f32, f32>(data, &writer_2),
|
||||||
cpal::SampleFormat::F32 => write_input_data::<f32, f32>(data, &writer_2),
|
cpal::SampleFormat::I16 => write_input_data::<i16, i16>(data, &writer_2),
|
||||||
cpal::SampleFormat::I16 => write_input_data::<i16, i16>(data, &writer_2),
|
cpal::SampleFormat::U16 => write_input_data::<u16, i16>(data, &writer_2),
|
||||||
cpal::SampleFormat::U16 => write_input_data::<u16, i16>(data, &writer_2),
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let stream = device.build_input_stream(&format, data_fn, err_fn)?;
|
let stream = device.build_input_stream(&format, data_fn, err_fn)?;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use {BackendSpecificError, DevicesError};
|
|
||||||
use super::Device;
|
|
||||||
use super::alsa;
|
use super::alsa;
|
||||||
use super::check_errors;
|
use super::check_errors;
|
||||||
|
use super::Device;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
use {BackendSpecificError, DevicesError};
|
||||||
|
|
||||||
/// ALSA implementation for `Devices`.
|
/// ALSA implementation for `Devices`.
|
||||||
pub struct Devices {
|
pub struct Devices {
|
||||||
|
@ -36,10 +36,8 @@ impl Devices {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for Devices {
|
unsafe impl Send for Devices {}
|
||||||
}
|
unsafe impl Sync for Devices {}
|
||||||
unsafe impl Sync for Devices {
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Devices {
|
impl Drop for Devices {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -61,8 +59,10 @@ impl Iterator for Devices {
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = {
|
let name = {
|
||||||
let n_ptr = alsa::snd_device_name_get_hint(*self.next_str as *const _,
|
let n_ptr = alsa::snd_device_name_get_hint(
|
||||||
b"NAME\0".as_ptr() as *const _);
|
*self.next_str as *const _,
|
||||||
|
b"NAME\0".as_ptr() as *const _,
|
||||||
|
);
|
||||||
if !n_ptr.is_null() {
|
if !n_ptr.is_null() {
|
||||||
let bytes = CString::from_raw(n_ptr).into_bytes();
|
let bytes = CString::from_raw(n_ptr).into_bytes();
|
||||||
let string = String::from_utf8(bytes).unwrap();
|
let string = String::from_utf8(bytes).unwrap();
|
||||||
|
@ -73,8 +73,10 @@ impl Iterator for Devices {
|
||||||
};
|
};
|
||||||
|
|
||||||
let io = {
|
let io = {
|
||||||
let n_ptr = alsa::snd_device_name_get_hint(*self.next_str as *const _,
|
let n_ptr = alsa::snd_device_name_get_hint(
|
||||||
b"IOID\0".as_ptr() as *const _);
|
*self.next_str as *const _,
|
||||||
|
b"IOID\0".as_ptr() as *const _,
|
||||||
|
);
|
||||||
if !n_ptr.is_null() {
|
if !n_ptr.is_null() {
|
||||||
let bytes = CString::from_raw(n_ptr).into_bytes();
|
let bytes = CString::from_raw(n_ptr).into_bytes();
|
||||||
let string = String::from_utf8(bytes).unwrap();
|
let string = String::from_utf8(bytes).unwrap();
|
||||||
|
@ -99,7 +101,7 @@ impl Iterator for Devices {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
name
|
name
|
||||||
},
|
}
|
||||||
_ => continue,
|
_ => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,26 +2,14 @@ extern crate alsa_sys as alsa;
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
BackendSpecificError,
|
BackendSpecificError, BuildStreamError, ChannelCount, Data, DefaultFormatError,
|
||||||
BuildStreamError,
|
DeviceNameError, DevicesError, Format, PauseStreamError, PlayStreamError, SampleFormat,
|
||||||
ChannelCount,
|
SampleRate, StreamError, SupportedFormat, SupportedFormatsError,
|
||||||
Data,
|
|
||||||
DefaultFormatError,
|
|
||||||
DeviceNameError,
|
|
||||||
DevicesError,
|
|
||||||
Format,
|
|
||||||
PauseStreamError,
|
|
||||||
PlayStreamError,
|
|
||||||
SampleFormat,
|
|
||||||
SampleRate,
|
|
||||||
StreamError,
|
|
||||||
SupportedFormat,
|
|
||||||
SupportedFormatsError,
|
|
||||||
};
|
};
|
||||||
use std::{cmp, ffi, io, ptr};
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::thread::{self, JoinHandle};
|
use std::thread::{self, JoinHandle};
|
||||||
use std::vec::IntoIter as VecIntoIter;
|
use std::vec::IntoIter as VecIntoIter;
|
||||||
|
use std::{cmp, ffi, io, ptr};
|
||||||
use traits::{DeviceTrait, HostTrait, StreamTrait};
|
use traits::{DeviceTrait, HostTrait, StreamTrait};
|
||||||
|
|
||||||
pub use self::enumerate::{default_input_device, default_output_device, Devices};
|
pub use self::enumerate::{default_input_device, default_output_device, Devices};
|
||||||
|
@ -72,11 +60,15 @@ impl DeviceTrait for Device {
|
||||||
Device::name(self)
|
Device::name(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn supported_input_formats(&self) -> Result<Self::SupportedInputFormats, SupportedFormatsError> {
|
fn supported_input_formats(
|
||||||
|
&self,
|
||||||
|
) -> Result<Self::SupportedInputFormats, SupportedFormatsError> {
|
||||||
Device::supported_input_formats(self)
|
Device::supported_input_formats(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn supported_output_formats(&self) -> Result<Self::SupportedOutputFormats, SupportedFormatsError> {
|
fn supported_output_formats(
|
||||||
|
&self,
|
||||||
|
) -> Result<Self::SupportedOutputFormats, SupportedFormatsError> {
|
||||||
Device::supported_output_formats(self)
|
Device::supported_output_formats(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,7 +111,6 @@ impl DeviceTrait for Device {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct TriggerSender(libc::c_int);
|
struct TriggerSender(libc::c_int);
|
||||||
|
|
||||||
struct TriggerReceiver(libc::c_int);
|
struct TriggerReceiver(libc::c_int);
|
||||||
|
@ -247,8 +238,7 @@ impl Device {
|
||||||
unsafe fn supported_formats(
|
unsafe fn supported_formats(
|
||||||
&self,
|
&self,
|
||||||
stream_t: alsa::snd_pcm_stream_t,
|
stream_t: alsa::snd_pcm_stream_t,
|
||||||
) -> Result<VecIntoIter<SupportedFormat>, SupportedFormatsError>
|
) -> Result<VecIntoIter<SupportedFormat>, SupportedFormatsError> {
|
||||||
{
|
|
||||||
let mut handle = ptr::null_mut();
|
let mut handle = ptr::null_mut();
|
||||||
let device_name = match ffi::CString::new(&self.0[..]) {
|
let device_name = match ffi::CString::new(&self.0[..]) {
|
||||||
Ok(name) => name,
|
Ok(name) => name,
|
||||||
|
@ -284,53 +274,50 @@ impl Device {
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: check endianess
|
// TODO: check endianess
|
||||||
const FORMATS: [(SampleFormat, alsa::snd_pcm_format_t); 3] =
|
const FORMATS: [(SampleFormat, alsa::snd_pcm_format_t); 3] = [
|
||||||
[
|
//SND_PCM_FORMAT_S8,
|
||||||
//SND_PCM_FORMAT_S8,
|
//SND_PCM_FORMAT_U8,
|
||||||
//SND_PCM_FORMAT_U8,
|
(SampleFormat::I16, alsa::SND_PCM_FORMAT_S16_LE),
|
||||||
(SampleFormat::I16, alsa::SND_PCM_FORMAT_S16_LE),
|
//SND_PCM_FORMAT_S16_BE,
|
||||||
//SND_PCM_FORMAT_S16_BE,
|
(SampleFormat::U16, alsa::SND_PCM_FORMAT_U16_LE),
|
||||||
(SampleFormat::U16, alsa::SND_PCM_FORMAT_U16_LE),
|
//SND_PCM_FORMAT_U16_BE,
|
||||||
//SND_PCM_FORMAT_U16_BE,
|
//SND_PCM_FORMAT_S24_LE,
|
||||||
/*SND_PCM_FORMAT_S24_LE,
|
//SND_PCM_FORMAT_S24_BE,
|
||||||
SND_PCM_FORMAT_S24_BE,
|
//SND_PCM_FORMAT_U24_LE,
|
||||||
SND_PCM_FORMAT_U24_LE,
|
//SND_PCM_FORMAT_U24_BE,
|
||||||
SND_PCM_FORMAT_U24_BE,
|
//SND_PCM_FORMAT_S32_LE,
|
||||||
SND_PCM_FORMAT_S32_LE,
|
//SND_PCM_FORMAT_S32_BE,
|
||||||
SND_PCM_FORMAT_S32_BE,
|
//SND_PCM_FORMAT_U32_LE,
|
||||||
SND_PCM_FORMAT_U32_LE,
|
//SND_PCM_FORMAT_U32_BE,
|
||||||
SND_PCM_FORMAT_U32_BE,*/
|
(SampleFormat::F32, alsa::SND_PCM_FORMAT_FLOAT_LE),
|
||||||
(SampleFormat::F32, alsa::SND_PCM_FORMAT_FLOAT_LE) /*SND_PCM_FORMAT_FLOAT_BE,
|
//SND_PCM_FORMAT_FLOAT_BE,
|
||||||
SND_PCM_FORMAT_FLOAT64_LE,
|
//SND_PCM_FORMAT_FLOAT64_LE,
|
||||||
SND_PCM_FORMAT_FLOAT64_BE,
|
//SND_PCM_FORMAT_FLOAT64_BE,
|
||||||
SND_PCM_FORMAT_IEC958_SUBFRAME_LE,
|
//SND_PCM_FORMAT_IEC958_SUBFRAME_LE,
|
||||||
SND_PCM_FORMAT_IEC958_SUBFRAME_BE,
|
//SND_PCM_FORMAT_IEC958_SUBFRAME_BE,
|
||||||
SND_PCM_FORMAT_MU_LAW,
|
//SND_PCM_FORMAT_MU_LAW,
|
||||||
SND_PCM_FORMAT_A_LAW,
|
//SND_PCM_FORMAT_A_LAW,
|
||||||
SND_PCM_FORMAT_IMA_ADPCM,
|
//SND_PCM_FORMAT_IMA_ADPCM,
|
||||||
SND_PCM_FORMAT_MPEG,
|
//SND_PCM_FORMAT_MPEG,
|
||||||
SND_PCM_FORMAT_GSM,
|
//SND_PCM_FORMAT_GSM,
|
||||||
SND_PCM_FORMAT_SPECIAL,
|
//SND_PCM_FORMAT_SPECIAL,
|
||||||
SND_PCM_FORMAT_S24_3LE,
|
//SND_PCM_FORMAT_S24_3LE,
|
||||||
SND_PCM_FORMAT_S24_3BE,
|
//SND_PCM_FORMAT_S24_3BE,
|
||||||
SND_PCM_FORMAT_U24_3LE,
|
//SND_PCM_FORMAT_U24_3LE,
|
||||||
SND_PCM_FORMAT_U24_3BE,
|
//SND_PCM_FORMAT_U24_3BE,
|
||||||
SND_PCM_FORMAT_S20_3LE,
|
//SND_PCM_FORMAT_S20_3LE,
|
||||||
SND_PCM_FORMAT_S20_3BE,
|
//SND_PCM_FORMAT_S20_3BE,
|
||||||
SND_PCM_FORMAT_U20_3LE,
|
//SND_PCM_FORMAT_U20_3LE,
|
||||||
SND_PCM_FORMAT_U20_3BE,
|
//SND_PCM_FORMAT_U20_3BE,
|
||||||
SND_PCM_FORMAT_S18_3LE,
|
//SND_PCM_FORMAT_S18_3LE,
|
||||||
SND_PCM_FORMAT_S18_3BE,
|
//SND_PCM_FORMAT_S18_3BE,
|
||||||
SND_PCM_FORMAT_U18_3LE,
|
//SND_PCM_FORMAT_U18_3LE,
|
||||||
SND_PCM_FORMAT_U18_3BE,*/,
|
//SND_PCM_FORMAT_U18_3BE,
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut supported_formats = Vec::new();
|
let mut supported_formats = Vec::new();
|
||||||
for &(sample_format, alsa_format) in FORMATS.iter() {
|
for &(sample_format, alsa_format) in FORMATS.iter() {
|
||||||
if alsa::snd_pcm_hw_params_test_format(handle,
|
if alsa::snd_pcm_hw_params_test_format(handle, hw_params.0, alsa_format) == 0 {
|
||||||
hw_params.0,
|
|
||||||
alsa_format) == 0
|
|
||||||
{
|
|
||||||
supported_formats.push(sample_format);
|
supported_formats.push(sample_format);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -357,34 +344,19 @@ impl Device {
|
||||||
return Err(err.into());
|
return Err(err.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let sample_rates = if min_rate == max_rate ||
|
let sample_rates = if min_rate == max_rate
|
||||||
alsa::snd_pcm_hw_params_test_rate(handle, hw_params.0, min_rate + 1, 0) == 0
|
|| alsa::snd_pcm_hw_params_test_rate(handle, hw_params.0, min_rate + 1, 0) == 0
|
||||||
{
|
{
|
||||||
vec![(min_rate, max_rate)]
|
vec![(min_rate, max_rate)]
|
||||||
} else {
|
} else {
|
||||||
const RATES: [libc::c_uint; 13] = [
|
const RATES: [libc::c_uint; 13] = [
|
||||||
5512,
|
5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, 88200, 96000, 176400,
|
||||||
8000,
|
|
||||||
11025,
|
|
||||||
16000,
|
|
||||||
22050,
|
|
||||||
32000,
|
|
||||||
44100,
|
|
||||||
48000,
|
|
||||||
64000,
|
|
||||||
88200,
|
|
||||||
96000,
|
|
||||||
176400,
|
|
||||||
192000,
|
192000,
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut rates = Vec::new();
|
let mut rates = Vec::new();
|
||||||
for &rate in RATES.iter() {
|
for &rate in RATES.iter() {
|
||||||
if alsa::snd_pcm_hw_params_test_rate(handle,
|
if alsa::snd_pcm_hw_params_test_rate(handle, hw_params.0, rate, 0) == 0 {
|
||||||
hw_params.0,
|
|
||||||
rate,
|
|
||||||
0) == 0
|
|
||||||
{
|
|
||||||
rates.push((rate, rate));
|
rates.push((rate, rate));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -397,44 +369,48 @@ impl Device {
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut min_channels = 0;
|
let mut min_channels = 0;
|
||||||
if let Err(desc) = check_errors(alsa::snd_pcm_hw_params_get_channels_min(hw_params.0, &mut min_channels)) {
|
if let Err(desc) = check_errors(alsa::snd_pcm_hw_params_get_channels_min(
|
||||||
|
hw_params.0,
|
||||||
|
&mut min_channels,
|
||||||
|
)) {
|
||||||
let description = format!("unable to get minimum supported channel count: {}", desc);
|
let description = format!("unable to get minimum supported channel count: {}", desc);
|
||||||
let err = BackendSpecificError { description };
|
let err = BackendSpecificError { description };
|
||||||
return Err(err.into());
|
return Err(err.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut max_channels = 0;
|
let mut max_channels = 0;
|
||||||
if let Err(desc) = check_errors(alsa::snd_pcm_hw_params_get_channels_max(hw_params.0, &mut max_channels)) {
|
if let Err(desc) = check_errors(alsa::snd_pcm_hw_params_get_channels_max(
|
||||||
|
hw_params.0,
|
||||||
|
&mut max_channels,
|
||||||
|
)) {
|
||||||
let description = format!("unable to get maximum supported channel count: {}", desc);
|
let description = format!("unable to get maximum supported channel count: {}", desc);
|
||||||
let err = BackendSpecificError { description };
|
let err = BackendSpecificError { description };
|
||||||
return Err(err.into());
|
return Err(err.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let max_channels = cmp::min(max_channels, 32); // TODO: limiting to 32 channels or too much stuff is returned
|
let max_channels = cmp::min(max_channels, 32); // TODO: limiting to 32 channels or too much stuff is returned
|
||||||
let supported_channels = (min_channels .. max_channels + 1)
|
let supported_channels = (min_channels..max_channels + 1)
|
||||||
.filter_map(|num| if alsa::snd_pcm_hw_params_test_channels(
|
.filter_map(|num| {
|
||||||
handle,
|
if alsa::snd_pcm_hw_params_test_channels(handle, hw_params.0, num) == 0 {
|
||||||
hw_params.0,
|
Some(num as ChannelCount)
|
||||||
num,
|
} else {
|
||||||
) == 0
|
None
|
||||||
{
|
}
|
||||||
Some(num as ChannelCount)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let mut output = Vec::with_capacity(supported_formats.len() * supported_channels.len() *
|
let mut output = Vec::with_capacity(
|
||||||
sample_rates.len());
|
supported_formats.len() * supported_channels.len() * sample_rates.len(),
|
||||||
|
);
|
||||||
for &data_type in supported_formats.iter() {
|
for &data_type in supported_formats.iter() {
|
||||||
for channels in supported_channels.iter() {
|
for channels in supported_channels.iter() {
|
||||||
for &(min_rate, max_rate) in sample_rates.iter() {
|
for &(min_rate, max_rate) in sample_rates.iter() {
|
||||||
output.push(SupportedFormat {
|
output.push(SupportedFormat {
|
||||||
channels: channels.clone(),
|
channels: channels.clone(),
|
||||||
min_sample_rate: SampleRate(min_rate as u32),
|
min_sample_rate: SampleRate(min_rate as u32),
|
||||||
max_sample_rate: SampleRate(max_rate as u32),
|
max_sample_rate: SampleRate(max_rate as u32),
|
||||||
data_type: data_type,
|
data_type: data_type,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -445,15 +421,11 @@ impl Device {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn supported_input_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> {
|
fn supported_input_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> {
|
||||||
unsafe {
|
unsafe { self.supported_formats(alsa::SND_PCM_STREAM_CAPTURE) }
|
||||||
self.supported_formats(alsa::SND_PCM_STREAM_CAPTURE)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn supported_output_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
|
fn supported_output_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
|
||||||
unsafe {
|
unsafe { self.supported_formats(alsa::SND_PCM_STREAM_PLAYBACK) }
|
||||||
self.supported_formats(alsa::SND_PCM_STREAM_PLAYBACK)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ALSA does not offer default stream formats, so instead we compare all supported formats by
|
// ALSA does not offer default stream formats, so instead we compare all supported formats by
|
||||||
|
@ -461,13 +433,12 @@ impl Device {
|
||||||
fn default_format(
|
fn default_format(
|
||||||
&self,
|
&self,
|
||||||
stream_t: alsa::snd_pcm_stream_t,
|
stream_t: alsa::snd_pcm_stream_t,
|
||||||
) -> Result<Format, DefaultFormatError>
|
) -> Result<Format, DefaultFormatError> {
|
||||||
{
|
|
||||||
let mut formats: Vec<_> = unsafe {
|
let mut formats: Vec<_> = unsafe {
|
||||||
match self.supported_formats(stream_t) {
|
match self.supported_formats(stream_t) {
|
||||||
Err(SupportedFormatsError::DeviceNotAvailable) => {
|
Err(SupportedFormatsError::DeviceNotAvailable) => {
|
||||||
return Err(DefaultFormatError::DeviceNotAvailable);
|
return Err(DefaultFormatError::DeviceNotAvailable);
|
||||||
},
|
}
|
||||||
Err(SupportedFormatsError::InvalidArgument) => {
|
Err(SupportedFormatsError::InvalidArgument) => {
|
||||||
// this happens sometimes when querying for input and output capabilities but
|
// this happens sometimes when querying for input and output capabilities but
|
||||||
// the device supports only one
|
// the device supports only one
|
||||||
|
@ -492,8 +463,8 @@ impl Device {
|
||||||
format.sample_rate = HZ_44100;
|
format.sample_rate = HZ_44100;
|
||||||
}
|
}
|
||||||
Ok(format)
|
Ok(format)
|
||||||
},
|
}
|
||||||
None => Err(DefaultFormatError::StreamTypeNotSupported)
|
None => Err(DefaultFormatError::StreamTypeNotSupported),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -536,7 +507,10 @@ unsafe impl Send for StreamInner {}
|
||||||
unsafe impl Sync for StreamInner {}
|
unsafe impl Sync for StreamInner {}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
enum StreamType { Input, Output }
|
enum StreamType {
|
||||||
|
Input,
|
||||||
|
Output,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Stream {
|
pub struct Stream {
|
||||||
/// The high-priority audio processing thread calling callbacks.
|
/// The high-priority audio processing thread calling callbacks.
|
||||||
|
@ -567,7 +541,10 @@ fn input_stream_worker(
|
||||||
match poll_descriptors_and_prepare_buffer(&rx, stream, &mut ctxt, error_callback) {
|
match poll_descriptors_and_prepare_buffer(&rx, stream, &mut ctxt, error_callback) {
|
||||||
PollDescriptorsFlow::Continue => continue,
|
PollDescriptorsFlow::Continue => continue,
|
||||||
PollDescriptorsFlow::Return => return,
|
PollDescriptorsFlow::Return => return,
|
||||||
PollDescriptorsFlow::Ready { available_frames, stream_type } => {
|
PollDescriptorsFlow::Ready {
|
||||||
|
available_frames,
|
||||||
|
stream_type,
|
||||||
|
} => {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stream_type,
|
stream_type,
|
||||||
StreamType::Input,
|
StreamType::Input,
|
||||||
|
@ -596,7 +573,10 @@ fn output_stream_worker(
|
||||||
match poll_descriptors_and_prepare_buffer(&rx, stream, &mut ctxt, error_callback) {
|
match poll_descriptors_and_prepare_buffer(&rx, stream, &mut ctxt, error_callback) {
|
||||||
PollDescriptorsFlow::Continue => continue,
|
PollDescriptorsFlow::Continue => continue,
|
||||||
PollDescriptorsFlow::Return => return,
|
PollDescriptorsFlow::Return => return,
|
||||||
PollDescriptorsFlow::Ready { available_frames, stream_type } => {
|
PollDescriptorsFlow::Ready {
|
||||||
|
available_frames,
|
||||||
|
stream_type,
|
||||||
|
} => {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stream_type,
|
stream_type,
|
||||||
StreamType::Output,
|
StreamType::Output,
|
||||||
|
@ -620,7 +600,7 @@ enum PollDescriptorsFlow {
|
||||||
Ready {
|
Ready {
|
||||||
stream_type: StreamType,
|
stream_type: StreamType,
|
||||||
available_frames: usize,
|
available_frames: usize,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// This block is shared between both input and output stream worker functions.
|
// This block is shared between both input and output stream worker functions.
|
||||||
|
@ -661,7 +641,11 @@ fn poll_descriptors_and_prepare_buffer(
|
||||||
|
|
||||||
let res = unsafe {
|
let res = unsafe {
|
||||||
// Don't timeout, wait forever.
|
// Don't timeout, wait forever.
|
||||||
libc::poll(descriptors.as_mut_ptr(), descriptors.len() as libc::nfds_t, -1)
|
libc::poll(
|
||||||
|
descriptors.as_mut_ptr(),
|
||||||
|
descriptors.len() as libc::nfds_t,
|
||||||
|
-1,
|
||||||
|
)
|
||||||
};
|
};
|
||||||
if res < 0 {
|
if res < 0 {
|
||||||
let description = format!("`libc::poll()` failed: {}", io::Error::last_os_error());
|
let description = format!("`libc::poll()` failed: {}", io::Error::last_os_error());
|
||||||
|
@ -684,7 +668,7 @@ fn poll_descriptors_and_prepare_buffer(
|
||||||
Ok(None) => {
|
Ok(None) => {
|
||||||
// Nothing to process, poll again
|
// Nothing to process, poll again
|
||||||
return PollDescriptorsFlow::Continue;
|
return PollDescriptorsFlow::Continue;
|
||||||
},
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error_callback(err.into());
|
error_callback(err.into());
|
||||||
return PollDescriptorsFlow::Continue;
|
return PollDescriptorsFlow::Continue;
|
||||||
|
@ -739,9 +723,7 @@ fn process_input(
|
||||||
let sample_format = stream.sample_format;
|
let sample_format = stream.sample_format;
|
||||||
let data = buffer.as_mut_ptr() as *mut ();
|
let data = buffer.as_mut_ptr() as *mut ();
|
||||||
let len = buffer.len() / sample_format.sample_size();
|
let len = buffer.len() / sample_format.sample_size();
|
||||||
let data = unsafe {
|
let data = unsafe { Data::from_parts(data, len, sample_format) };
|
||||||
Data::from_parts(data, len, sample_format)
|
|
||||||
};
|
|
||||||
data_callback(&data);
|
data_callback(&data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -760,9 +742,7 @@ fn process_output(
|
||||||
let sample_format = stream.sample_format;
|
let sample_format = stream.sample_format;
|
||||||
let data = buffer.as_mut_ptr() as *mut ();
|
let data = buffer.as_mut_ptr() as *mut ();
|
||||||
let len = buffer.len() / sample_format.sample_size();
|
let len = buffer.len() / sample_format.sample_size();
|
||||||
let mut data = unsafe {
|
let mut data = unsafe { Data::from_parts(data, len, sample_format) };
|
||||||
Data::from_parts(data, len, sample_format)
|
|
||||||
};
|
|
||||||
data_callback(&mut data);
|
data_callback(&mut data);
|
||||||
}
|
}
|
||||||
loop {
|
loop {
|
||||||
|
@ -784,9 +764,8 @@ fn process_output(
|
||||||
} else if result as usize != available_frames {
|
} else if result as usize != available_frames {
|
||||||
let description = format!(
|
let description = format!(
|
||||||
"unexpected number of frames written: expected {}, \
|
"unexpected number of frames written: expected {}, \
|
||||||
result {} (this should never happen)",
|
result {} (this should never happen)",
|
||||||
available_frames,
|
available_frames, result,
|
||||||
result,
|
|
||||||
);
|
);
|
||||||
error_callback(BackendSpecificError { description }.into());
|
error_callback(BackendSpecificError { description }.into());
|
||||||
continue;
|
continue;
|
||||||
|
@ -857,7 +836,7 @@ impl StreamTrait for Stream {
|
||||||
// TODO: error handling
|
// TODO: error handling
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn pause(&self)-> Result<(), PauseStreamError> {
|
fn pause(&self) -> Result<(), PauseStreamError> {
|
||||||
unsafe {
|
unsafe {
|
||||||
alsa::snd_pcm_pause(self.inner.channel, 1);
|
alsa::snd_pcm_pause(self.inner.channel, 1);
|
||||||
}
|
}
|
||||||
|
@ -888,8 +867,7 @@ fn check_for_pollout_or_pollin(
|
||||||
(revent, res)
|
(revent, res)
|
||||||
};
|
};
|
||||||
if let Err(desc) = check_errors(res) {
|
if let Err(desc) = check_errors(res) {
|
||||||
let description =
|
let description = format!("`snd_pcm_poll_descriptors_revents` failed: {}", desc);
|
||||||
format!("`snd_pcm_poll_descriptors_revents` failed: {}",desc);
|
|
||||||
let err = BackendSpecificError { description };
|
let err = BackendSpecificError { description };
|
||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
|
@ -905,9 +883,7 @@ fn check_for_pollout_or_pollin(
|
||||||
|
|
||||||
// Determine the number of samples that are available to read/write.
|
// Determine the number of samples that are available to read/write.
|
||||||
fn get_available_samples(stream: &StreamInner) -> Result<usize, BackendSpecificError> {
|
fn get_available_samples(stream: &StreamInner) -> Result<usize, BackendSpecificError> {
|
||||||
let available = unsafe {
|
let available = unsafe { alsa::snd_pcm_avail_update(stream.channel) };
|
||||||
alsa::snd_pcm_avail_update(stream.channel)
|
|
||||||
};
|
|
||||||
if available == -32 {
|
if available == -32 {
|
||||||
// buffer underrun
|
// buffer underrun
|
||||||
// TODO: Notify the user some how.
|
// TODO: Notify the user some how.
|
||||||
|
@ -929,9 +905,11 @@ unsafe fn set_hw_params_from_format(
|
||||||
if let Err(e) = check_errors(alsa::snd_pcm_hw_params_any(pcm_handle, hw_params.0)) {
|
if let Err(e) = check_errors(alsa::snd_pcm_hw_params_any(pcm_handle, hw_params.0)) {
|
||||||
return Err(format!("errors on pcm handle: {}", e));
|
return Err(format!("errors on pcm handle: {}", e));
|
||||||
}
|
}
|
||||||
if let Err(e) = check_errors(alsa::snd_pcm_hw_params_set_access(pcm_handle,
|
if let Err(e) = check_errors(alsa::snd_pcm_hw_params_set_access(
|
||||||
hw_params.0,
|
pcm_handle,
|
||||||
alsa::SND_PCM_ACCESS_RW_INTERLEAVED)) {
|
hw_params.0,
|
||||||
|
alsa::SND_PCM_ACCESS_RW_INTERLEAVED,
|
||||||
|
)) {
|
||||||
return Err(format!("handle not acessible: {}", e));
|
return Err(format!("handle not acessible: {}", e));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -949,21 +927,26 @@ unsafe fn set_hw_params_from_format(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = check_errors(alsa::snd_pcm_hw_params_set_format(pcm_handle,
|
if let Err(e) = check_errors(alsa::snd_pcm_hw_params_set_format(
|
||||||
hw_params.0,
|
pcm_handle,
|
||||||
data_type)) {
|
hw_params.0,
|
||||||
|
data_type,
|
||||||
|
)) {
|
||||||
return Err(format!("format could not be set: {}", e));
|
return Err(format!("format could not be set: {}", e));
|
||||||
}
|
}
|
||||||
if let Err(e) = check_errors(alsa::snd_pcm_hw_params_set_rate(pcm_handle,
|
if let Err(e) = check_errors(alsa::snd_pcm_hw_params_set_rate(
|
||||||
hw_params.0,
|
pcm_handle,
|
||||||
format.sample_rate.0 as libc::c_uint,
|
hw_params.0,
|
||||||
0)) {
|
format.sample_rate.0 as libc::c_uint,
|
||||||
|
0,
|
||||||
|
)) {
|
||||||
return Err(format!("sample rate could not be set: {}", e));
|
return Err(format!("sample rate could not be set: {}", e));
|
||||||
}
|
}
|
||||||
if let Err(e) = check_errors(alsa::snd_pcm_hw_params_set_channels(pcm_handle,
|
if let Err(e) = check_errors(alsa::snd_pcm_hw_params_set_channels(
|
||||||
hw_params.0,
|
pcm_handle,
|
||||||
format.channels as
|
hw_params.0,
|
||||||
libc::c_uint)) {
|
format.channels as libc::c_uint,
|
||||||
|
)) {
|
||||||
return Err(format!("channel count could not be set: {}", e));
|
return Err(format!("channel count could not be set: {}", e));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -987,8 +970,7 @@ unsafe fn set_hw_params_from_format(
|
||||||
unsafe fn set_sw_params_from_format(
|
unsafe fn set_sw_params_from_format(
|
||||||
pcm_handle: *mut alsa::snd_pcm_t,
|
pcm_handle: *mut alsa::snd_pcm_t,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
) -> Result<(usize, usize), String>
|
) -> Result<(usize, usize), String> {
|
||||||
{
|
|
||||||
let mut sw_params = ptr::null_mut(); // TODO: RAII
|
let mut sw_params = ptr::null_mut(); // TODO: RAII
|
||||||
if let Err(e) = check_errors(alsa::snd_pcm_sw_params_malloc(&mut sw_params)) {
|
if let Err(e) = check_errors(alsa::snd_pcm_sw_params_malloc(&mut sw_params)) {
|
||||||
return Err(format!("snd_pcm_sw_params_malloc failed: {}", e));
|
return Err(format!("snd_pcm_sw_params_malloc failed: {}", e));
|
||||||
|
@ -996,20 +978,31 @@ unsafe fn set_sw_params_from_format(
|
||||||
if let Err(e) = check_errors(alsa::snd_pcm_sw_params_current(pcm_handle, sw_params)) {
|
if let Err(e) = check_errors(alsa::snd_pcm_sw_params_current(pcm_handle, sw_params)) {
|
||||||
return Err(format!("snd_pcm_sw_params_current failed: {}", e));
|
return Err(format!("snd_pcm_sw_params_current failed: {}", e));
|
||||||
}
|
}
|
||||||
if let Err(e) = check_errors(alsa::snd_pcm_sw_params_set_start_threshold(pcm_handle, sw_params, 0)) {
|
if let Err(e) = check_errors(alsa::snd_pcm_sw_params_set_start_threshold(
|
||||||
return Err(format!("snd_pcm_sw_params_set_start_threshold failed: {}", e));
|
pcm_handle, sw_params, 0,
|
||||||
|
)) {
|
||||||
|
return Err(format!(
|
||||||
|
"snd_pcm_sw_params_set_start_threshold failed: {}",
|
||||||
|
e
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (buffer_len, period_len) = {
|
let (buffer_len, period_len) = {
|
||||||
let mut buffer = 0;
|
let mut buffer = 0;
|
||||||
let mut period = 0;
|
let mut period = 0;
|
||||||
if let Err(e) = check_errors(alsa::snd_pcm_get_params(pcm_handle, &mut buffer, &mut period)) {
|
if let Err(e) = check_errors(alsa::snd_pcm_get_params(
|
||||||
|
pcm_handle,
|
||||||
|
&mut buffer,
|
||||||
|
&mut period,
|
||||||
|
)) {
|
||||||
return Err(format!("failed to initialize buffer: {}", e));
|
return Err(format!("failed to initialize buffer: {}", e));
|
||||||
}
|
}
|
||||||
if buffer == 0 {
|
if buffer == 0 {
|
||||||
return Err(format!("initialization resulted in a null buffer"));
|
return Err(format!("initialization resulted in a null buffer"));
|
||||||
}
|
}
|
||||||
if let Err(e) = check_errors(alsa::snd_pcm_sw_params_set_avail_min(pcm_handle, sw_params, period)) {
|
if let Err(e) = check_errors(alsa::snd_pcm_sw_params_set_avail_min(
|
||||||
|
pcm_handle, sw_params, period,
|
||||||
|
)) {
|
||||||
return Err(format!("snd_pcm_sw_params_set_avail_min failed: {}", e));
|
return Err(format!("snd_pcm_sw_params_set_avail_min failed: {}", e));
|
||||||
}
|
}
|
||||||
let buffer = buffer as usize * format.channels as usize;
|
let buffer = buffer as usize * format.channels as usize;
|
||||||
|
|
|
@ -2,8 +2,10 @@ use std;
|
||||||
pub type SupportedInputFormats = std::vec::IntoIter<SupportedFormat>;
|
pub type SupportedInputFormats = std::vec::IntoIter<SupportedFormat>;
|
||||||
pub type SupportedOutputFormats = std::vec::IntoIter<SupportedFormat>;
|
pub type SupportedOutputFormats = std::vec::IntoIter<SupportedFormat>;
|
||||||
|
|
||||||
|
use super::parking_lot::Mutex;
|
||||||
|
use super::sys;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::sync::{Arc};
|
use std::sync::Arc;
|
||||||
use BackendSpecificError;
|
use BackendSpecificError;
|
||||||
use DefaultFormatError;
|
use DefaultFormatError;
|
||||||
use DeviceNameError;
|
use DeviceNameError;
|
||||||
|
@ -13,8 +15,6 @@ use SampleFormat;
|
||||||
use SampleRate;
|
use SampleRate;
|
||||||
use SupportedFormat;
|
use SupportedFormat;
|
||||||
use SupportedFormatsError;
|
use SupportedFormatsError;
|
||||||
use super::sys;
|
|
||||||
use super::parking_lot::Mutex;
|
|
||||||
|
|
||||||
/// A ASIO Device
|
/// A ASIO Device
|
||||||
pub struct Device {
|
pub struct Device {
|
||||||
|
@ -55,9 +55,7 @@ impl Device {
|
||||||
/// Gets the supported input formats.
|
/// Gets the supported input formats.
|
||||||
/// TODO currently only supports the default.
|
/// TODO currently only supports the default.
|
||||||
/// Need to find all possible formats.
|
/// Need to find all possible formats.
|
||||||
pub fn supported_input_formats(
|
pub fn supported_input_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> {
|
||||||
&self,
|
|
||||||
) -> Result<SupportedInputFormats, SupportedFormatsError> {
|
|
||||||
// Retrieve the default format for the total supported channels and supported sample
|
// Retrieve the default format for the total supported channels and supported sample
|
||||||
// format.
|
// format.
|
||||||
let mut f = match self.default_input_format() {
|
let mut f = match self.default_input_format() {
|
||||||
|
@ -68,7 +66,12 @@ impl Device {
|
||||||
// Collect a format for every combination of supported sample rate and number of channels.
|
// Collect a format for every combination of supported sample rate and number of channels.
|
||||||
let mut supported_formats = vec![];
|
let mut supported_formats = vec![];
|
||||||
for &rate in ::COMMON_SAMPLE_RATES {
|
for &rate in ::COMMON_SAMPLE_RATES {
|
||||||
if !self.driver.can_sample_rate(rate.0.into()).ok().unwrap_or(false) {
|
if !self
|
||||||
|
.driver
|
||||||
|
.can_sample_rate(rate.0.into())
|
||||||
|
.ok()
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for channels in 1..f.channels + 1 {
|
for channels in 1..f.channels + 1 {
|
||||||
|
@ -96,7 +99,12 @@ impl Device {
|
||||||
// Collect a format for every combination of supported sample rate and number of channels.
|
// Collect a format for every combination of supported sample rate and number of channels.
|
||||||
let mut supported_formats = vec![];
|
let mut supported_formats = vec![];
|
||||||
for &rate in ::COMMON_SAMPLE_RATES {
|
for &rate in ::COMMON_SAMPLE_RATES {
|
||||||
if !self.driver.can_sample_rate(rate.0.into()).ok().unwrap_or(false) {
|
if !self
|
||||||
|
.driver
|
||||||
|
.can_sample_rate(rate.0.into())
|
||||||
|
.ok()
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for channels in 1..f.channels + 1 {
|
for channels in 1..f.channels + 1 {
|
||||||
|
@ -114,8 +122,8 @@ impl Device {
|
||||||
let sample_rate = SampleRate(self.driver.sample_rate().map_err(default_format_err)? as _);
|
let sample_rate = SampleRate(self.driver.sample_rate().map_err(default_format_err)? as _);
|
||||||
// Map th ASIO sample type to a CPAL sample type
|
// Map th ASIO sample type to a CPAL sample type
|
||||||
let data_type = self.driver.input_data_type().map_err(default_format_err)?;
|
let data_type = self.driver.input_data_type().map_err(default_format_err)?;
|
||||||
let data_type = convert_data_type(&data_type)
|
let data_type =
|
||||||
.ok_or(DefaultFormatError::StreamTypeNotSupported)?;
|
convert_data_type(&data_type).ok_or(DefaultFormatError::StreamTypeNotSupported)?;
|
||||||
Ok(Format {
|
Ok(Format {
|
||||||
channels,
|
channels,
|
||||||
sample_rate,
|
sample_rate,
|
||||||
|
@ -128,8 +136,8 @@ impl Device {
|
||||||
let channels = self.driver.channels().map_err(default_format_err)?.outs as u16;
|
let channels = self.driver.channels().map_err(default_format_err)?.outs as u16;
|
||||||
let sample_rate = SampleRate(self.driver.sample_rate().map_err(default_format_err)? as _);
|
let sample_rate = SampleRate(self.driver.sample_rate().map_err(default_format_err)? as _);
|
||||||
let data_type = self.driver.output_data_type().map_err(default_format_err)?;
|
let data_type = self.driver.output_data_type().map_err(default_format_err)?;
|
||||||
let data_type = convert_data_type(&data_type)
|
let data_type =
|
||||||
.ok_or(DefaultFormatError::StreamTypeNotSupported)?;
|
convert_data_type(&data_type).ok_or(DefaultFormatError::StreamTypeNotSupported)?;
|
||||||
Ok(Format {
|
Ok(Format {
|
||||||
channels,
|
channels,
|
||||||
sample_rate,
|
sample_rate,
|
||||||
|
@ -159,10 +167,13 @@ impl Iterator for Devices {
|
||||||
input: None,
|
input: None,
|
||||||
output: None,
|
output: None,
|
||||||
}));
|
}));
|
||||||
return Some(Device { driver, asio_streams });
|
return Some(Device {
|
||||||
|
driver,
|
||||||
|
asio_streams,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
Err(_) => continue,
|
Err(_) => continue,
|
||||||
}
|
},
|
||||||
None => return None,
|
None => return None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -193,8 +204,9 @@ pub(crate) fn convert_data_type(ty: &sys::AsioSampleType) -> Option<SampleFormat
|
||||||
|
|
||||||
fn default_format_err(e: sys::AsioError) -> DefaultFormatError {
|
fn default_format_err(e: sys::AsioError) -> DefaultFormatError {
|
||||||
match e {
|
match e {
|
||||||
sys::AsioError::NoDrivers |
|
sys::AsioError::NoDrivers | sys::AsioError::HardwareMalfunction => {
|
||||||
sys::AsioError::HardwareMalfunction => DefaultFormatError::DeviceNotAvailable,
|
DefaultFormatError::DeviceNotAvailable
|
||||||
|
}
|
||||||
sys::AsioError::NoRate => DefaultFormatError::StreamTypeNotSupported,
|
sys::AsioError::NoRate => DefaultFormatError::StreamTypeNotSupported,
|
||||||
err => {
|
err => {
|
||||||
let description = format!("{}", err);
|
let description = format!("{}", err);
|
||||||
|
|
|
@ -2,22 +2,10 @@ extern crate asio_sys as sys;
|
||||||
extern crate parking_lot;
|
extern crate parking_lot;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
BuildStreamError,
|
BuildStreamError, Data, DefaultFormatError, DeviceNameError, DevicesError, Format,
|
||||||
Data,
|
PauseStreamError, PlayStreamError, StreamError, SupportedFormatsError,
|
||||||
DefaultFormatError,
|
|
||||||
DeviceNameError,
|
|
||||||
DevicesError,
|
|
||||||
Format,
|
|
||||||
PauseStreamError,
|
|
||||||
PlayStreamError,
|
|
||||||
StreamError,
|
|
||||||
SupportedFormatsError,
|
|
||||||
};
|
|
||||||
use traits::{
|
|
||||||
DeviceTrait,
|
|
||||||
HostTrait,
|
|
||||||
StreamTrait,
|
|
||||||
};
|
};
|
||||||
|
use traits::{DeviceTrait, HostTrait, StreamTrait};
|
||||||
|
|
||||||
pub use self::device::{Device, Devices, SupportedInputFormats, SupportedOutputFormats};
|
pub use self::device::{Device, Devices, SupportedInputFormats, SupportedOutputFormats};
|
||||||
pub use self::stream::Stream;
|
pub use self::stream::Stream;
|
||||||
|
@ -73,11 +61,15 @@ impl DeviceTrait for Device {
|
||||||
Device::name(self)
|
Device::name(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn supported_input_formats(&self) -> Result<Self::SupportedInputFormats, SupportedFormatsError> {
|
fn supported_input_formats(
|
||||||
|
&self,
|
||||||
|
) -> Result<Self::SupportedInputFormats, SupportedFormatsError> {
|
||||||
Device::supported_input_formats(self)
|
Device::supported_input_formats(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn supported_output_formats(&self) -> Result<Self::SupportedOutputFormats, SupportedFormatsError> {
|
fn supported_output_formats(
|
||||||
|
&self,
|
||||||
|
) -> Result<Self::SupportedOutputFormats, SupportedFormatsError> {
|
||||||
Device::supported_output_formats(self)
|
Device::supported_output_formats(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,7 +89,7 @@ impl DeviceTrait for Device {
|
||||||
) -> Result<Self::Stream, BuildStreamError>
|
) -> Result<Self::Stream, BuildStreamError>
|
||||||
where
|
where
|
||||||
D: FnMut(&Data) + Send + 'static,
|
D: FnMut(&Data) + Send + 'static,
|
||||||
E: FnMut(StreamError) + Send + 'static
|
E: FnMut(StreamError) + Send + 'static,
|
||||||
{
|
{
|
||||||
Device::build_input_stream(self, format, data_callback, error_callback)
|
Device::build_input_stream(self, format, data_callback, error_callback)
|
||||||
}
|
}
|
||||||
|
@ -110,7 +102,7 @@ impl DeviceTrait for Device {
|
||||||
) -> Result<Self::Stream, BuildStreamError>
|
) -> Result<Self::Stream, BuildStreamError>
|
||||||
where
|
where
|
||||||
D: FnMut(&mut Data) + Send + 'static,
|
D: FnMut(&mut Data) + Send + 'static,
|
||||||
E: FnMut(StreamError) + Send + 'static
|
E: FnMut(StreamError) + Send + 'static,
|
||||||
{
|
{
|
||||||
Device::build_output_stream(self, format, data_callback, error_callback)
|
Device::build_output_stream(self, format, data_callback, error_callback)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,11 @@ extern crate asio_sys as sys;
|
||||||
extern crate num_traits;
|
extern crate num_traits;
|
||||||
|
|
||||||
use self::num_traits::PrimInt;
|
use self::num_traits::PrimInt;
|
||||||
|
use super::parking_lot::Mutex;
|
||||||
use super::Device;
|
use super::Device;
|
||||||
use std;
|
use std;
|
||||||
use std::sync::atomic::{Ordering, AtomicBool};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use super::parking_lot::Mutex;
|
|
||||||
use BackendSpecificError;
|
use BackendSpecificError;
|
||||||
use BuildStreamError;
|
use BuildStreamError;
|
||||||
use Data;
|
use Data;
|
||||||
|
@ -93,7 +93,7 @@ impl Device {
|
||||||
let callback_id = self.driver.add_callback(move |buffer_index| unsafe {
|
let callback_id = self.driver.add_callback(move |buffer_index| unsafe {
|
||||||
// If not playing return early.
|
// If not playing return early.
|
||||||
if !playing.load(Ordering::SeqCst) {
|
if !playing.load(Ordering::SeqCst) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// There is 0% chance of lock contention the host only locks when recreating streams.
|
// There is 0% chance of lock contention the host only locks when recreating streams.
|
||||||
|
@ -111,8 +111,7 @@ impl Device {
|
||||||
asio_stream: &sys::AsioStream,
|
asio_stream: &sys::AsioStream,
|
||||||
buffer_index: usize,
|
buffer_index: usize,
|
||||||
from_endianness: F,
|
from_endianness: F,
|
||||||
)
|
) where
|
||||||
where
|
|
||||||
A: AsioSample,
|
A: AsioSample,
|
||||||
B: Sample,
|
B: Sample,
|
||||||
D: FnMut(&Data) + Send + 'static,
|
D: FnMut(&Data) + Send + 'static,
|
||||||
|
@ -157,8 +156,8 @@ impl Device {
|
||||||
|
|
||||||
// TODO: Handle endianness conversion for floats? We currently use the `PrimInt`
|
// TODO: Handle endianness conversion for floats? We currently use the `PrimInt`
|
||||||
// trait for the `to_le` and `to_be` methods, but this does not support floats.
|
// trait for the `to_le` and `to_be` methods, but this does not support floats.
|
||||||
(&sys::AsioSampleType::ASIOSTFloat32LSB, SampleFormat::F32) |
|
(&sys::AsioSampleType::ASIOSTFloat32LSB, SampleFormat::F32)
|
||||||
(&sys::AsioSampleType::ASIOSTFloat32MSB, SampleFormat::F32) => {
|
| (&sys::AsioSampleType::ASIOSTFloat32MSB, SampleFormat::F32) => {
|
||||||
process_input_callback::<f32, f32, _, _>(
|
process_input_callback::<f32, f32, _, _>(
|
||||||
&mut data_callback,
|
&mut data_callback,
|
||||||
&mut interleaved,
|
&mut interleaved,
|
||||||
|
@ -191,8 +190,8 @@ impl Device {
|
||||||
}
|
}
|
||||||
// TODO: Handle endianness conversion for floats? We currently use the `PrimInt`
|
// TODO: Handle endianness conversion for floats? We currently use the `PrimInt`
|
||||||
// trait for the `to_le` and `to_be` methods, but this does not support floats.
|
// trait for the `to_le` and `to_be` methods, but this does not support floats.
|
||||||
(&sys::AsioSampleType::ASIOSTFloat64LSB, SampleFormat::F32) |
|
(&sys::AsioSampleType::ASIOSTFloat64LSB, SampleFormat::F32)
|
||||||
(&sys::AsioSampleType::ASIOSTFloat64MSB, SampleFormat::F32) => {
|
| (&sys::AsioSampleType::ASIOSTFloat64MSB, SampleFormat::F32) => {
|
||||||
process_input_callback::<f64, f32, _, _>(
|
process_input_callback::<f64, f32, _, _>(
|
||||||
&mut data_callback,
|
&mut data_callback,
|
||||||
&mut interleaved,
|
&mut interleaved,
|
||||||
|
@ -202,10 +201,11 @@ impl Device {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsupported_format_pair => {
|
unsupported_format_pair => unreachable!(
|
||||||
unreachable!("`build_input_stream` should have returned with unsupported \
|
"`build_input_stream` should have returned with unsupported \
|
||||||
format {:?}", unsupported_format_pair)
|
format {:?}",
|
||||||
}
|
unsupported_format_pair
|
||||||
|
),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -258,7 +258,7 @@ impl Device {
|
||||||
let callback_id = self.driver.add_callback(move |buffer_index| unsafe {
|
let callback_id = self.driver.add_callback(move |buffer_index| unsafe {
|
||||||
// If not playing, return early.
|
// If not playing, return early.
|
||||||
if !playing.load(Ordering::SeqCst) {
|
if !playing.load(Ordering::SeqCst) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// There is 0% chance of lock contention the host only locks when recreating streams.
|
// There is 0% chance of lock contention the host only locks when recreating streams.
|
||||||
|
@ -301,8 +301,7 @@ impl Device {
|
||||||
asio_stream: &sys::AsioStream,
|
asio_stream: &sys::AsioStream,
|
||||||
buffer_index: usize,
|
buffer_index: usize,
|
||||||
to_endianness: F,
|
to_endianness: F,
|
||||||
)
|
) where
|
||||||
where
|
|
||||||
A: Sample,
|
A: Sample,
|
||||||
B: AsioSample,
|
B: AsioSample,
|
||||||
D: FnMut(&mut Data) + Send + 'static,
|
D: FnMut(&mut Data) + Send + 'static,
|
||||||
|
@ -321,7 +320,9 @@ impl Device {
|
||||||
for ch_ix in 0..n_channels {
|
for ch_ix in 0..n_channels {
|
||||||
let asio_channel =
|
let asio_channel =
|
||||||
asio_channel_slice_mut::<B>(asio_stream, buffer_index, ch_ix);
|
asio_channel_slice_mut::<B>(asio_stream, buffer_index, ch_ix);
|
||||||
asio_channel.iter_mut().for_each(|s| *s = to_endianness(B::SILENCE));
|
asio_channel
|
||||||
|
.iter_mut()
|
||||||
|
.for_each(|s| *s = to_endianness(B::SILENCE));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -359,8 +360,8 @@ impl Device {
|
||||||
|
|
||||||
// TODO: Handle endianness conversion for floats? We currently use the `PrimInt`
|
// TODO: Handle endianness conversion for floats? We currently use the `PrimInt`
|
||||||
// trait for the `to_le` and `to_be` methods, but this does not support floats.
|
// trait for the `to_le` and `to_be` methods, but this does not support floats.
|
||||||
(SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat32LSB) |
|
(SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat32LSB)
|
||||||
(SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat32MSB) => {
|
| (SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat32MSB) => {
|
||||||
process_output_callback::<f32, f32, _, _>(
|
process_output_callback::<f32, f32, _, _>(
|
||||||
&mut data_callback,
|
&mut data_callback,
|
||||||
&mut interleaved,
|
&mut interleaved,
|
||||||
|
@ -396,8 +397,8 @@ impl Device {
|
||||||
}
|
}
|
||||||
// TODO: Handle endianness conversion for floats? We currently use the `PrimInt`
|
// TODO: Handle endianness conversion for floats? We currently use the `PrimInt`
|
||||||
// trait for the `to_le` and `to_be` methods, but this does not support floats.
|
// trait for the `to_le` and `to_be` methods, but this does not support floats.
|
||||||
(SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat64LSB) |
|
(SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat64LSB)
|
||||||
(SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat64MSB) => {
|
| (SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat64MSB) => {
|
||||||
process_output_callback::<f32, f64, _, _>(
|
process_output_callback::<f32, f64, _, _>(
|
||||||
&mut data_callback,
|
&mut data_callback,
|
||||||
&mut interleaved,
|
&mut interleaved,
|
||||||
|
@ -408,10 +409,11 @@ impl Device {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsupported_format_pair => {
|
unsupported_format_pair => unreachable!(
|
||||||
unreachable!("`build_output_stream` should have returned with unsupported \
|
"`build_output_stream` should have returned with unsupported \
|
||||||
format {:?}", unsupported_format_pair)
|
format {:?}",
|
||||||
}
|
unsupported_format_pair
|
||||||
|
),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -434,15 +436,12 @@ impl Device {
|
||||||
/// If there is no existing ASIO Input Stream it will be created.
|
/// If there is no existing ASIO Input Stream it will be created.
|
||||||
///
|
///
|
||||||
/// On success, the buffer size of the stream is returned.
|
/// On success, the buffer size of the stream is returned.
|
||||||
fn get_or_create_input_stream(
|
fn get_or_create_input_stream(&self, format: &Format) -> Result<usize, BuildStreamError> {
|
||||||
&self,
|
|
||||||
format: &Format,
|
|
||||||
) -> Result<usize, BuildStreamError> {
|
|
||||||
match self.default_input_format() {
|
match self.default_input_format() {
|
||||||
Ok(f) => {
|
Ok(f) => {
|
||||||
let num_asio_channels = f.channels;
|
let num_asio_channels = f.channels;
|
||||||
check_format(&self.driver, format, num_asio_channels)
|
check_format(&self.driver, format, num_asio_channels)
|
||||||
},
|
}
|
||||||
Err(_) => Err(BuildStreamError::FormatNotSupported),
|
Err(_) => Err(BuildStreamError::FormatNotSupported),
|
||||||
}?;
|
}?;
|
||||||
let num_channels = format.channels as usize;
|
let num_channels = format.channels as usize;
|
||||||
|
@ -462,7 +461,8 @@ impl Device {
|
||||||
};
|
};
|
||||||
*streams = new_streams;
|
*streams = new_streams;
|
||||||
bs
|
bs
|
||||||
}).map_err(|ref e| {
|
})
|
||||||
|
.map_err(|ref e| {
|
||||||
println!("Error preparing stream: {}", e);
|
println!("Error preparing stream: {}", e);
|
||||||
BuildStreamError::DeviceNotAvailable
|
BuildStreamError::DeviceNotAvailable
|
||||||
})
|
})
|
||||||
|
@ -473,15 +473,12 @@ impl Device {
|
||||||
/// Create a new CPAL Output Stream.
|
/// Create a new CPAL Output Stream.
|
||||||
///
|
///
|
||||||
/// If there is no existing ASIO Output Stream it will be created.
|
/// If there is no existing ASIO Output Stream it will be created.
|
||||||
fn get_or_create_output_stream(
|
fn get_or_create_output_stream(&self, format: &Format) -> Result<usize, BuildStreamError> {
|
||||||
&self,
|
|
||||||
format: &Format,
|
|
||||||
) -> Result<usize, BuildStreamError> {
|
|
||||||
match self.default_output_format() {
|
match self.default_output_format() {
|
||||||
Ok(f) => {
|
Ok(f) => {
|
||||||
let num_asio_channels = f.channels;
|
let num_asio_channels = f.channels;
|
||||||
check_format(&self.driver, format, num_asio_channels)
|
check_format(&self.driver, format, num_asio_channels)
|
||||||
},
|
}
|
||||||
Err(_) => Err(BuildStreamError::FormatNotSupported),
|
Err(_) => Err(BuildStreamError::FormatNotSupported),
|
||||||
}?;
|
}?;
|
||||||
let num_channels = format.channels as usize;
|
let num_channels = format.channels as usize;
|
||||||
|
@ -501,7 +498,8 @@ impl Device {
|
||||||
};
|
};
|
||||||
*streams = new_streams;
|
*streams = new_streams;
|
||||||
bs
|
bs
|
||||||
}).map_err(|ref e| {
|
})
|
||||||
|
.map_err(|ref e| {
|
||||||
println!("Error preparing stream: {}", e);
|
println!("Error preparing stream: {}", e);
|
||||||
BuildStreamError::DeviceNotAvailable
|
BuildStreamError::DeviceNotAvailable
|
||||||
})
|
})
|
||||||
|
@ -588,7 +586,10 @@ fn check_format(
|
||||||
// Try and set the sample rate to what the user selected.
|
// Try and set the sample rate to what the user selected.
|
||||||
let sample_rate = sample_rate.0.into();
|
let sample_rate = sample_rate.0.into();
|
||||||
if sample_rate != driver.sample_rate().map_err(build_stream_err)? {
|
if sample_rate != driver.sample_rate().map_err(build_stream_err)? {
|
||||||
if driver.can_sample_rate(sample_rate).map_err(build_stream_err)? {
|
if driver
|
||||||
|
.can_sample_rate(sample_rate)
|
||||||
|
.map_err(build_stream_err)?
|
||||||
|
{
|
||||||
driver
|
driver
|
||||||
.set_sample_rate(sample_rate)
|
.set_sample_rate(sample_rate)
|
||||||
.map_err(build_stream_err)?;
|
.map_err(build_stream_err)?;
|
||||||
|
@ -656,19 +657,17 @@ unsafe fn asio_channel_slice_mut<T>(
|
||||||
buffer_index: usize,
|
buffer_index: usize,
|
||||||
channel_index: usize,
|
channel_index: usize,
|
||||||
) -> &mut [T] {
|
) -> &mut [T] {
|
||||||
let buff_ptr: *mut T = asio_stream
|
let buff_ptr: *mut T =
|
||||||
.buffer_infos[channel_index]
|
asio_stream.buffer_infos[channel_index].buffers[buffer_index as usize] as *mut _;
|
||||||
.buffers[buffer_index as usize]
|
|
||||||
as *mut _;
|
|
||||||
std::slice::from_raw_parts_mut(buff_ptr, asio_stream.buffer_size as usize)
|
std::slice::from_raw_parts_mut(buff_ptr, asio_stream.buffer_size as usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_stream_err(e: sys::AsioError) -> BuildStreamError {
|
fn build_stream_err(e: sys::AsioError) -> BuildStreamError {
|
||||||
match e {
|
match e {
|
||||||
sys::AsioError::NoDrivers |
|
sys::AsioError::NoDrivers | sys::AsioError::HardwareMalfunction => {
|
||||||
sys::AsioError::HardwareMalfunction => BuildStreamError::DeviceNotAvailable,
|
BuildStreamError::DeviceNotAvailable
|
||||||
sys::AsioError::InvalidInput |
|
}
|
||||||
sys::AsioError::BadMode => BuildStreamError::InvalidArgument,
|
sys::AsioError::InvalidInput | sys::AsioError::BadMode => BuildStreamError::InvalidArgument,
|
||||||
err => {
|
err => {
|
||||||
let description = format!("{}", err);
|
let description = format!("{}", err);
|
||||||
BackendSpecificError { description }.into()
|
BackendSpecificError { description }.into()
|
||||||
|
|
|
@ -1,22 +1,15 @@
|
||||||
use {BackendSpecificError, DevicesError, SupportedFormat};
|
use super::coreaudio::sys::{
|
||||||
|
kAudioHardwareNoError, kAudioHardwarePropertyDefaultInputDevice,
|
||||||
|
kAudioHardwarePropertyDefaultOutputDevice, kAudioHardwarePropertyDevices,
|
||||||
|
kAudioObjectPropertyElementMaster, kAudioObjectPropertyScopeGlobal, kAudioObjectSystemObject,
|
||||||
|
AudioDeviceID, AudioObjectGetPropertyData, AudioObjectGetPropertyDataSize,
|
||||||
|
AudioObjectPropertyAddress, OSStatus,
|
||||||
|
};
|
||||||
|
use super::Device;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ptr::null;
|
use std::ptr::null;
|
||||||
use std::vec::IntoIter as VecIntoIter;
|
use std::vec::IntoIter as VecIntoIter;
|
||||||
use super::coreaudio::sys::{
|
use {BackendSpecificError, DevicesError, SupportedFormat};
|
||||||
AudioDeviceID,
|
|
||||||
AudioObjectPropertyAddress,
|
|
||||||
AudioObjectGetPropertyData,
|
|
||||||
AudioObjectGetPropertyDataSize,
|
|
||||||
kAudioHardwareNoError,
|
|
||||||
kAudioHardwarePropertyDefaultInputDevice,
|
|
||||||
kAudioHardwarePropertyDefaultOutputDevice,
|
|
||||||
kAudioHardwarePropertyDevices,
|
|
||||||
kAudioObjectPropertyElementMaster,
|
|
||||||
kAudioObjectPropertyScopeGlobal,
|
|
||||||
kAudioObjectSystemObject,
|
|
||||||
OSStatus,
|
|
||||||
};
|
|
||||||
use super::Device;
|
|
||||||
|
|
||||||
unsafe fn audio_devices() -> Result<Vec<AudioDeviceID>, OSStatus> {
|
unsafe fn audio_devices() -> Result<Vec<AudioDeviceID>, OSStatus> {
|
||||||
let property_address = AudioObjectPropertyAddress {
|
let property_address = AudioObjectPropertyAddress {
|
||||||
|
@ -80,15 +73,15 @@ impl Devices {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for Devices {
|
unsafe impl Send for Devices {}
|
||||||
}
|
unsafe impl Sync for Devices {}
|
||||||
unsafe impl Sync for Devices {
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Iterator for Devices {
|
impl Iterator for Devices {
|
||||||
type Item = Device;
|
type Item = Device;
|
||||||
fn next(&mut self) -> Option<Device> {
|
fn next(&mut self) -> Option<Device> {
|
||||||
self.0.next().map(|id| Device { audio_device_id: id })
|
self.0.next().map(|id| Device {
|
||||||
|
audio_device_id: id,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,67 +1,33 @@
|
||||||
extern crate coreaudio;
|
|
||||||
extern crate core_foundation_sys;
|
extern crate core_foundation_sys;
|
||||||
|
extern crate coreaudio;
|
||||||
|
|
||||||
use crate::{
|
use self::core_foundation_sys::string::{CFStringGetCStringPtr, CFStringRef};
|
||||||
ChannelCount,
|
use self::coreaudio::audio_unit::render_callback::{self, data};
|
||||||
BackendSpecificError,
|
use self::coreaudio::audio_unit::{AudioUnit, Element, Scope};
|
||||||
BuildStreamError,
|
use self::coreaudio::sys::{
|
||||||
Data,
|
kAudioDevicePropertyAvailableNominalSampleRates, kAudioDevicePropertyDeviceNameCFString,
|
||||||
DefaultFormatError,
|
kAudioDevicePropertyNominalSampleRate, kAudioDevicePropertyScopeOutput,
|
||||||
DeviceNameError,
|
kAudioDevicePropertyStreamConfiguration, kAudioDevicePropertyStreamFormat,
|
||||||
DevicesError,
|
kAudioFormatFlagIsFloat, kAudioFormatFlagIsPacked, kAudioFormatLinearPCM,
|
||||||
Format,
|
kAudioObjectPropertyElementMaster, kAudioObjectPropertyScopeGlobal,
|
||||||
PauseStreamError,
|
kAudioObjectPropertyScopeInput, kAudioObjectPropertyScopeOutput,
|
||||||
PlayStreamError,
|
kAudioOutputUnitProperty_CurrentDevice, kAudioOutputUnitProperty_EnableIO,
|
||||||
SampleFormat,
|
kAudioUnitProperty_StreamFormat, kCFStringEncodingUTF8, AudioBuffer, AudioBufferList,
|
||||||
SampleRate,
|
AudioDeviceID, AudioObjectAddPropertyListener, AudioObjectGetPropertyData,
|
||||||
StreamError,
|
AudioObjectGetPropertyDataSize, AudioObjectID, AudioObjectPropertyAddress,
|
||||||
SupportedFormat,
|
AudioObjectPropertyScope, AudioObjectRemovePropertyListener, AudioObjectSetPropertyData,
|
||||||
SupportedFormatsError,
|
AudioStreamBasicDescription, AudioValueRange, OSStatus,
|
||||||
};
|
};
|
||||||
use crate::traits::{DeviceTrait, HostTrait, StreamTrait};
|
use crate::traits::{DeviceTrait, HostTrait, StreamTrait};
|
||||||
use self::coreaudio::audio_unit::{AudioUnit, Scope, Element};
|
use crate::{
|
||||||
use self::coreaudio::audio_unit::render_callback::{self, data};
|
BackendSpecificError, BuildStreamError, ChannelCount, Data, DefaultFormatError,
|
||||||
use self::coreaudio::sys::{
|
DeviceNameError, DevicesError, Format, PauseStreamError, PlayStreamError, SampleFormat,
|
||||||
AudioBuffer,
|
SampleRate, StreamError, SupportedFormat, SupportedFormatsError,
|
||||||
AudioBufferList,
|
|
||||||
AudioDeviceID,
|
|
||||||
AudioObjectAddPropertyListener,
|
|
||||||
AudioObjectGetPropertyData,
|
|
||||||
AudioObjectGetPropertyDataSize,
|
|
||||||
AudioObjectID,
|
|
||||||
AudioObjectPropertyAddress,
|
|
||||||
AudioObjectPropertyScope,
|
|
||||||
AudioObjectRemovePropertyListener,
|
|
||||||
AudioObjectSetPropertyData,
|
|
||||||
AudioStreamBasicDescription,
|
|
||||||
AudioValueRange,
|
|
||||||
kAudioDevicePropertyAvailableNominalSampleRates,
|
|
||||||
kAudioDevicePropertyDeviceNameCFString,
|
|
||||||
kAudioDevicePropertyNominalSampleRate,
|
|
||||||
kAudioObjectPropertyScopeInput,
|
|
||||||
kAudioObjectPropertyScopeGlobal,
|
|
||||||
kAudioDevicePropertyScopeOutput,
|
|
||||||
kAudioDevicePropertyStreamConfiguration,
|
|
||||||
kAudioDevicePropertyStreamFormat,
|
|
||||||
kAudioFormatFlagIsFloat,
|
|
||||||
kAudioFormatFlagIsPacked,
|
|
||||||
kAudioFormatLinearPCM,
|
|
||||||
kAudioObjectPropertyElementMaster,
|
|
||||||
kAudioObjectPropertyScopeOutput,
|
|
||||||
kAudioOutputUnitProperty_CurrentDevice,
|
|
||||||
kAudioOutputUnitProperty_EnableIO,
|
|
||||||
kAudioUnitProperty_StreamFormat,
|
|
||||||
kCFStringEncodingUTF8,
|
|
||||||
OSStatus,
|
|
||||||
};
|
|
||||||
use self::core_foundation_sys::string::{
|
|
||||||
CFStringRef,
|
|
||||||
CFStringGetCStringPtr,
|
|
||||||
};
|
};
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::os::raw::c_char;
|
use std::os::raw::c_char;
|
||||||
use std::ptr::null;
|
use std::ptr::null;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
|
@ -70,7 +36,10 @@ use std::time::Duration;
|
||||||
|
|
||||||
mod enumerate;
|
mod enumerate;
|
||||||
|
|
||||||
pub use self::enumerate::{Devices, SupportedInputFormats, SupportedOutputFormats, default_input_device, default_output_device};
|
pub use self::enumerate::{
|
||||||
|
default_input_device, default_output_device, Devices, SupportedInputFormats,
|
||||||
|
SupportedOutputFormats,
|
||||||
|
};
|
||||||
|
|
||||||
/// Coreaudio host, the default host on macOS and iOS.
|
/// Coreaudio host, the default host on macOS and iOS.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -107,17 +76,21 @@ impl HostTrait for Host {
|
||||||
impl DeviceTrait for Device {
|
impl DeviceTrait for Device {
|
||||||
type SupportedInputFormats = SupportedInputFormats;
|
type SupportedInputFormats = SupportedInputFormats;
|
||||||
type SupportedOutputFormats = SupportedOutputFormats;
|
type SupportedOutputFormats = SupportedOutputFormats;
|
||||||
type Stream = Stream;
|
type Stream = Stream;
|
||||||
|
|
||||||
fn name(&self) -> Result<String, DeviceNameError> {
|
fn name(&self) -> Result<String, DeviceNameError> {
|
||||||
Device::name(self)
|
Device::name(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn supported_input_formats(&self) -> Result<Self::SupportedInputFormats, SupportedFormatsError> {
|
fn supported_input_formats(
|
||||||
|
&self,
|
||||||
|
) -> Result<Self::SupportedInputFormats, SupportedFormatsError> {
|
||||||
Device::supported_input_formats(self)
|
Device::supported_input_formats(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn supported_output_formats(&self) -> Result<Self::SupportedOutputFormats, SupportedFormatsError> {
|
fn supported_output_formats(
|
||||||
|
&self,
|
||||||
|
) -> Result<Self::SupportedOutputFormats, SupportedFormatsError> {
|
||||||
Device::supported_output_formats(self)
|
Device::supported_output_formats(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,8 +169,7 @@ impl Device {
|
||||||
fn supported_formats(
|
fn supported_formats(
|
||||||
&self,
|
&self,
|
||||||
scope: AudioObjectPropertyScope,
|
scope: AudioObjectPropertyScope,
|
||||||
) -> Result<SupportedOutputFormats, SupportedFormatsError>
|
) -> Result<SupportedOutputFormats, SupportedFormatsError> {
|
||||||
{
|
|
||||||
let mut property_address = AudioObjectPropertyAddress {
|
let mut property_address = AudioObjectPropertyAddress {
|
||||||
mSelector: kAudioDevicePropertyStreamConfiguration,
|
mSelector: kAudioDevicePropertyStreamConfiguration,
|
||||||
mScope: scope,
|
mScope: scope,
|
||||||
|
@ -307,17 +279,18 @@ impl Device {
|
||||||
fn default_format(
|
fn default_format(
|
||||||
&self,
|
&self,
|
||||||
scope: AudioObjectPropertyScope,
|
scope: AudioObjectPropertyScope,
|
||||||
) -> Result<Format, DefaultFormatError>
|
) -> Result<Format, DefaultFormatError> {
|
||||||
{
|
|
||||||
fn default_format_error_from_os_status(status: OSStatus) -> Result<(), DefaultFormatError> {
|
fn default_format_error_from_os_status(status: OSStatus) -> Result<(), DefaultFormatError> {
|
||||||
let err = match coreaudio::Error::from_os_status(status) {
|
let err = match coreaudio::Error::from_os_status(status) {
|
||||||
Err(err) => err,
|
Err(err) => err,
|
||||||
Ok(_) => return Ok(()),
|
Ok(_) => return Ok(()),
|
||||||
};
|
};
|
||||||
match err {
|
match err {
|
||||||
coreaudio::Error::AudioUnit(coreaudio::error::AudioUnitError::FormatNotSupported) |
|
coreaudio::Error::AudioUnit(
|
||||||
coreaudio::Error::AudioCodec(_) |
|
coreaudio::error::AudioUnitError::FormatNotSupported,
|
||||||
coreaudio::Error::AudioFormat(_) => {
|
)
|
||||||
|
| coreaudio::Error::AudioCodec(_)
|
||||||
|
| coreaudio::Error::AudioFormat(_) => {
|
||||||
Err(DefaultFormatError::StreamTypeNotSupported)
|
Err(DefaultFormatError::StreamTypeNotSupported)
|
||||||
}
|
}
|
||||||
coreaudio::Error::AudioUnit(coreaudio::error::AudioUnitError::NoConnection) => {
|
coreaudio::Error::AudioUnit(coreaudio::error::AudioUnitError::NoConnection) => {
|
||||||
|
@ -412,11 +385,11 @@ struct StreamInner {
|
||||||
impl From<coreaudio::Error> for BuildStreamError {
|
impl From<coreaudio::Error> for BuildStreamError {
|
||||||
fn from(err: coreaudio::Error) -> BuildStreamError {
|
fn from(err: coreaudio::Error) -> BuildStreamError {
|
||||||
match err {
|
match err {
|
||||||
coreaudio::Error::RenderCallbackBufferFormatDoesNotMatchAudioUnitStreamFormat |
|
coreaudio::Error::RenderCallbackBufferFormatDoesNotMatchAudioUnitStreamFormat
|
||||||
coreaudio::Error::NoKnownSubtype |
|
| coreaudio::Error::NoKnownSubtype
|
||||||
coreaudio::Error::AudioUnit(coreaudio::error::AudioUnitError::FormatNotSupported) |
|
| coreaudio::Error::AudioUnit(coreaudio::error::AudioUnitError::FormatNotSupported)
|
||||||
coreaudio::Error::AudioCodec(_) |
|
| coreaudio::Error::AudioCodec(_)
|
||||||
coreaudio::Error::AudioFormat(_) => BuildStreamError::FormatNotSupported,
|
| coreaudio::Error::AudioFormat(_) => BuildStreamError::FormatNotSupported,
|
||||||
_ => BuildStreamError::DeviceNotAvailable,
|
_ => BuildStreamError::DeviceNotAvailable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -513,8 +486,8 @@ impl Device {
|
||||||
// Get the current sample rate.
|
// Get the current sample rate.
|
||||||
let mut property_address = AudioObjectPropertyAddress {
|
let mut property_address = AudioObjectPropertyAddress {
|
||||||
mSelector: kAudioDevicePropertyNominalSampleRate,
|
mSelector: kAudioDevicePropertyNominalSampleRate,
|
||||||
mScope: kAudioObjectPropertyScopeGlobal,
|
mScope: kAudioObjectPropertyScopeGlobal,
|
||||||
mElement: kAudioObjectPropertyElementMaster,
|
mElement: kAudioObjectPropertyElementMaster,
|
||||||
};
|
};
|
||||||
let sample_rate: f64 = 0.0;
|
let sample_rate: f64 = 0.0;
|
||||||
let data_size = mem::size_of::<f64>() as u32;
|
let data_size = mem::size_of::<f64>() as u32;
|
||||||
|
@ -558,9 +531,9 @@ impl Device {
|
||||||
|
|
||||||
// Now that we have the available ranges, pick the one matching the desired rate.
|
// Now that we have the available ranges, pick the one matching the desired rate.
|
||||||
let sample_rate = format.sample_rate.0;
|
let sample_rate = format.sample_rate.0;
|
||||||
let maybe_index = ranges
|
let maybe_index = ranges.iter().position(|r| {
|
||||||
.iter()
|
r.mMinimum as u32 == sample_rate && r.mMaximum as u32 == sample_rate
|
||||||
.position(|r| r.mMinimum as u32 == sample_rate && r.mMaximum as u32 == sample_rate);
|
});
|
||||||
let range_index = match maybe_index {
|
let range_index = match maybe_index {
|
||||||
None => return Err(BuildStreamError::FormatNotSupported),
|
None => return Err(BuildStreamError::FormatNotSupported),
|
||||||
Some(i) => i,
|
Some(i) => i,
|
||||||
|
@ -583,8 +556,8 @@ impl Device {
|
||||||
let data_size = mem::size_of::<f64>();
|
let data_size = mem::size_of::<f64>();
|
||||||
let property_address = AudioObjectPropertyAddress {
|
let property_address = AudioObjectPropertyAddress {
|
||||||
mSelector: kAudioDevicePropertyNominalSampleRate,
|
mSelector: kAudioDevicePropertyNominalSampleRate,
|
||||||
mScope: kAudioObjectPropertyScopeGlobal,
|
mScope: kAudioObjectPropertyScopeGlobal,
|
||||||
mElement: kAudioObjectPropertyElementMaster,
|
mElement: kAudioObjectPropertyElementMaster,
|
||||||
};
|
};
|
||||||
AudioObjectGetPropertyData(
|
AudioObjectGetPropertyData(
|
||||||
device_id,
|
device_id,
|
||||||
|
@ -624,7 +597,8 @@ impl Device {
|
||||||
let timer = ::std::time::Instant::now();
|
let timer = ::std::time::Instant::now();
|
||||||
while sample_rate != reported_rate {
|
while sample_rate != reported_rate {
|
||||||
if timer.elapsed() > Duration::from_secs(1) {
|
if timer.elapsed() > Duration::from_secs(1) {
|
||||||
let description = "timeout waiting for sample rate update for device".into();
|
let description =
|
||||||
|
"timeout waiting for sample rate update for device".into();
|
||||||
let err = BackendSpecificError { description };
|
let err = BackendSpecificError { description };
|
||||||
return Err(err.into());
|
return Err(err.into());
|
||||||
}
|
}
|
||||||
|
@ -662,7 +636,7 @@ impl Device {
|
||||||
let AudioBuffer {
|
let AudioBuffer {
|
||||||
mNumberChannels: _num_channels,
|
mNumberChannels: _num_channels,
|
||||||
mDataByteSize: data_byte_size,
|
mDataByteSize: data_byte_size,
|
||||||
mData: data
|
mData: data,
|
||||||
} = buffers[0];
|
} = buffers[0];
|
||||||
|
|
||||||
let data = data as *mut ();
|
let data = data as *mut ();
|
||||||
|
@ -713,7 +687,7 @@ impl Device {
|
||||||
let AudioBuffer {
|
let AudioBuffer {
|
||||||
mNumberChannels: _num_channels,
|
mNumberChannels: _num_channels,
|
||||||
mDataByteSize: data_byte_size,
|
mDataByteSize: data_byte_size,
|
||||||
mData: data
|
mData: data,
|
||||||
} = (*args.data.data).mBuffers[0];
|
} = (*args.data.data).mBuffers[0];
|
||||||
|
|
||||||
let data = data as *mut ();
|
let data = data as *mut ();
|
||||||
|
|
|
@ -2,23 +2,14 @@ use std::mem;
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::c_void;
|
||||||
use std::slice::from_raw_parts;
|
use std::slice::from_raw_parts;
|
||||||
use stdweb;
|
use stdweb;
|
||||||
use stdweb::Reference;
|
|
||||||
use stdweb::unstable::TryInto;
|
use stdweb::unstable::TryInto;
|
||||||
use stdweb::web::TypedArray;
|
|
||||||
use stdweb::web::set_timeout;
|
use stdweb::web::set_timeout;
|
||||||
|
use stdweb::web::TypedArray;
|
||||||
|
use stdweb::Reference;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
BuildStreamError,
|
BuildStreamError, Data, DefaultFormatError, DeviceNameError, DevicesError, Format,
|
||||||
Data,
|
PauseStreamError, PlayStreamError, SampleFormat, StreamError, SupportedFormat,
|
||||||
DefaultFormatError,
|
|
||||||
DeviceNameError,
|
|
||||||
DevicesError,
|
|
||||||
Format,
|
|
||||||
PauseStreamError,
|
|
||||||
PlayStreamError,
|
|
||||||
SampleFormat,
|
|
||||||
StreamError,
|
|
||||||
SupportedFormat,
|
|
||||||
SupportedFormatsError,
|
SupportedFormatsError,
|
||||||
};
|
};
|
||||||
use traits::{DeviceTrait, HostTrait, StreamTrait};
|
use traits::{DeviceTrait, HostTrait, StreamTrait};
|
||||||
|
@ -83,16 +74,13 @@ impl Device {
|
||||||
//
|
//
|
||||||
// UPDATE: We can do this now. Might be best to use `crate::COMMON_SAMPLE_RATES` and
|
// UPDATE: We can do this now. Might be best to use `crate::COMMON_SAMPLE_RATES` and
|
||||||
// filter out those that lay outside the range specified above.
|
// filter out those that lay outside the range specified above.
|
||||||
Ok(
|
Ok(vec![SupportedFormat {
|
||||||
vec![
|
channels: 2,
|
||||||
SupportedFormat {
|
min_sample_rate: ::SampleRate(44100),
|
||||||
channels: 2,
|
max_sample_rate: ::SampleRate(44100),
|
||||||
min_sample_rate: ::SampleRate(44100),
|
data_type: ::SampleFormat::F32,
|
||||||
max_sample_rate: ::SampleRate(44100),
|
}]
|
||||||
data_type: ::SampleFormat::F32,
|
.into_iter())
|
||||||
},
|
|
||||||
].into_iter(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
|
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
|
||||||
|
@ -101,13 +89,11 @@ impl Device {
|
||||||
|
|
||||||
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
|
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
|
||||||
// TODO: because it is hard coded, see supported_output_formats.
|
// TODO: because it is hard coded, see supported_output_formats.
|
||||||
Ok(
|
Ok(Format {
|
||||||
Format {
|
channels: 2,
|
||||||
channels: 2,
|
sample_rate: ::SampleRate(44100),
|
||||||
sample_rate: ::SampleRate(44100),
|
data_type: ::SampleFormat::F32,
|
||||||
data_type: ::SampleFormat::F32,
|
})
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,11 +128,15 @@ impl DeviceTrait for Device {
|
||||||
Device::name(self)
|
Device::name(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn supported_input_formats(&self) -> Result<Self::SupportedInputFormats, SupportedFormatsError> {
|
fn supported_input_formats(
|
||||||
|
&self,
|
||||||
|
) -> Result<Self::SupportedInputFormats, SupportedFormatsError> {
|
||||||
Device::supported_input_formats(self)
|
Device::supported_input_formats(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn supported_output_formats(&self) -> Result<Self::SupportedOutputFormats, SupportedFormatsError> {
|
fn supported_output_formats(
|
||||||
|
&self,
|
||||||
|
) -> Result<Self::SupportedOutputFormats, SupportedFormatsError> {
|
||||||
Device::supported_output_formats(self)
|
Device::supported_output_formats(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,7 +191,10 @@ impl DeviceTrait for Device {
|
||||||
//
|
//
|
||||||
// See also: The call to `set_timeout` at the end of the `audio_callback_fn` which creates
|
// See also: The call to `set_timeout` at the end of the `audio_callback_fn` which creates
|
||||||
// the loop.
|
// the loop.
|
||||||
set_timeout(|| audio_callback_fn::<D, E>(user_data_ptr as *mut c_void), 10);
|
set_timeout(
|
||||||
|
|| audio_callback_fn::<D, E>(user_data_ptr as *mut c_void),
|
||||||
|
10,
|
||||||
|
);
|
||||||
|
|
||||||
Ok(stream)
|
Ok(stream)
|
||||||
}
|
}
|
||||||
|
@ -323,9 +316,10 @@ fn default_output_device() -> Option<Device> {
|
||||||
fn is_webaudio_available() -> bool {
|
fn is_webaudio_available() -> bool {
|
||||||
stdweb::initialize();
|
stdweb::initialize();
|
||||||
js!(if (!AudioContext) {
|
js!(if (!AudioContext) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return true;
|
||||||
}).try_into()
|
})
|
||||||
.unwrap()
|
.try_into()
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
BuildStreamError,
|
BuildStreamError, Data, DefaultFormatError, DeviceNameError, DevicesError, Format,
|
||||||
Data,
|
PauseStreamError, PlayStreamError, StreamError, SupportedFormat, SupportedFormatsError,
|
||||||
DefaultFormatError,
|
|
||||||
DevicesError,
|
|
||||||
DeviceNameError,
|
|
||||||
Format,
|
|
||||||
PauseStreamError,
|
|
||||||
PlayStreamError,
|
|
||||||
StreamError,
|
|
||||||
SupportedFormatsError,
|
|
||||||
SupportedFormat,
|
|
||||||
};
|
};
|
||||||
use traits::{DeviceTrait, HostTrait, StreamTrait};
|
use traits::{DeviceTrait, HostTrait, StreamTrait};
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
BackendSpecificError,
|
BackendSpecificError, Data, DefaultFormatError, DeviceNameError, DevicesError, Format,
|
||||||
Data,
|
SampleFormat, SampleRate, SupportedFormat, SupportedFormatsError, COMMON_SAMPLE_RATES,
|
||||||
DefaultFormatError,
|
|
||||||
DeviceNameError,
|
|
||||||
DevicesError,
|
|
||||||
Format,
|
|
||||||
SampleFormat,
|
|
||||||
SampleRate,
|
|
||||||
SupportedFormat,
|
|
||||||
SupportedFormatsError,
|
|
||||||
COMMON_SAMPLE_RATES,
|
|
||||||
};
|
};
|
||||||
use std;
|
use std;
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
|
@ -115,7 +106,11 @@ impl DeviceTrait for Device {
|
||||||
E: FnMut(StreamError) + Send + 'static,
|
E: FnMut(StreamError) + Send + 'static,
|
||||||
{
|
{
|
||||||
let stream_inner = self.build_input_stream_inner(format)?;
|
let stream_inner = self.build_input_stream_inner(format)?;
|
||||||
Ok(Stream::new_input(stream_inner, data_callback, error_callback))
|
Ok(Stream::new_input(
|
||||||
|
stream_inner,
|
||||||
|
data_callback,
|
||||||
|
error_callback,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_output_stream<D, E>(
|
fn build_output_stream<D, E>(
|
||||||
|
@ -129,7 +124,11 @@ impl DeviceTrait for Device {
|
||||||
E: FnMut(StreamError) + Send + 'static,
|
E: FnMut(StreamError) + Send + 'static,
|
||||||
{
|
{
|
||||||
let stream_inner = self.build_output_stream_inner(format)?;
|
let stream_inner = self.build_output_stream_inner(format)?;
|
||||||
Ok(Stream::new_output(stream_inner, data_callback, error_callback))
|
Ok(Stream::new_output(
|
||||||
|
stream_inner,
|
||||||
|
data_callback,
|
||||||
|
error_callback,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -716,8 +715,7 @@ impl Device {
|
||||||
|
|
||||||
// Building a `IAudioCaptureClient` that will be used to read captured samples.
|
// Building a `IAudioCaptureClient` that will be used to read captured samples.
|
||||||
let capture_client = {
|
let capture_client = {
|
||||||
let mut capture_client: *mut audioclient::IAudioCaptureClient =
|
let mut capture_client: *mut audioclient::IAudioCaptureClient = ptr::null_mut();
|
||||||
ptr::null_mut();
|
|
||||||
let hresult = (*audio_client).GetService(
|
let hresult = (*audio_client).GetService(
|
||||||
&audioclient::IID_IAudioCaptureClient,
|
&audioclient::IID_IAudioCaptureClient,
|
||||||
&mut capture_client as *mut *mut audioclient::IAudioCaptureClient as *mut _,
|
&mut capture_client as *mut *mut audioclient::IAudioCaptureClient as *mut _,
|
||||||
|
|
|
@ -1,16 +1,3 @@
|
||||||
use crate::{
|
|
||||||
BackendSpecificError,
|
|
||||||
Data,
|
|
||||||
PauseStreamError,
|
|
||||||
PlayStreamError,
|
|
||||||
SampleFormat,
|
|
||||||
StreamError,
|
|
||||||
};
|
|
||||||
use crate::traits::StreamTrait;
|
|
||||||
use std::mem;
|
|
||||||
use std::ptr;
|
|
||||||
use std::sync::mpsc::{channel, Receiver, Sender};
|
|
||||||
use std::thread::{self, JoinHandle};
|
|
||||||
use super::check_result;
|
use super::check_result;
|
||||||
use super::winapi::shared::basetsd::UINT32;
|
use super::winapi::shared::basetsd::UINT32;
|
||||||
use super::winapi::shared::minwindef::{BYTE, FALSE, WORD};
|
use super::winapi::shared::minwindef::{BYTE, FALSE, WORD};
|
||||||
|
@ -19,6 +6,14 @@ use super::winapi::um::handleapi;
|
||||||
use super::winapi::um::synchapi;
|
use super::winapi::um::synchapi;
|
||||||
use super::winapi::um::winbase;
|
use super::winapi::um::winbase;
|
||||||
use super::winapi::um::winnt;
|
use super::winapi::um::winnt;
|
||||||
|
use crate::traits::StreamTrait;
|
||||||
|
use crate::{
|
||||||
|
BackendSpecificError, Data, PauseStreamError, PlayStreamError, SampleFormat, StreamError,
|
||||||
|
};
|
||||||
|
use std::mem;
|
||||||
|
use std::ptr;
|
||||||
|
use std::sync::mpsc::{channel, Receiver, Sender};
|
||||||
|
use std::thread::{self, JoinHandle};
|
||||||
|
|
||||||
pub struct Stream {
|
pub struct Stream {
|
||||||
/// The high-priority audio processing thread calling callbacks.
|
/// The high-priority audio processing thread calling callbacks.
|
||||||
|
@ -295,7 +290,12 @@ fn run_input(
|
||||||
AudioClientFlow::Capture { capture_client } => capture_client,
|
AudioClientFlow::Capture { capture_client } => capture_client,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
match process_input(&mut run_ctxt.stream, capture_client, data_callback, error_callback) {
|
match process_input(
|
||||||
|
&mut run_ctxt.stream,
|
||||||
|
capture_client,
|
||||||
|
data_callback,
|
||||||
|
error_callback,
|
||||||
|
) {
|
||||||
ControlFlow::Break => break,
|
ControlFlow::Break => break,
|
||||||
ControlFlow::Continue => continue,
|
ControlFlow::Continue => continue,
|
||||||
}
|
}
|
||||||
|
@ -317,7 +317,12 @@ fn run_output(
|
||||||
AudioClientFlow::Render { render_client } => render_client,
|
AudioClientFlow::Render { render_client } => render_client,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
match process_output(&mut run_ctxt.stream, render_client, data_callback, error_callback) {
|
match process_output(
|
||||||
|
&mut run_ctxt.stream,
|
||||||
|
render_client,
|
||||||
|
data_callback,
|
||||||
|
error_callback,
|
||||||
|
) {
|
||||||
ControlFlow::Break => break,
|
ControlFlow::Break => break,
|
||||||
ControlFlow::Continue => continue,
|
ControlFlow::Continue => continue,
|
||||||
}
|
}
|
||||||
|
@ -401,8 +406,7 @@ fn process_input(
|
||||||
debug_assert!(!buffer.is_null());
|
debug_assert!(!buffer.is_null());
|
||||||
|
|
||||||
let data = buffer as *mut ();
|
let data = buffer as *mut ();
|
||||||
let len = frames_available as usize
|
let len = frames_available as usize * stream.bytes_per_frame as usize
|
||||||
* stream.bytes_per_frame as usize
|
|
||||||
/ stream.sample_format.sample_size();
|
/ stream.sample_format.sample_size();
|
||||||
let data = Data::from_parts(data, len, stream.sample_format);
|
let data = Data::from_parts(data, len, stream.sample_format);
|
||||||
data_callback(&data);
|
data_callback(&data);
|
||||||
|
@ -436,8 +440,7 @@ fn process_output(
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut buffer: *mut BYTE = ptr::null_mut();
|
let mut buffer: *mut BYTE = ptr::null_mut();
|
||||||
let hresult =
|
let hresult = (*render_client).GetBuffer(frames_available, &mut buffer as *mut *mut _);
|
||||||
(*render_client).GetBuffer(frames_available, &mut buffer as *mut *mut _);
|
|
||||||
|
|
||||||
if let Err(err) = stream_error_from_hresult(hresult) {
|
if let Err(err) = stream_error_from_hresult(hresult) {
|
||||||
error_callback(err);
|
error_callback(err);
|
||||||
|
@ -447,8 +450,7 @@ fn process_output(
|
||||||
debug_assert!(!buffer.is_null());
|
debug_assert!(!buffer.is_null());
|
||||||
|
|
||||||
let data = buffer as *mut ();
|
let data = buffer as *mut ();
|
||||||
let len = frames_available as usize
|
let len = frames_available as usize * stream.bytes_per_frame as usize
|
||||||
* stream.bytes_per_frame as usize
|
|
||||||
/ stream.sample_format.sample_size();
|
/ stream.sample_format.sample_size();
|
||||||
let mut data = Data::from_parts(data, len, stream.sample_format);
|
let mut data = Data::from_parts(data, len, stream.sample_format);
|
||||||
data_callback(&mut data);
|
data_callback(&mut data);
|
||||||
|
|
34
src/lib.rs
34
src/lib.rs
|
@ -149,8 +149,8 @@ extern crate thiserror;
|
||||||
|
|
||||||
pub use error::*;
|
pub use error::*;
|
||||||
pub use platform::{
|
pub use platform::{
|
||||||
ALL_HOSTS, available_hosts, default_host, Device, Devices, Host, host_from_id,
|
available_hosts, default_host, host_from_id, Device, Devices, Host, HostId, Stream,
|
||||||
HostId, Stream, SupportedInputFormats, SupportedOutputFormats,
|
SupportedInputFormats, SupportedOutputFormats, ALL_HOSTS,
|
||||||
};
|
};
|
||||||
pub use samples_formats::{Sample, SampleFormat};
|
pub use samples_formats::{Sample, SampleFormat};
|
||||||
|
|
||||||
|
@ -218,7 +218,11 @@ impl Data {
|
||||||
len: usize,
|
len: usize,
|
||||||
sample_format: SampleFormat,
|
sample_format: SampleFormat,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Data { data, len, sample_format }
|
Data {
|
||||||
|
data,
|
||||||
|
len,
|
||||||
|
sample_format,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The sample format of the internal audio data.
|
/// The sample format of the internal audio data.
|
||||||
|
@ -241,9 +245,7 @@ impl Data {
|
||||||
let len = self.len * self.sample_format.sample_size();
|
let len = self.len * self.sample_format.sample_size();
|
||||||
// The safety of this block relies on correct construction of the `Data` instance. See
|
// The safety of this block relies on correct construction of the `Data` instance. See
|
||||||
// the unsafe `from_parts` constructor for these requirements.
|
// the unsafe `from_parts` constructor for these requirements.
|
||||||
unsafe {
|
unsafe { std::slice::from_raw_parts(self.data as *const u8, len) }
|
||||||
std::slice::from_raw_parts(self.data as *const u8, len)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The raw slice of memory representing the underlying audio data as a slice of bytes.
|
/// The raw slice of memory representing the underlying audio data as a slice of bytes.
|
||||||
|
@ -253,9 +255,7 @@ impl Data {
|
||||||
let len = self.len * self.sample_format.sample_size();
|
let len = self.len * self.sample_format.sample_size();
|
||||||
// The safety of this block relies on correct construction of the `Data` instance. See
|
// The safety of this block relies on correct construction of the `Data` instance. See
|
||||||
// the unsafe `from_parts` constructor for these requirements.
|
// the unsafe `from_parts` constructor for these requirements.
|
||||||
unsafe {
|
unsafe { std::slice::from_raw_parts_mut(self.data as *mut u8, len) }
|
||||||
std::slice::from_raw_parts_mut(self.data as *mut u8, len)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access the data as a slice of sample type `T`.
|
/// Access the data as a slice of sample type `T`.
|
||||||
|
@ -268,9 +268,7 @@ impl Data {
|
||||||
if T::FORMAT == self.sample_format {
|
if T::FORMAT == self.sample_format {
|
||||||
// The safety of this block relies on correct construction of the `Data` instance. See
|
// The safety of this block relies on correct construction of the `Data` instance. See
|
||||||
// the unsafe `from_parts` constructor for these requirements.
|
// the unsafe `from_parts` constructor for these requirements.
|
||||||
unsafe {
|
unsafe { Some(std::slice::from_raw_parts(self.data as *const T, self.len)) }
|
||||||
Some(std::slice::from_raw_parts(self.data as *const T, self.len))
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -287,7 +285,10 @@ impl Data {
|
||||||
// The safety of this block relies on correct construction of the `Data` instance. See
|
// The safety of this block relies on correct construction of the `Data` instance. See
|
||||||
// the unsafe `from_parts` constructor for these requirements.
|
// the unsafe `from_parts` constructor for these requirements.
|
||||||
unsafe {
|
unsafe {
|
||||||
Some(std::slice::from_raw_parts_mut(self.data as *mut T, self.len))
|
Some(std::slice::from_raw_parts_mut(
|
||||||
|
self.data as *mut T,
|
||||||
|
self.len,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -365,10 +366,9 @@ impl SupportedFormat {
|
||||||
}
|
}
|
||||||
|
|
||||||
const HZ_44100: SampleRate = SampleRate(44_100);
|
const HZ_44100: SampleRate = SampleRate(44_100);
|
||||||
let r44100_in_self = self.min_sample_rate <= HZ_44100
|
let r44100_in_self = self.min_sample_rate <= HZ_44100 && HZ_44100 <= self.max_sample_rate;
|
||||||
&& HZ_44100 <= self.max_sample_rate;
|
let r44100_in_other =
|
||||||
let r44100_in_other = other.min_sample_rate <= HZ_44100
|
other.min_sample_rate <= HZ_44100 && HZ_44100 <= other.max_sample_rate;
|
||||||
&& HZ_44100 <= other.max_sample_rate;
|
|
||||||
let cmp_r44100 = r44100_in_self.cmp(&r44100_in_other);
|
let cmp_r44100 = r44100_in_self.cmp(&r44100_in_other);
|
||||||
if cmp_r44100 != Equal {
|
if cmp_r44100 != Equal {
|
||||||
return cmp_r44100;
|
return cmp_r44100;
|
||||||
|
|
|
@ -435,10 +435,7 @@ macro_rules! impl_platform_host {
|
||||||
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd"))]
|
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd"))]
|
||||||
mod platform_impl {
|
mod platform_impl {
|
||||||
pub use crate::host::alsa::{
|
pub use crate::host::alsa::{
|
||||||
Device as AlsaDevice,
|
Device as AlsaDevice, Devices as AlsaDevices, Host as AlsaHost, Stream as AlsaStream,
|
||||||
Devices as AlsaDevices,
|
|
||||||
Host as AlsaHost,
|
|
||||||
Stream as AlsaStream,
|
|
||||||
SupportedInputFormats as AlsaSupportedInputFormats,
|
SupportedInputFormats as AlsaSupportedInputFormats,
|
||||||
SupportedOutputFormats as AlsaSupportedOutputFormats,
|
SupportedOutputFormats as AlsaSupportedOutputFormats,
|
||||||
};
|
};
|
||||||
|
@ -453,15 +450,11 @@ mod platform_impl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||||
mod platform_impl {
|
mod platform_impl {
|
||||||
pub use crate::host::coreaudio::{
|
pub use crate::host::coreaudio::{
|
||||||
Device as CoreAudioDevice,
|
Device as CoreAudioDevice, Devices as CoreAudioDevices, Host as CoreAudioHost,
|
||||||
Devices as CoreAudioDevices,
|
Stream as CoreAudioStream, SupportedInputFormats as CoreAudioSupportedInputFormats,
|
||||||
Host as CoreAudioHost,
|
|
||||||
Stream as CoreAudioStream,
|
|
||||||
SupportedInputFormats as CoreAudioSupportedInputFormats,
|
|
||||||
SupportedOutputFormats as CoreAudioSupportedOutputFormats,
|
SupportedOutputFormats as CoreAudioSupportedOutputFormats,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -478,11 +471,8 @@ mod platform_impl {
|
||||||
#[cfg(target_os = "emscripten")]
|
#[cfg(target_os = "emscripten")]
|
||||||
mod platform_impl {
|
mod platform_impl {
|
||||||
pub use crate::host::emscripten::{
|
pub use crate::host::emscripten::{
|
||||||
Device as EmscriptenDevice,
|
Device as EmscriptenDevice, Devices as EmscriptenDevices, Host as EmscriptenHost,
|
||||||
Devices as EmscriptenDevices,
|
Stream as EmscriptenStream, SupportedInputFormats as EmscriptenSupportedInputFormats,
|
||||||
Host as EmscriptenHost,
|
|
||||||
Stream as EmscriptenStream,
|
|
||||||
SupportedInputFormats as EmscriptenSupportedInputFormats,
|
|
||||||
SupportedOutputFormats as EmscriptenSupportedOutputFormats,
|
SupportedOutputFormats as EmscriptenSupportedOutputFormats,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -500,19 +490,13 @@ mod platform_impl {
|
||||||
mod platform_impl {
|
mod platform_impl {
|
||||||
#[cfg(feature = "asio")]
|
#[cfg(feature = "asio")]
|
||||||
pub use crate::host::asio::{
|
pub use crate::host::asio::{
|
||||||
Device as AsioDevice,
|
Device as AsioDevice, Devices as AsioDevices, Host as AsioHost, Stream as AsioStream,
|
||||||
Devices as AsioDevices,
|
|
||||||
Stream as AsioStream,
|
|
||||||
Host as AsioHost,
|
|
||||||
SupportedInputFormats as AsioSupportedInputFormats,
|
SupportedInputFormats as AsioSupportedInputFormats,
|
||||||
SupportedOutputFormats as AsioSupportedOutputFormats,
|
SupportedOutputFormats as AsioSupportedOutputFormats,
|
||||||
};
|
};
|
||||||
pub use crate::host::wasapi::{
|
pub use crate::host::wasapi::{
|
||||||
Device as WasapiDevice,
|
Device as WasapiDevice, Devices as WasapiDevices, Host as WasapiHost,
|
||||||
Devices as WasapiDevices,
|
Stream as WasapiStream, SupportedInputFormats as WasapiSupportedInputFormats,
|
||||||
Stream as WasapiStream,
|
|
||||||
Host as WasapiHost,
|
|
||||||
SupportedInputFormats as WasapiSupportedInputFormats,
|
|
||||||
SupportedOutputFormats as WasapiSupportedOutputFormats,
|
SupportedOutputFormats as WasapiSupportedOutputFormats,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -530,16 +514,19 @@ mod platform_impl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(any(windows, target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "macos",
|
#[cfg(not(any(
|
||||||
target_os = "ios", target_os = "emscripten")))]
|
windows,
|
||||||
|
target_os = "linux",
|
||||||
|
target_os = "dragonfly",
|
||||||
|
target_os = "freebsd",
|
||||||
|
target_os = "macos",
|
||||||
|
target_os = "ios",
|
||||||
|
target_os = "emscripten"
|
||||||
|
)))]
|
||||||
mod platform_impl {
|
mod platform_impl {
|
||||||
pub use crate::host::null::{
|
pub use crate::host::null::{
|
||||||
Device as NullDevice,
|
Device as NullDevice, Devices as NullDevices, EventLoop as NullEventLoop, Host as NullHost,
|
||||||
Devices as NullDevices,
|
StreamId as NullStreamId, SupportedInputFormats as NullSupportedInputFormats,
|
||||||
EventLoop as NullEventLoop,
|
|
||||||
Host as NullHost,
|
|
||||||
StreamId as NullStreamId,
|
|
||||||
SupportedInputFormats as NullSupportedInputFormats,
|
|
||||||
SupportedOutputFormats as NullSupportedOutputFormats,
|
SupportedOutputFormats as NullSupportedOutputFormats,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,8 @@ pub unsafe trait Sample: Copy + Clone {
|
||||||
|
|
||||||
/// Converts any sample type to this one by calling `to_i16`, `to_u16` or `to_f32`.
|
/// Converts any sample type to this one by calling `to_i16`, `to_u16` or `to_f32`.
|
||||||
fn from<S>(&S) -> Self
|
fn from<S>(&S) -> Self
|
||||||
where S: Sample;
|
where
|
||||||
|
S: Sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Sample for u16 {
|
unsafe impl Sample for u16 {
|
||||||
|
@ -64,7 +65,8 @@ unsafe impl Sample for u16 {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from<S>(sample: &S) -> Self
|
fn from<S>(sample: &S) -> Self
|
||||||
where S: Sample
|
where
|
||||||
|
S: Sample,
|
||||||
{
|
{
|
||||||
sample.to_u16()
|
sample.to_u16()
|
||||||
}
|
}
|
||||||
|
@ -98,7 +100,8 @@ unsafe impl Sample for i16 {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from<S>(sample: &S) -> Self
|
fn from<S>(sample: &S) -> Self
|
||||||
where S: Sample
|
where
|
||||||
|
S: Sample,
|
||||||
{
|
{
|
||||||
sample.to_i16()
|
sample.to_i16()
|
||||||
}
|
}
|
||||||
|
@ -128,7 +131,8 @@ unsafe impl Sample for f32 {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from<S>(sample: &S) -> Self
|
fn from<S>(sample: &S) -> Self
|
||||||
where S: Sample
|
where
|
||||||
|
S: Sample,
|
||||||
{
|
{
|
||||||
sample.to_f32()
|
sample.to_f32()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,8 @@
|
||||||
//! The suite of traits allowing CPAL to abstract over hosts, devices, event loops and stream IDs.
|
//! The suite of traits allowing CPAL to abstract over hosts, devices, event loops and stream IDs.
|
||||||
|
|
||||||
use {
|
use {
|
||||||
BuildStreamError,
|
BuildStreamError, Data, DefaultFormatError, DeviceNameError, DevicesError, Format,
|
||||||
Data,
|
InputDevices, OutputDevices, PauseStreamError, PlayStreamError, StreamError, SupportedFormat,
|
||||||
DefaultFormatError,
|
|
||||||
DeviceNameError,
|
|
||||||
DevicesError,
|
|
||||||
Format,
|
|
||||||
InputDevices,
|
|
||||||
OutputDevices,
|
|
||||||
PauseStreamError,
|
|
||||||
PlayStreamError,
|
|
||||||
StreamError,
|
|
||||||
SupportedFormat,
|
|
||||||
SupportedFormatsError,
|
SupportedFormatsError,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -65,7 +55,8 @@ pub trait HostTrait {
|
||||||
/// Can be empty if the system does not support audio input.
|
/// Can be empty if the system does not support audio input.
|
||||||
fn input_devices(&self) -> Result<InputDevices<Self::Devices>, DevicesError> {
|
fn input_devices(&self) -> Result<InputDevices<Self::Devices>, DevicesError> {
|
||||||
fn supports_input<D: DeviceTrait>(device: &D) -> bool {
|
fn supports_input<D: DeviceTrait>(device: &D) -> bool {
|
||||||
device.supported_input_formats()
|
device
|
||||||
|
.supported_input_formats()
|
||||||
.map(|mut iter| iter.next().is_some())
|
.map(|mut iter| iter.next().is_some())
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
@ -78,7 +69,8 @@ pub trait HostTrait {
|
||||||
/// Can be empty if the system does not support audio output.
|
/// Can be empty if the system does not support audio output.
|
||||||
fn output_devices(&self) -> Result<OutputDevices<Self::Devices>, DevicesError> {
|
fn output_devices(&self) -> Result<OutputDevices<Self::Devices>, DevicesError> {
|
||||||
fn supports_output<D: DeviceTrait>(device: &D) -> bool {
|
fn supports_output<D: DeviceTrait>(device: &D) -> bool {
|
||||||
device.supported_output_formats()
|
device
|
||||||
|
.supported_output_formats()
|
||||||
.map(|mut iter| iter.next().is_some())
|
.map(|mut iter| iter.next().is_some())
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
@ -104,12 +96,15 @@ pub trait DeviceTrait {
|
||||||
/// An iterator yielding formats that are supported by the backend.
|
/// An iterator yielding formats that are supported by the backend.
|
||||||
///
|
///
|
||||||
/// Can return an error if the device is no longer valid (eg. it has been disconnected).
|
/// Can return an error if the device is no longer valid (eg. it has been disconnected).
|
||||||
fn supported_input_formats(&self) -> Result<Self::SupportedInputFormats, SupportedFormatsError>;
|
fn supported_input_formats(&self)
|
||||||
|
-> Result<Self::SupportedInputFormats, SupportedFormatsError>;
|
||||||
|
|
||||||
/// An iterator yielding output stream formats that are supported by the device.
|
/// An iterator yielding output stream formats that are supported by the device.
|
||||||
///
|
///
|
||||||
/// Can return an error if the device is no longer valid (eg. it has been disconnected).
|
/// Can return an error if the device is no longer valid (eg. it has been disconnected).
|
||||||
fn supported_output_formats(&self) -> Result<Self::SupportedOutputFormats, SupportedFormatsError>;
|
fn supported_output_formats(
|
||||||
|
&self,
|
||||||
|
) -> Result<Self::SupportedOutputFormats, SupportedFormatsError>;
|
||||||
|
|
||||||
/// The default input stream format for the device.
|
/// The default input stream format for the device.
|
||||||
fn default_input_format(&self) -> Result<Format, DefaultFormatError>;
|
fn default_input_format(&self) -> Result<Format, DefaultFormatError>;
|
||||||
|
|
Loading…
Reference in New Issue