Skip to content

Commit

Permalink
add enumeration
Browse files Browse the repository at this point in the history
  • Loading branch information
ImUrX committed Jan 28, 2023
1 parent 352d762 commit 483e344
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 43 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ alsa = "0.6"
libc = "0.2"
parking_lot = "0.12"
jack = { version = "0.10", optional = true }
intmap = "2.0"
pipewire = { git = "https://gitlab.freedesktop.org/pipewire/pipewire-rs", optional = true }

[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies]
Expand Down
6 changes: 3 additions & 3 deletions src/host/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ pub(crate) mod emscripten;
feature = "jack"
))]
pub(crate) mod jack;
pub(crate) mod null;
#[cfg(target_os = "android")]
pub(crate) mod oboe;
#[cfg(all(
any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd"),
feature = "pipewire"
))]
pub(crate) mod pipewire;
pub(crate) mod null;
#[cfg(target_os = "android")]
pub(crate) mod oboe;
#[cfg(windows)]
pub(crate) mod wasapi;
#[cfg(all(target_arch = "wasm32", feature = "wasm-bindgen"))]
Expand Down
89 changes: 82 additions & 7 deletions src/host/pipewire/conn.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
extern crate pipewire;

use intmap::IntMap;

use super::Device;

use self::pipewire::{
metadata::{Metadata, MetadataListener},
node::{Node, NodeListener},
Expand Down Expand Up @@ -32,6 +36,7 @@ enum Message {
device_type: DeviceType,
autoconnect: bool,
},
EnumerateDevices,
}

enum MessageRepl {
Expand All @@ -41,6 +46,7 @@ enum MessageRepl {

pub struct NodeInfo {
pub name: String,
pub id: u32,
}

pub struct PWClient {
Expand Down Expand Up @@ -97,11 +103,12 @@ impl PWClient {
struct State {
settings: Settings,
nodes: Vec<ProxyItem>,
devices: IntMap<NodeInfo>,
}

#[derive(Default, Clone, Debug)]
pub struct Settings {
pub sample_rate: u32,
pub allowed_sample_rates: Vec<u32>,
pub min_buffer_size: u32,
pub max_buffer_size: u32,
pub default_buffer_size: u32,
Expand Down Expand Up @@ -148,7 +155,9 @@ fn pw_thread(
Message::Terminate => mainloop.quit(),
Message::GetSettings => {
let settings = state.borrow().settings.clone();
main_sender.send(MessageRepl::Settings(settings));
main_sender
.send(MessageRepl::Settings(settings))
.expect("Failed to send settings");
}
Message::CreateDeviceNode {
name,
Expand Down Expand Up @@ -176,25 +185,32 @@ fn pw_thread(
)
.expect("Failed to create object");

let id = Rc::new(Cell::new(0));
let id_clone = id.clone();
let _listener = node
.add_listener_local()
.info(|info| {
// println!("{:?}", info);
.info(move |info| {
id_clone.set(info.id());
})
.param(|a, b, c, d| {
println!("{}, {}, {}, {}", a, b, c, d);
})
.register();

println!("{:?}", node);
while id.get() == 0 {
mainloop.run();
}

state.as_ref().borrow_mut().nodes.push(ProxyItem::Node {
_proxy: node,
_listener,
});

main_sender.send(MessageRepl::NodeInfo(NodeInfo { name }));
main_sender
.send(MessageRepl::NodeInfo(NodeInfo { name, id: id.get() }))
.expect("Failed to send node info");
}
Message::EnumerateDevices => {}
}
});

Expand All @@ -207,6 +223,47 @@ fn pw_thread(

move |global| match global.type_ {
ObjectType::Metadata => handle_metadata(global, &state, &registry, &proxies),
ObjectType::Node => {
if let Some(ref props) = global.props {
let mut state = state.as_ref().borrow_mut();
let name = props
.get("node.nick")
.or(props.get("node.description"))
.unwrap_or("Unknown device");
match props.get("media.class") {
Some("Audio/Source") => {
state.devices.insert(
global.id.into(),
NodeInfo {
name: name.to_string(),
id: global.id,
},
);
}
Some("Audio/Sink") => {
state.devices.insert(
global.id.into(),
NodeInfo {
name: name.to_string(),
id: global.id,
},
);
}
_ => {}
}
if props.get("media.class") == Some("Audio/Source")
&& global.type_ == ObjectType::Node
{
println!(
"object: id:{} type:{}/{} nick:{}",
global.id,
global.type_,
global.version,
props.get("node.nick").unwrap_or("failed to get name")
);
}
}
}
_ => {}
}
})
Expand Down Expand Up @@ -248,11 +305,12 @@ fn handle_metadata(
.property({
let state = state.clone();
move |_, key, _, value| {
let mut sample_rate = 0;
let mut state = state.as_ref().borrow_mut();
if let Some(value) = value {
if let Ok(value) = value.parse::<u32>() {
match key {
Some("clock.rate") => state.settings.sample_rate = value,
Some("clock.rate") => sample_rate = value,
Some("clock.quantum") => {
state.settings.default_buffer_size = value
}
Expand All @@ -264,8 +322,25 @@ fn handle_metadata(
}
_ => {}
};
} else {
match key {
Some("clock.allowed-rates") => {
let rates: Result<Vec<u32>, _> = value[2..value.len() - 2]
.split_whitespace()
.map(|x| x.parse::<u32>())
.collect();
state.settings.allowed_sample_rates =
rates.expect("Couldn't parse allowed rates");
}
_ => {}
}
}
}
// Not sure if allowed-rates can be empty,
// but if it is just push the currently used one.
if state.settings.allowed_sample_rates.is_empty() {
state.settings.allowed_sample_rates.push(sample_rate);
}
0
}
})
Expand Down
74 changes: 52 additions & 22 deletions src/host/pipewire/device.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::traits::DeviceTrait;
use crate::{
BackendSpecificError, BuildStreamError, Data, DefaultStreamConfigError, DeviceNameError,
InputCallbackInfo, OutputCallbackInfo, SampleFormat, SampleRate, StreamConfig, StreamError,
Expand All @@ -6,7 +7,6 @@ use crate::{
};
use std::hash::{Hash, Hasher};
use std::rc::Rc;
use crate::traits::DeviceTrait;

use super::stream::Stream;
use super::PIPEWIRE_SAMPLE_FORMAT;
Expand All @@ -26,12 +26,10 @@ pub enum DeviceType {
}
#[derive(Clone)]
pub struct Device {
name: String,
sample_rate: SampleRate,
buffer_size: SupportedBufferSize,
device_type: DeviceType,
connect_ports_automatically: bool,
client: Rc<super::conn::PWClient>
pub(crate) name: String,
pub(crate) device_type: DeviceType,
pub(crate) connect_ports_automatically: bool,
pub(crate) client: Rc<super::conn::PWClient>,
}

impl Device {
Expand All @@ -41,22 +39,29 @@ impl Device {
device_type: DeviceType,
client: Rc<super::conn::PWClient>,
) -> Result<Self, String> {
while client.get_settings().and_then(|s| if s.sample_rate == 0 {Err(String::new())} else {Ok(true)} ).is_err() {}
while client
.get_settings()
.and_then(|s| {
if s.allowed_sample_rates.is_empty() {
Err(String::new())
} else {
Ok(true)
}
})
.is_err()
{}

let settings = client.get_settings().unwrap();

let info = client.create_device_node(name, device_type.clone(), connect_ports_automatically).expect("Error creating device");
let info = client
.create_device_node(name, device_type.clone(), connect_ports_automatically)
.expect("Error creating device");

Ok(Device {
name: info.name,
sample_rate: SampleRate(settings.sample_rate),
buffer_size: SupportedBufferSize::Range {
min: settings.min_buffer_size,
max: settings.max_buffer_size,
},
device_type,
connect_ports_automatically,
client
client,
})
}

Expand Down Expand Up @@ -89,9 +94,14 @@ impl Device {
}

pub fn default_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
let settings = self.client.get_settings().unwrap();
let channels = DEFAULT_NUM_CHANNELS;
let sample_rate = self.sample_rate;
let buffer_size = self.buffer_size.clone();
// Default is highest sample rate possible
let sample_rate = SampleRate(*settings.allowed_sample_rates.last().unwrap());
let buffer_size = SupportedBufferSize::Range {
min: settings.min_buffer_size,
max: settings.max_buffer_size,
};
// The sample format for JACK audio ports is always "32-bit float mono audio" in the current implementation.
// Custom formats are allowed within JACK, but this is of niche interest.
// The format can be found programmatically by calling jack::PortSpec::port_type() on a created port.
Expand All @@ -105,6 +115,7 @@ impl Device {
}

pub fn supported_configs(&self) -> Vec<SupportedStreamConfigRange> {
let settings = self.client.get_settings().unwrap();
let f = match self.default_config() {
Err(_) => return vec![],
Ok(f) => f,
Expand All @@ -115,7 +126,8 @@ impl Device {
for &channels in DEFAULT_SUPPORTED_CHANNELS.iter() {
supported_configs.push(SupportedStreamConfigRange {
channels,
min_sample_rate: f.sample_rate,
min_sample_rate: SampleRate(*settings.allowed_sample_rates.first().unwrap()),
// Default is maximum possible, so just use that
max_sample_rate: f.sample_rate,
buffer_size: f.buffer_size.clone(),
sample_format: f.sample_format,
Expand Down Expand Up @@ -179,15 +191,25 @@ impl DeviceTrait for Device {
D: FnMut(&Data, &InputCallbackInfo) + Send + 'static,
E: FnMut(StreamError) + Send + 'static,
{
let settings = self.client.get_settings().unwrap();
if let DeviceType::OutputDevice = &self.device_type {
// Trying to create an input stream from an output device
return Err(BuildStreamError::StreamConfigNotSupported);
}
if conf.sample_rate != self.sample_rate || sample_format != PIPEWIRE_SAMPLE_FORMAT {
// FIXME: Not sure if we should go to the nearest neighbour sample rate
// This issue also happens on build_output_stream_raw()
if settings.allowed_sample_rates.contains(&conf.sample_rate.0)
|| sample_format != PIPEWIRE_SAMPLE_FORMAT
{
return Err(BuildStreamError::StreamConfigNotSupported);
}

let mut stream = Stream::new_input(self.client.clone(), conf.channels, data_callback, error_callback);
let mut stream = Stream::new_input(
self.client.clone(),
conf.channels,
data_callback,
error_callback,
);

if self.connect_ports_automatically {
stream.connect_to_system_inputs();
Expand All @@ -207,15 +229,23 @@ impl DeviceTrait for Device {
D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static,
E: FnMut(StreamError) + Send + 'static,
{
let settings = self.client.get_settings().unwrap();
if let DeviceType::InputDevice = &self.device_type {
// Trying to create an output stream from an input device
return Err(BuildStreamError::StreamConfigNotSupported);
}
if conf.sample_rate != self.sample_rate || sample_format != PIPEWIRE_SAMPLE_FORMAT {
if settings.allowed_sample_rates.contains(&conf.sample_rate.0)
|| sample_format != PIPEWIRE_SAMPLE_FORMAT
{
return Err(BuildStreamError::StreamConfigNotSupported);
}

let mut stream = Stream::new_output(self.client.clone(), conf.channels, data_callback, error_callback);
let mut stream = Stream::new_output(
self.client.clone(),
conf.channels,
data_callback,
error_callback,
);

if self.connect_ports_automatically {
stream.connect_to_system_outputs();
Expand Down
Loading

0 comments on commit 483e344

Please sign in to comment.