diff --git a/crates/sound/Cargo.toml b/crates/sound/Cargo.toml index 330c188..2a8f621 100644 --- a/crates/sound/Cargo.toml +++ b/crates/sound/Cargo.toml @@ -10,7 +10,7 @@ license = "Apache-2.0 OR BSD-3-Clause" edition = "2018" [features] -default = ["null-backend"] +default = ["null-backend", "pw-backend"] null-backend = [] pw-backend = ["pipewire", "libspa", "pipewire-sys", "libspa-sys", "bindgen"] diff --git a/crates/sound/src/audio_backends.rs b/crates/sound/src/audio_backends.rs index 18f3bed..205d753 100644 --- a/crates/sound/src/audio_backends.rs +++ b/crates/sound/src/audio_backends.rs @@ -9,13 +9,32 @@ mod pw_backend; #[cfg(feature = "null-backend")] use self::null::NullBackend; #[cfg(feature = "pw-backend")] -use self::pw_backend::PwBackend; -use crate::{Error, Result, SoundRequest}; +use self::pw_backend::{PwBackend, PCMParams}; +use crate::{Error, Result}; pub trait AudioBackend { - fn write(&self, req: &SoundRequest) -> Result<()>; + fn write(&self, stream_id: u32) -> Result<()>; + fn read(&self, stream_id: u32) -> Result<()>; - fn read(&self, req: &mut SoundRequest) -> Result<()>; + fn set_param(&self, _stream_id: u32, _params: PCMParams) -> Result<()> { + Ok(()) + } + + fn prepare(&self, _stream_id: u32) -> Result<()> { + Ok(()) + } + + fn release(&self, _stream_id: u32) -> Result<()> { + Ok(()) + } + + fn start(&self, _stream_id: u32) -> Result<()> { + Ok(()) + } + + fn stop(&self, _stream_id: u32) -> Result<()> { + Ok(()) + } } pub fn alloc_audio_backend(name: String) -> Result> { diff --git a/crates/sound/src/audio_backends/null.rs b/crates/sound/src/audio_backends/null.rs index e12c36d..de4f3e1 100644 --- a/crates/sound/src/audio_backends/null.rs +++ b/crates/sound/src/audio_backends/null.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause use super::AudioBackend; -use crate::{Error, Result, SoundRequest}; +use crate::{Error, Result}; pub struct NullBackend {} @@ -12,16 +12,20 @@ impl NullBackend { } impl AudioBackend for NullBackend { - fn write(&self, _req: &SoundRequest) -> Result<()> { + fn write(&self, stream_id: u32) -> Result<()> { + println!("null backend, writting to stream: {}", stream_id); Ok(()) } - fn read(&self, req: &mut SoundRequest) -> Result<()> { + fn read(&self, _stream_id: u32) -> Result<()> { + + /* let buf = req.data_slice().ok_or(Error::SoundReqMissingData)?; let zero_mem = vec![0u8; buf.len()]; buf.copy_from(&zero_mem); + */ Ok(()) } } diff --git a/crates/sound/src/audio_backends/pw_backend.rs b/crates/sound/src/audio_backends/pw_backend.rs index a939365..6e9194f 100644 --- a/crates/sound/src/audio_backends/pw_backend.rs +++ b/crates/sound/src/audio_backends/pw_backend.rs @@ -2,27 +2,111 @@ // SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause use super::AudioBackend; -use crate::{Error, Result, SoundRequest}; +use std::{thread}; +use std::{cell::Cell, rc::Rc}; +use crate::{Error, Result}; -pub struct PwBackend {} +use vm_memory::{Le32, Le64}; +use pipewire as pw; +use pw::{sys::*}; + +#[derive(Default, Debug)] +pub struct StreamInfo { + pub id: usize, + pub params: PCMParams, + pub formats: Le64, + pub rates: Le64, + pub direction: u8, + pub channels_min: u8, + pub channels_max: u8, +} + +#[derive(Default, Debug)] +pub struct PCMParams { + pub features: Le32, + /// size of hardware buffer in bytes + pub buffer_bytes: Le32, + /// size of hardware period in bytes + pub period_bytes: Le32, + pub channels: u8, + pub format: u8, + pub rate: u8, +} + +// SAFETY: Safe as the structure can be sent to another thread. +unsafe impl Send for WrapMainLoop {} + +// SAFETY: Safe as the structure can be shared with another thread as the state +// is protected with a lock. +unsafe impl Sync for WrapMainLoop {} + +#[derive(Clone, Debug)] +pub struct WrapMainLoop { + mainloop: pipewire::MainLoop, +} +pub struct PwBackend { + //pub streams: Arc>>, +} impl PwBackend { pub fn new() -> Self { - PwBackend {} + pw::init(); + + let wrap_mainloop = WrapMainLoop { + mainloop : pw::MainLoop::new().expect("we can't create mainloop") + }; + //let mainloop = pw::MainLoop::new().expect("Failed to create Pipewire Mainloop"); + let context = pw::Context::new(&wrap_mainloop.mainloop).expect("Failed to create Pipewire Context"); + let core = context + .connect(None) + .expect("Failed to connect to Pipewire Core"); + + // To comply with Rust's safety rules, we wrap this variable in an `Rc` and a `Cell`. + let done = Rc::new(Cell::new(false)); + + // Create new reference for each variable so that they can be moved into the closure. + let done_clone = done.clone(); + let loop_clone = wrap_mainloop.mainloop.clone(); + + let pending = core.sync(0).expect("sync failed"); + let _listener_core = core + .add_listener_local() + .done(move |id, seq| { + if id == PW_ID_CORE && seq == pending { + done_clone.set(true); + loop_clone.quit(); + } + }) + .register(); + + thread::spawn(move || { + wrap_mainloop.mainloop.run(); + }); + + println!("pipewire backend running"); + + Self { + } + } } impl AudioBackend for PwBackend { - fn write(&self, _req: &SoundRequest) -> Result<()> { + fn write(&self, stream_id: u32) -> Result<()> { + println!("pipewire backend, writting to stream: {}", stream_id); Ok(()) } - fn read(&self, req: &mut SoundRequest) -> Result<()> { + fn read(&self, _stream_id: u32) -> Result<()> { + /* let buf = req.data_slice().ok_or(Error::SoundReqMissingData)?; let zero_mem = vec![0u8; buf.len()]; buf.copy_from(&zero_mem); - + */ + Ok(()) + } + fn set_param(&self, _stream_id: u32, _params: PCMParams) -> Result<()> { Ok(()) } }