extern crate jack;
use crate::{traits::HostTrait, Error, ErrorKind, SampleFormat};
mod device;
mod stream;
#[allow(unused_imports)] pub use self::{
device::{Device, SupportedInputConfigs, SupportedOutputConfigs},
stream::Stream,
};
const JACK_SAMPLE_FORMAT: SampleFormat = SampleFormat::F32;
pub type Devices = std::vec::IntoIter<Device>;
#[derive(Debug)]
pub struct Host {
name: String,
connect_ports_automatically: bool,
start_server_automatically: bool,
devices_created: Vec<Device>,
}
impl Host {
pub fn new() -> Result<Self, Error> {
let mut host = Host {
name: format!("cpal_client_{}", std::process::id()),
connect_ports_automatically: true,
start_server_automatically: false,
devices_created: vec![],
};
host.initialize_default_devices();
Ok(host)
}
pub fn set_connect_automatically(&mut self, do_connect: bool) {
self.connect_ports_automatically = do_connect;
}
pub fn set_start_server_automatically(&mut self, do_start_server: bool) {
self.start_server_automatically = do_start_server;
}
pub fn input_device_with_name(&mut self, name: &str) -> Option<Device> {
self.name = name.to_owned();
self.default_input_device()
}
pub fn output_device_with_name(&mut self, name: &str) -> Option<Device> {
self.name = name.to_owned();
self.default_output_device()
}
fn initialize_default_devices(&mut self) {
let in_device_res = Device::default_input_device(
&self.name,
self.connect_ports_automatically,
self.start_server_automatically,
);
if let Ok(device) = in_device_res {
self.devices_created.push(device);
}
let out_device_res = Device::default_output_device(
&self.name,
self.connect_ports_automatically,
self.start_server_automatically,
);
if let Ok(device) = out_device_res {
self.devices_created.push(device);
}
}
}
impl HostTrait for Host {
type Devices = Devices;
type Device = Device;
fn is_available() -> bool {
true
}
fn devices(&self) -> Result<Self::Devices, Error> {
Ok(self.devices_created.clone().into_iter())
}
fn default_input_device(&self) -> Option<Self::Device> {
for device in &self.devices_created {
if device.is_input() {
return Some(device.clone());
}
}
None
}
fn default_output_device(&self) -> Option<Self::Device> {
for device in &self.devices_created {
if device.is_output() {
return Some(device.clone());
}
}
None
}
}
fn get_client_options(start_server_automatically: bool) -> jack::ClientOptions {
let mut client_options = jack::ClientOptions::empty();
client_options.set(
jack::ClientOptions::NO_START_SERVER,
!start_server_automatically,
);
client_options
}
impl From<jack::Error> for Error {
fn from(err: jack::Error) -> Self {
let msg = format!("{err}");
match err {
jack::Error::ClientError(_)
| jack::Error::ClientActivationError
| jack::Error::ClientDeactivationError
| jack::Error::LibraryError(_)
| jack::Error::WeakFunctionNotFound(_)
| jack::Error::RingbufferCreateFailed => {
Error::with_message(ErrorKind::DeviceNotAvailable, msg)
}
jack::Error::ClientIsNoLongerAlive | jack::Error::ClientPanicked => {
Error::with_message(ErrorKind::StreamInvalidated, msg)
}
jack::Error::SetBufferSizeError | jack::Error::NotEnoughSpace => {
Error::with_message(ErrorKind::UnsupportedConfig, msg)
}
jack::Error::InvalidDeactivation | jack::Error::FreewheelError => {
Error::with_message(ErrorKind::UnsupportedOperation, msg)
}
jack::Error::PortNamingError | jack::Error::PortAliasError => {
Error::with_message(ErrorKind::InvalidInput, msg)
}
_ => Error::with_message(ErrorKind::BackendError, msg),
}
}
}
fn get_client(name: &str, client_options: jack::ClientOptions) -> Result<jack::Client, Error> {
let (client, status) = jack::Client::new(name, client_options)?;
if status.intersects(jack::ClientStatus::VERSION_ERROR) {
Err(Error::with_message(
ErrorKind::UnsupportedOperation,
"Client protocol version does not match the JACK server",
))
} else if status.intersects(jack::ClientStatus::INVALID_OPTION) {
Err(Error::with_message(
ErrorKind::UnsupportedOperation,
"JACK client operation contained an invalid or unsupported option",
))
} else if status.intersects(jack::ClientStatus::SERVER_ERROR) {
Err(Error::with_message(
ErrorKind::DeviceNotAvailable,
"Error communicating with the JACK server",
))
} else if status.intersects(jack::ClientStatus::SERVER_FAILED) {
Err(Error::with_message(
ErrorKind::DeviceNotAvailable,
"Could not connect to the JACK server",
))
} else if status.intersects(jack::ClientStatus::INIT_FAILURE) {
Err(Error::with_message(
ErrorKind::DeviceNotAvailable,
"Unable to initialize JACK client",
))
} else if status.intersects(jack::ClientStatus::SHM_FAILURE) {
Err(Error::with_message(
ErrorKind::DeviceNotAvailable,
"Unable to access JACK shared memory",
))
} else if status.intersects(jack::ClientStatus::NO_SUCH_CLIENT) {
Err(Error::with_message(
ErrorKind::DeviceNotAvailable,
"Requested JACK client does not exist",
))
} else {
Ok(client)
}
}