From 2c66db4205989ddd3c3c0160d33ce16bc5888f32 Mon Sep 17 00:00:00 2001 From: Muaz Ahmad Date: Tue, 3 Dec 2024 14:30:55 +0500 Subject: [PATCH] Init and test sending samples to pipewire stream --- src/audio/mod.rs | 6 +++- src/audio/pipewire.rs | 32 ------------------ src/audio/pw.rs | 76 ++++++++++++++++++++++++++++++++++++++++++ src/audio/sound_mgr.rs | 53 ++++++++++++++++++++++++++--- 4 files changed, 129 insertions(+), 38 deletions(-) delete mode 100644 src/audio/pipewire.rs create mode 100644 src/audio/pw.rs diff --git a/src/audio/mod.rs b/src/audio/mod.rs index c35b4c6..bdd4b47 100644 --- a/src/audio/mod.rs +++ b/src/audio/mod.rs @@ -8,6 +8,8 @@ use std::{ use std::sync::mpsc::Receiver; +use pipewire::spa::pod::Pod; + use crate::{config::Settings, player::PlayerEvent, utils::Error}; pub enum AudioEvent { @@ -19,6 +21,8 @@ pub struct SoundManager { error_chan: Sender, audio_controls: Receiver, player_chan: Sender, + sample_in: Sender>, + pw_signal_in: pipewire::channel::Sender>>, } pub fn init( @@ -53,5 +57,5 @@ pub fn init( Ok(audio_control_in) } -mod pipewire; +mod pw; mod sound_mgr; diff --git a/src/audio/pipewire.rs b/src/audio/pipewire.rs deleted file mode 100644 index 44e4dd3..0000000 --- a/src/audio/pipewire.rs +++ /dev/null @@ -1,32 +0,0 @@ -use pipewire::{ - context::Context, core::Core, keys, main_loop::MainLoop, properties::properties, stream::Stream, -}; - -use crate::utils::Error; - -pub struct PipewireContext { - pub mainloop: MainLoop, - context: Context, - core: Core, - stream: Stream, -} - -pub fn init() -> Result { - let mainloop = MainLoop::new(None)?; - let context = Context::new(&mainloop)?; - let core = context.connect(None)?; - let props = properties! { - *keys::MEDIA_TYPE => "Audio", - *keys::MEDIA_ROLE => "Music", - *keys::MEDIA_CATEGORY => "Playback", - *keys::AUDIO_CHANNELS => "2", - }; - let audio_source = Stream::new(&core, "Subtails", props)?; - - Ok(PipewireContext { - mainloop, - context, - core, - stream: audio_source, - }) -} diff --git a/src/audio/pw.rs b/src/audio/pw.rs new file mode 100644 index 0000000..ca3f4e7 --- /dev/null +++ b/src/audio/pw.rs @@ -0,0 +1,76 @@ +use std::{process::exit, sync::Arc}; + +use pipewire::{ + channel::Receiver, + context::Context, + core::Core, + keys, + main_loop::MainLoop, + properties::properties, + spa::{pod::Pod, utils::Direction}, + stream::{Stream, StreamFlags}, +}; + +use crate::utils::Error; + +pub fn init( + samples: std::sync::mpsc::Receiver>, + pw_signal: Receiver>>, +) -> Result<(), Error> { + let mainloop = MainLoop::new(None)?; + let context = Context::new(&mainloop)?; + let core = context.connect(None)?; + let props = properties! { + *keys::MEDIA_TYPE => "Audio", + *keys::MEDIA_ROLE => "Music", + *keys::MEDIA_CATEGORY => "Playback", + *keys::AUDIO_CHANNELS => "2", + }; + let audio_source = Arc::new(Stream::new(&core, "Subtails", props)?); + + let _listener = audio_source + .add_local_listener_with_user_data(0.0 as f64) + .process(move |stream, _| match stream.dequeue_buffer() { + None => { + eprintln!("Buffer dequeue failed!"); + exit(1) + } + Some(mut buf) => { + let data = &mut (buf.datas_mut()[0]); + let n_iter = if let Some(sample_buf) = data.data() { + let next_samples = samples + .recv() + .expect("Sample sender disconnected unexpectedly"); + if next_samples.len() > sample_buf.len() { + panic!("Buffer too small for given sample chunks"); + } + let slice = &mut sample_buf[0..next_samples.len()]; + slice.copy_from_slice(&next_samples); + slice.len() + } else { + 0 + }; + let chunk = data.chunk_mut(); + *chunk.offset_mut() = 0; + *chunk.size_mut() = n_iter as _; + } + }) + .register()?; + + let audio_source_ref = audio_source.clone(); + let _receiver = pw_signal.attach(mainloop.loop_(), move |pod_bytes| { + let mut params = [Pod::from_bytes(&pod_bytes).unwrap()]; + audio_source_ref.disconnect().unwrap(); + audio_source_ref + .connect( + Direction::Output, + None, + StreamFlags::AUTOCONNECT | StreamFlags::MAP_BUFFERS | StreamFlags::ALLOC_BUFFERS, + &mut params, + ) + .unwrap(); + }); + + mainloop.run(); + Ok(()) +} diff --git a/src/audio/sound_mgr.rs b/src/audio/sound_mgr.rs index 648ab2e..4b48629 100644 --- a/src/audio/sound_mgr.rs +++ b/src/audio/sound_mgr.rs @@ -1,14 +1,24 @@ use std::{ + f64::consts, + io::Cursor, sync::{ - mpsc::{Receiver, Sender}, + mpsc::{channel, Receiver, Sender}, Arc, }, thread, }; +use pipewire::spa::{ + param::audio::{AudioFormat, AudioInfoRaw, MAX_CHANNELS}, + pod::{serialize::PodSerializer, Object, Pod, Value}, + sys::{ + SPA_PARAM_EnumFormat, SPA_TYPE_OBJECT_Format, SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, + }, +}; + use crate::{config::Settings, player::PlayerEvent, utils::Error}; -use super::{pipewire, AudioEvent, SoundManager}; +use super::{pw, AudioEvent, SoundManager}; impl SoundManager { pub fn init( @@ -18,30 +28,63 @@ impl SoundManager { player_chan: Sender, ) -> Result { let error_chan_clone = error_chan.clone(); + let (sample_in, sample_out) = channel(); + let (pw_signal_in, pw_signal_out) = pipewire::channel::channel(); thread::spawn(move || { - let pwire_context = match pipewire::init() { + match pw::init(sample_out, pw_signal_out) { Err(err) => { error_chan_clone.send(err).unwrap(); thread::park(); return; } - Ok(x) => x, + Ok(_) => (), }; - pwire_context.mainloop.run(); }); + let mut test_info = AudioInfoRaw::new(); + test_info.set_format(AudioFormat::S16LE); + test_info.set_rate(44100); + test_info.set_channels(2); + let mut positions = [0; MAX_CHANNELS]; + (positions[0], positions[1]) = (SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR); + test_info.set_position(positions); + let test_pod = PodSerializer::serialize( + Cursor::new(Vec::new()), + &Value::Object(Object { + type_: SPA_TYPE_OBJECT_Format, + id: SPA_PARAM_EnumFormat, + properties: test_info.into(), + }), + ) + .unwrap() + .0 + .into_inner(); + pw_signal_in.send(Box::new(test_pod)).unwrap(); Ok(SoundManager { settings, error_chan, audio_controls: audio_control_chan, player_chan, + sample_in, + pw_signal_in, }) } pub fn begin(&mut self) -> Result<(), Error> { + let mut test_tone: Vec = vec![0; 1764]; + for i in 0..441 { + let t = i as f64 / 441.0 * consts::PI * 2.0; + let v = (f64::sin(t) * 16767.0) as i16; + test_tone[i * 4..i * 4 + 2].copy_from_slice(&i16::to_le_bytes(v)); + test_tone[i * 4 + 2..i * 4 + 4].copy_from_slice(&i16::to_le_bytes(v)); + } + loop { match self.audio_controls.recv()? { AudioEvent::DecodeChunk(chunk) => (), _ => unimplemented!(), } + for i in 0..100 { + self.sample_in.send(test_tone.clone())?; + } } } }